user1564: I have an Arduino Nano & am success fully controlling 4 servos from it but it then occurred to me that I am controlling these in a serial manner, ie I set 1 position then the next then the next....
I would like the servos to act in a parallel manner i.e. that their motions can happen at the same time but at different speeds or different position.
Is this possible using just 1 controller (maybe some sort of polling/interrupt setup?) or will I need to run separate controllers for each set of servos & synchronize them?
EDIT: Added the PC side code & the Arduino side code
PC Python code:
from Tkinter import *
import serial

usbport = 'COM5'
ser = serial.Serial(usbport, 9600, timeout=1)

def init():
    for servo in range(1, 5):
        print servo
        ser.write(chr(255))
        ser.write(chr(servo))
        ser.write(chr(90))

class App:

    def __init__(self, master):

        frame = Frame(master)
        frame.pack()
        self.scale1 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(1), bd=5, bigincrement=2, length=360, width=30, label='Servo 1')
        self.scale1.set(90)
        self.scale1.pack(side=LEFT)
        self.scale2 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(2), bd=5, bigincrement=2, length=360, width=30, label='Servo 2')
        self.scale2.set(90)
        self.scale2.pack(side=LEFT)
        self.scale3 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(3), bd=5, bigincrement=2, length=360, width=30, label='Servo 3')
        self.scale3.set(90)
        self.scale3.pack(side=LEFT)
        self.scale4 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(4), bd=5, bigincrement=2, length=360, width=30, label='Servo 4')
        self.scale4.set(90)
        self.scale4.pack(side=LEFT)
        self.centre = Button(frame, text="Centre All", command=self.centre)
        self.centre.pack(side=TOP)


    def getAngle(self, slider):
        if slider==1:
            ang = self.scale1.get()
        if slider==2:
            ang = self.scale2.get()            
        if slider==3:
            ang = self.scale3.get()
        if slider==4:
            ang = self.scale4.get()
        ser.write(chr(255))
        ser.write(chr(slider))
        ser.write(chr(ang))

    def centre(self):
        for servo in range(1, 5):
            ser.write(chr(255))
            ser.write(chr(servo))
            ser.write(chr(90))
        self.scale1.set(90)
        self.scale2.set(90)
        self.scale3.set(90)
        self.scale4.set(90)

init()
root = Tk()
app = App(root)
root.mainloop()

Arduino code
*
 * ------------------------------
 *   MultipleSerialServoControl
 * ------------------------------
 *
 * Uses the Arduino Serial library
 *  (http://arduino.cc/en/Reference/Serial)
 * and the Arduino Servo library
 *  (http://arduino.cc/en/Reference/Servo)
 * to control multiple servos from a PC using a USB cable.
 *
 * Dependencies:
 *   Arduino 0017 or higher
 *     (http://www.arduino.cc/en/Main/Software)
 *   Python servo.py module
 *     (http://principialabs.com/arduino-python-4-axis-servo-control/)
 *
 * Created:  23 December 2009
 * Author:   Brian D. Wendt
 *   (http://principialabs.com/)
 * Version:  1.1
 * License:  GPLv3
 *   (http://www.fsf.org/licensing/)
 *
 */

// Import the Arduino Servo library
#include <Servo.h> 

// Create a Servo object for each servo
Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;

// Common servo setup values
int minPulse = 600;   // minimum servo position, us (microseconds)
int maxPulse = 2400;  // maximum servo position, us

// User input for servo and position
int userInput[3];    // raw input from serial buffer, 3 bytes
int startbyte;       // start byte, begin reading input
int servo;           // which servo to pulse?
int pos = 90;        // servo angle 0-180
int i;               // iterator

// LED on Pin 13 for digital on/off demo
int ledPin = 13;
int pinState = LOW;

void setup() 
{ 
  // Attach each Servo object to a digital pin
  servo1.attach(3, minPulse, maxPulse);
  servo2.attach(4, minPulse, maxPulse);
  servo3.attach(5, minPulse, maxPulse);
  servo4.attach(9, minPulse, maxPulse);

  // LED on Pin 13 for digital on/off demo
  pinMode(ledPin, OUTPUT);

  // Open the serial connection, 9600 baud
  Serial.begin(9600);
} 

void loop() 
{ 
  // Wait for serial input (min 3 bytes in buffer)
  if (Serial.available() > 2) {
    // Read the first byte
    startbyte = Serial.read();
    // If it's really the startbyte (255) ...
    if (startbyte == 255) {
      // ... then get the next two bytes
      for (i=0;i<2;i++) {
        userInput[i] = Serial.read();
      }
      // First byte = servo to move?
      servo = userInput[0];
      // Second byte = which position?
      pos = userInput[1];
      // Packet error checking and recovery
      if (pos == 255) { servo = 255; }

      // Assign new position to appropriate servo
      switch (servo) {
        case 1:
          servo1.write(pos);    // move servo1 to 'pos'
          break;
        case 2:
          servo2.write(pos);
          break;
        case 3:
          servo3.write(pos);
          break;
        case 4:
          servo4.write(pos);
          break;

        // LED on Pin 13 for digital on/off demo
        case 99:
          if (pos == 180) {
            if (pinState == LOW) { pinState = HIGH; }
            else { pinState = LOW; }
          }
          if (pos == 0) {
            pinState = LOW;
          }
          digitalWrite(ledPin, pinState);
          break;
      }
    }
  }
}


user6628: The python code is polling through the servos sequentially/serialy, but the arduino code looks parallel. The arduino will parse the string and pass the command to the servo code, passing control back to the loop() command before the servo reaches its assigned position. Maybe that is your question: "is servo.write(position) a blocking call?" If so, the answer is no, it is non-blocking, the motors will drive at the motors speed to the final positions independently. If you want coordinated movement through this scheme, you have to break the movements up and coordinate it on the python side without feedback.
For instance, if you want to go from position (90,90,90,90) to position (81,0,90,90) you will need to know it takes 900ms (for example) to complete the -90 move, and break it up into 10 segments of 100ms to coordinate the (-1,0,-10,0) steps.  Doing the coordination without feedback will be awkward, and might be better handled from your python/UI side.

1564: <p>I have an Arduino Nano &amp; am success fully controlling 4 servos from it but it then occurred to me that I am controlling these in a serial manner, ie I set 1 position then the next then the next....</p>

<p>I would like the servos to act in a parallel manner i.e. that their motions can happen at the same time but at different speeds or different position.</p>

<p>Is this possible using just 1 controller (maybe some sort of polling/interrupt setup?) or will I need to run separate controllers for each set of servos &amp; synchronize them?</p>

<p>EDIT: Added the PC side code &amp; the Arduino side code</p>

<p>PC Python code:</p>

<pre><code>from Tkinter import *
import serial

usbport = 'COM5'
ser = serial.Serial(usbport, 9600, timeout=1)

def init():
    for servo in range(1, 5):
        print servo
        ser.write(chr(255))
        ser.write(chr(servo))
        ser.write(chr(90))

class App:

    def __init__(self, master):

        frame = Frame(master)
        frame.pack()
        self.scale1 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(1), bd=5, bigincrement=2, length=360, width=30, label='Servo 1')
        self.scale1.set(90)
        self.scale1.pack(side=LEFT)
        self.scale2 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(2), bd=5, bigincrement=2, length=360, width=30, label='Servo 2')
        self.scale2.set(90)
        self.scale2.pack(side=LEFT)
        self.scale3 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(3), bd=5, bigincrement=2, length=360, width=30, label='Servo 3')
        self.scale3.set(90)
        self.scale3.pack(side=LEFT)
        self.scale4 = Scale(master, from_=0, to=180, command=lambda ev: self.getAngle(4), bd=5, bigincrement=2, length=360, width=30, label='Servo 4')
        self.scale4.set(90)
        self.scale4.pack(side=LEFT)
        self.centre = Button(frame, text="Centre All", command=self.centre)
        self.centre.pack(side=TOP)


    def getAngle(self, slider):
        if slider==1:
            ang = self.scale1.get()
        if slider==2:
            ang = self.scale2.get()            
        if slider==3:
            ang = self.scale3.get()
        if slider==4:
            ang = self.scale4.get()
        ser.write(chr(255))
        ser.write(chr(slider))
        ser.write(chr(ang))

    def centre(self):
        for servo in range(1, 5):
            ser.write(chr(255))
            ser.write(chr(servo))
            ser.write(chr(90))
        self.scale1.set(90)
        self.scale2.set(90)
        self.scale3.set(90)
        self.scale4.set(90)

init()
root = Tk()
app = App(root)
root.mainloop()
</code></pre>

<p>Arduino code</p>

<pre><code>*
 * ------------------------------
 *   MultipleSerialServoControl
 * ------------------------------
 *
 * Uses the Arduino Serial library
 *  (http://arduino.cc/en/Reference/Serial)
 * and the Arduino Servo library
 *  (http://arduino.cc/en/Reference/Servo)
 * to control multiple servos from a PC using a USB cable.
 *
 * Dependencies:
 *   Arduino 0017 or higher
 *     (http://www.arduino.cc/en/Main/Software)
 *   Python servo.py module
 *     (http://principialabs.com/arduino-python-4-axis-servo-control/)
 *
 * Created:  23 December 2009
 * Author:   Brian D. Wendt
 *   (http://principialabs.com/)
 * Version:  1.1
 * License:  GPLv3
 *   (http://www.fsf.org/licensing/)
 *
 */

// Import the Arduino Servo library
#include &lt;Servo.h&gt; 

// Create a Servo object for each servo
Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;

// Common servo setup values
int minPulse = 600;   // minimum servo position, us (microseconds)
int maxPulse = 2400;  // maximum servo position, us

// User input for servo and position
int userInput[3];    // raw input from serial buffer, 3 bytes
int startbyte;       // start byte, begin reading input
int servo;           // which servo to pulse?
int pos = 90;        // servo angle 0-180
int i;               // iterator

// LED on Pin 13 for digital on/off demo
int ledPin = 13;
int pinState = LOW;

void setup() 
{ 
  // Attach each Servo object to a digital pin
  servo1.attach(3, minPulse, maxPulse);
  servo2.attach(4, minPulse, maxPulse);
  servo3.attach(5, minPulse, maxPulse);
  servo4.attach(9, minPulse, maxPulse);

  // LED on Pin 13 for digital on/off demo
  pinMode(ledPin, OUTPUT);

  // Open the serial connection, 9600 baud
  Serial.begin(9600);
} 

void loop() 
{ 
  // Wait for serial input (min 3 bytes in buffer)
  if (Serial.available() &gt; 2) {
    // Read the first byte
    startbyte = Serial.read();
    // If it's really the startbyte (255) ...
    if (startbyte == 255) {
      // ... then get the next two bytes
      for (i=0;i&lt;2;i++) {
        userInput[i] = Serial.read();
      }
      // First byte = servo to move?
      servo = userInput[0];
      // Second byte = which position?
      pos = userInput[1];
      // Packet error checking and recovery
      if (pos == 255) { servo = 255; }

      // Assign new position to appropriate servo
      switch (servo) {
        case 1:
          servo1.write(pos);    // move servo1 to 'pos'
          break;
        case 2:
          servo2.write(pos);
          break;
        case 3:
          servo3.write(pos);
          break;
        case 4:
          servo4.write(pos);
          break;

        // LED on Pin 13 for digital on/off demo
        case 99:
          if (pos == 180) {
            if (pinState == LOW) { pinState = HIGH; }
            else { pinState = LOW; }
          }
          if (pos == 0) {
            pinState = LOW;
          }
          digitalWrite(ledPin, pinState);
          break;
      }
    }
  }
}
</code></pre>
 6628: <p>The python code is polling through the servos sequentially/serialy, but the arduino code looks parallel. The arduino will parse the string and pass the command to the servo code, passing control back to the loop() command before the servo reaches its assigned position. Maybe that is your question: "is servo.write(position) a blocking call?" If so, the answer is no, it is non-blocking, the motors will drive at the motors speed to the final positions independently. If you want coordinated movement through this scheme, you have to break the movements up and coordinate it on the python side without feedback.</p>

<p>For instance, if you want to go from position (90,90,90,90) to position (81,0,90,90) you will need to know it takes 900ms (for example) to complete the -90 move, and break it up into 10 segments of 100ms to coordinate the (-1,0,-10,0) steps.  Doing the coordination without feedback will be awkward, and might be better handled from your python/UI side.</p>
