Although the Arduino platform is ideal for standalone applications, it really comes to life when interfaced with a PC. Connect Arduino to a personal computer and you instantly add a ton of versatility and processing power to your project.

This tutorial will describe how to use Arduino to control a bank of four independent RC servos with your PC (or Mac, or *nix Box), using a USB cable and a modular Arduino-Python software stack.

The following discussion builds upon concepts presented in two previous articles, “Arduino Serial Servo Control” and “Joystick Control of a Servo.” As always, comments, critiques, or suggestions for improving or adapting this code are welcome and appreciated.

Project Outline

The primary goal for this project was to create a software stack that allows simple and flexible control of multiple servos from any type of Python script.

The solution has two basic components: (1) an Arduino sketch that waits for serial input from a connected PC, then moves each servo to its commanded position, and; (2) a Python module on the PC that opens the serial connection and formats the data packets expected by the Arduino.

Any other Python program written to sit on top of these two layers need not worry about the messy details of serial communication, but rather can just say something like, “Move servo #2 to 90 degrees.” Or, more precisely:

servo.move(2,90)

Easy, right? Let’s get started.

Part I: Smoke, Mirrors, and Hand-Waving

If you just want to get things up and running quickly, start here. These instructions will get your servos connected and obeying every whim of your PC in no time.

Hardware Setup

Hardware for this project consists of an Arduino module, four JR Sport ST47 standard servos, and a breadboard to create the circuit.

The servos each have three wires: Ground (brown), Power (red) and Control (yellow). Each of the Control wires will connect to a different digital pin on the Arduino board (pins 2 through 5 in our setup), and all of the Power and Ground wires will need to connect somehow to the 5V and Gnd pins.

The simplest way to accomplish this is to create a “bus” bar along one of the breadboard’s edges, as shown in the photo above. Simply route the Arduino’s 5V and Gnd to a convenient area on the breadboard, and connect all the servos.

Required Software: The Lower Layers

To get the effects seen in the video above, you’ll need at least the following two programs. Although this code is designed to control four servos, it also works as-is with fewer servos, and — with a few modifications — as many as twelve.

Download the code:

MultipleServos.pde: This is the Arduino sketch. Copy and paste this code into your Arduino IDE software and upload it to the board.

servo.py: This is the Python module which talks directly to the Arduino sketch “MultipleServos.” This script requires the pyserial module, available from Sourceforge. Save this script on your PC wherever you like, just be sure to name it “servo.py”.

New to Python? Welcome! Python is a versatile and fun language to learn, and it’s used by just about everyone, from newbies to NASA! Check out the Beginner’s Guide to get your bearings, or get the full skinny at python.org.

Customize the code:

Depending on your computer system and Arduino hardware setup, you may need to make a few modifications to the code.

Arduino: In the “MultipleServos” sketch, take note of the following three variables and make adjustments as necessary for your setup. See “Arduino Serial Servo Control” for more details regarding the minPulse and maxPulse variables.

int pinArray[4] = {2, 3, 4, 5}; // digital pins for the servos
int minPulse = 600;             // minimum servo position
int maxPulse = 2400;            // maximum servo position

Python: In the “servo.py” script, you’ll most likely need to change the value of the usbport variable, which tells Python how to find your Arduino (On Windows, it’ll be something like ‘COM5′. On a Mac, ‘/dev/tty.usbserial-xxxxx’. On Linux, ‘/dev/ttyUSB0′.). Try running ls /dev/tty* from a Mac or Linux terminal for a list of available ports. [ToDo: Modify the script to make this step unnecessary.]

Test the code:

Once your hardware is set up and the software is installed, you can test the system’s basic functionality from the Python interactive interpreter, like so:

~/path/to/servo.py$ python
>>> import servo
>>> servo.move(2,150)

The servo.move() method takes two arguments, both integers. The first is the servo number you wish to move, 1-4. The second is the commanded angular position of the servo horn, from 0-180 degrees. So, if you want to move Servo #3 fully clockwise (180 degrees), you’ll type servo.move(3,180). Cake, baby!

Optional Software: From Totally Geek to Totally Chic

The following scripts are designed to leverage the functionality of the servo.move() method for simple and readable code. Make sure these files reside in the same directory as “servo.py”.

  • servodance.py: A cascading effect that feels like watching a quarter spiral down one of those funnel-shaped wishing wells.

  • multijoystick.py: Allows joystick control of the servos, with each joystick axis controlling a single servo. This code could the basis for a Wi-Fi RC vehicle of some kind.

  • servorandom.py: The final servo sequence seen in the video, with individual servos moving to random positions and then waving “goodbye” in unison.

With any luck, you should now have everything up and running just like in the video!

Part II: Getting Down to Brass Tacks

Next, let’s take a look under the hood to see how it all works. If you’re the type that just wants to get things working and damn the details, STOP HERE. Otherwise, continue on, and I’ll do my best to explain how the code “do what it do.”

The Problem Set

Asynchronous serial communication is not perfect. Sometimes there are errors, dropped packets, confusion. Sometimes the mail does not get through. In both of the previous two serial/servo projects, the Arduino expected only one byte from the PC, and in both cases that byte represented a commanded servo position — and nothing more. If a byte was missed or skipped, it wasn’t a big deal, another one was sure to come along, and it was impossible to misinterpret.

This project presents a couple of new challenges. First, we are controlling more than one servo, so the Arduino needs more than one command element for each move. As we’ve seen above, it needs to know (at least) which servo to move, and how much to move it. Secondly, we have the problem of communication. This time, we’re sending two command elements for each move (servo number & position), and these elements are clearly not interchangable. That is, if we want to send servo.move(4,90), we need to make sure that Arduino knows that the ‘4′ means “Servo #4″ and the ‘90′ means “90 degrees.”

Tom Igoe’s article, “Interpreting Serial Data,” contains an excellent discussion of some of the problems involved in serial communication, and lists several issues that need to be addressed in every project, namely:

  1. How many bytes am I sending? Am I receiving the same number?
  2. Did I get the bytes in the right order?
  3. Are my bytes part of a larger variable?
  4. Is my data getting garbled in transit?

The Arduino’s Serial.read() function reads one byte of data at a time from its serial buffer. Think of the serial buffer as a mailbox. It’s a small (128 bytes) area of memory where incoming serial messages are stashed until the Arduino is ready to read them. Every character we send from the PC to Arduino is one byte. So, while we could send the Arduino something very unambiguous like, “Yeah, hi, Arduino, it’s the Linux Box again. What’s happening? If you could go ahead and move Servo #4 to the 90-degree position, that would be great. Thaaanks,” (163 bytes) it’s obviously better if we can come up with something a little more terse.

However, as we’ve seen, if we just send over the characters ‘4′, ‘9′, and ‘0′ — remember, each character is a byte — the Arduino might get confused. This problem is amplified when more commands start stacking up in the buffer. Let’s say now we command servo.move(2,180) and servo.move(3,120). Now the buffer should hold {4,9,0,2,1,8,0,3,1,2,0}, except–OOPS!–one of the bytes got dropped along the way, so now it holds {4,9,0,2,1,8,3,1,2,0}. “Wait, which servo did you want me to move?” You can clearly see a problem developing.

Solution: Data Packets and Start Bytes

Luckily, part of the solution is handled in the way Arduino communicates. Arduino uses ASCII encoding to represent alphanumeric characters. Each character sent over the wire is converted to the binary equivalent of a decimal value from 0 to 255. [See this conversion chart for specifics.]

So, for example, if we send over the character ‘A’, Arduino recognizes this as its decimal value, ‘65′. We won’t get too deep into this concept except to say that the implementation is great for our application, because as long as the values we’re sending are less than 255, they’ll fit neatly into one byte. Since the largest value we send is 180, we only have to send two bytes per command.

Now, if you’ve looked at the ASCII conversion chart, you’ll recognize that doing this every time you want to send a command would be a real pain. Also, trying to teach Python this chart would take up a lot of unnecessary code. Thankfully, this problem is already solved for us with Python’s chr() function. Wrap any decimal value from 0-255 in chr(), and you get back its ASCII equivalent. A few examples:

~$ python
>>> chr(65)
'A'
>>> chr(110)
'n'
>>> chr(13)
'r'
>>> chr(9)
't'

You get the idea, but notice that ASCII doesn’t just represent letters and numbers, but also symbols and other “control” or “non-printing” characters like line-feeds, returns, and tabs.

So, now if we want to send servo.move(4,90), we only need two bytes, the ASCII equivalents of ‘4′ and ‘90′, represented in Python as chr(4) and chr(90), and interpreted by the Arduino sketch as, once again, simply ‘4′ and ‘90′. Easy! [Seriously, if your brain explodes at this point, or you're bleeding from the ears, it's understandable. I don't like it any more than you do, but stick with me, it'll all work out neatly in the end.]

Packets, Headers, and Payloads

Okay, great, now instead of just digits in Arduino’s serial buffer, we have meaningful values. Part of the problem is solved, but we still haven’t addressed the issue of dropped or missing bytes. That is, how will the Arduino know that a ‘4′ is “Servo #4″ and not “4 degrees” when pulling values out of a crowded buffer like {4,90,2,180,3,0,1,110} ?

The answer is data packets. Very simply, instead of just sending a long string of numbers to Arduino, we’ll send a very specific ordered message, a packet of values, that is intended to be read and interpreted as a whole and in order, or else discarded completely.

Now, the structure of a packet can be as simple or as complex as we need it to be, as you might have noticed if you followed that last link. But all we really need is some means of ensuring that Arduino doesn’t confuse one value for another.

Essentially, our Python script needs to tell Arduino three things:

  1. Here comes a new servo command.
  2. Servo number to move.
  3. Commanded servo position.

We’ve already been sending the last two elements, the servo number and position. Here, we’re adding a third element, which we’ll call the header or the start byte. Our header, like the rest of the data in our packet, will be just one byte long, and contain no real information other than the conceptual message, “I am a header.”

The order of this message is important. Every packet sent over the wire, or read from the serial buffer, will now have the following format:

(Header, Servo Number, Servo Position)

or, more tersely:

(startbyte, servo, angle)

What to use as a startbyte? Well, we’re only using the values from 0-180 as either our Servo Number or Servo Postion. Any value from 181 to 255 would be unique. We’ll use ‘255′ just to make it obvious. So, every packet will now look something like one of the following:

(255, 1, 90)
(255, 2, 180)
(255, 3, 0)

And the Arduino’s serial buffer would look something like:

{255,1,90,255,2,180,255,3,0}

Now, instead of reading byte after byte and hoping for the best, Arduino will wait until a minimum of three bytes arrive in the buffer, and then read the first byte to determine whether or not it is, in fact, a header (255). If it’s not, Arduino skips that value, and moves on to the next byte without touching the servos. When it finally sees a header, Arduino continues reading the next two bytes, in order, and assigning them to the Servo Number and the Servo Position, respectively. If either of those two values is ‘255′, Arduino assumes something is wrong, and skips everything until it reads a new header.

Side Note: Authoritarian Flow Control

“Now just a minute!” you’re saying. “If that is the case, then some of the commands Python sends to the Arduino will be totally ignored!” And you’re right. This method of serial flow control is definitely one-way, with no error-checking. Other methods, such as “call-and-response” or “handshaking” are much better at ensuring accuracy, since there’s a back-and-forth arrangement that can call for data to be re-sent in the event of dropped packets. But the two-way protocol this method requires is much slower.

We have to make an engineering decision. In our application, which is more important, accuracy or quick response? It depends on exactly how you are using the servos, but if you consider say, a joystick-controlled robot or RC vehicle application, then clearly an immediate response and quick visual feedback is preferable to perfect accuracy. If you command “turn right” with a joystick, and your vehicle doesn’t respond appropriately, you’ll just instinctively add more right stick input.

Perfect accuracy is not required.

Writing the Code

Very briefly, let’s look at how the above concepts are implemented in both the Python and Arduino software.

Python Implementation

Whenever we call the servo.move() method, the Python script servo.py handles the serial communication details using the pyserial module, and formats the arguments into the data packet outlined above. The bare-bones version looks like this:

#!/usr/bin/env python

import serial
usbport = '/dev/ttyUSB0'
ser = serial.Serial(usbport, 9600, timeout=1)

def move(servo, angle):
    if (0 <= angle <= 180):
        ser.write(chr(255))
        ser.write(chr(servo))
        ser.write(chr(angle))
    else:
        pass

Arduino Implementation

Arduino opens its own serial connection, waits for at least three bytes to fill the buffer, then starts reading:

/** MultipleServos.pde (bare bones) **/

void setup() {
  // open serial connection
  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?
      servoPosition = userInput[1];
      // packet check
      if (servoPosition == 255) { servo = 255; }

If Arduino gets a complete packet with header, servo, and angle values, it calculates the correct pulseWidth for the commanded servo angle, and assigns that value to the appropriate servo. If the value of servo is not between 1 and 4, the loop exits without assigning any new values.

      // compute pulseWidth from servoPosition
      pulseWidth = minPulse + (servoPosition * (pulseRange/180));
      // stop servo pulse at min and max
      if (pulseWidth > maxPulse) { pulseWidth = maxPulse; }
      if (pulseWidth < minPulse) { pulseWidth = minPulse; }
      // assign new pulsewidth to appropriate servo
      switch (servo) {
        case 1:
          servo1[1] = pulseWidth;
          break;
        case 2:
          servo2[1] = pulseWidth;
          break;
        case 3:
          servo3[1] = pulseWidth;
          break;
        case 4:
          servo4[1] = pulseWidth;
          break;
      }
    }
  }

Finally, once each loop, whether it receives any serial data or not, Arduino checks to see if the servos need a pulse. To hold their current positions, RC servos expect a pulse at 50Hz (every 20ms), so Arduino keeps track of the lastPulse. If the timer is up, all servos get a pulse.

  // pulse each servo
  if (millis() - lastPulse >= refreshTime) {
    pulse(servo1[0], servo1[1]);
    pulse(servo2[0], servo2[1]);
    pulse(servo3[0], servo3[1]);
    pulse(servo4[0], servo4[1]);
    // save the time of the last pulse
    lastPulse = millis();
  }
}

// create the pulse
void pulse(int pin, int puls) {
    digitalWrite(pin, HIGH); // start the pulse
    delayMicroseconds(puls); // pulse width
    digitalWrite(pin, LOW);  // stop the pulse
}

Whew! We're Done.

Well, if you've made it this far, congratulations: you're totally insane. I hope the above dissertation helps at least one person better grasp these concepts, since it took me across many web pages and into several late nights to find the answers. Good luck!

References

  1. Tom Igoe, Making Things Talk: Practical Methods for Connecting Physical Objects
  2. Tom Igoe, "Serial Communication"
  3. Tom Igoe, "Interpreting Serial Data"
  4. Society of Robots, "Actuators and Servos"
  5. ITP Physical Computing, "Servo Lab"
  6. ITP Physical Computing, "Serial Lab"


Reader Comments Add your own

  • 1. Allen Riddell  |  April 27th, 2008 at 12:07 pm

    This is really excellent. Thank you!

    p.s. any tips on how to extend servo wires? I’m finding that the wire is really thin and frays easily. Is there a name for that black terminal? Or does one need some sort of wire crimp?

  • 2. Brian  |  April 27th, 2008 at 7:47 pm

    Hey Allen, thanks for the props! The best solution I’ve found so far is the servo wire extensions you get at your local hobby store. They come in several different lengths, although it looks like there might be a practical limit of a few feet.

    For longer wire runs or applications other than radio control, I’d probably chop the connectors off a couple of the short extensions and splice them to something beefier. There could be signal degradation and/or interference with the pulses over a longer run; you’ll probably have to experiment a bit with setup and voltages if you’re going beyond three feet or so.

  • 3. John  |  May 12th, 2008 at 5:41 am

    Gidday , this looks great. I am into wanting to make a motion platform for a flight simulator.am looking at using 12 v motors as servo’s. will this system be able to be beefed up to run these , or will do the job as is. My ? may sound silly ,But Be patient with me as I’m new to this .

  • 4. Brian  |  May 12th, 2008 at 9:09 am

    Hey John, you could certainly use Arduino and a PC to control your project, but the hardware setup would be substantially different, primarily due to your need to isolate the 12V devices from the Arduino’s 5V circuit. You code would probably need to be different as well, although the modular theory presented here could still apply. Try posting your question over on ladyada’s forum. You’re sure to get a wealth of helpful responses over there.

  • 5. Keith Chester  |  May 12th, 2008 at 10:39 am

    I’ve had to do a similar program that I will be posting about soon on my project blog. When you need to make sure information goes through but don’t want to try and synchronize the serial port connection, send data both ways through arrays. Certain spots in the arrays are reserved for data for a certain spot. If you continually resend the array, then it will catch dropped bits if you reserve the first and last spots for a certain value. If the array does not begin or end with the right values, it ignores those values. If it does, it just takes the values of the arrays and applies them to your outputs. This works for inputs as well.

  • 6. drewp  |  May 19th, 2008 at 3:16 am

    Hi, I’m doing similar stuff, but I’m controlling ShiftBrites instead. Here’s my serial port setup code, which tries multiple port devices until it finds one that works:

    getserial.py

  • 7. loonquawl  |  June 8th, 2008 at 7:43 am

    Hi. Great tutorial! It worked right from the start, and left me happiliy tinkering with the code, without hassle. I used some servos i had lying around, old ones and thus still analog. Now i’d like to use some stronger and faster ones, and i had my eyes on digital servos. from the vantage of control this should not make a difference, the whole ‘digital’ thing about digital servos is kinda overstated anyway, but those servos will be using a lot more juice (2 amp peak 7 per servo) – do i’d like to hook them to a separate current source, not simply give a wall wart to my diecimila, but running the power/ground cables of the servo directly off a wall wart – after all this preambling: I don’t have much clue of electronics -> Is it possible to have the power run directly to the servos and have a branch of that supply run to the diecimila so the brunt of the current will be caught by the wall wart, but the control is still on the same level?

  • 8. Brian  |  June 8th, 2008 at 7:18 pm

    loonquawl: I’m glad everything worked for you out of the box; thanks for the feedback! There are undoubtedly several different options for keeping the power separate. I’d probably just power the Arduino from USB, then use the wall wart to power the servos. I’m not sure about power requirements for digital servos, since I’ve never used them, but you can get a nice little 5V/3.3V Breadboard Power Supply kit from SparkFun that will run off the same 9V wall wart that works on the Diecimila. You might also want to take a look at Adafruit Industries’ Arduino Motor Shield kit as well. At the very least, you’ll get some ideas for circuits!

  • 9. Popcorn  |  August 27th, 2008 at 11:35 am

    Hi John, as Brian says you will fry the Arduino if you even try to run a modest DC motor from it directly. As an experiment I tried to isolate some commercial hobby servos from my Arduino by using a Darlington Array IC (ULN2003). It did not work, for me at least. The array seems to monkey with the signal in a way that my non-electronics brain does not fathom. (I expect I will be enlightened)

    Can I suggest it might be better to run stepper motors, rather than servos for your flight simulator? There are many circuits that you could google to drive even power hungry stepper motors. I’m guessing your motors are to move scenery, or perhaps even the ‘pilot’ so they would have to be reasonably powerful.

    Good luck on your project and please share your experiences with the community.

    Pop

  • 10. Joe  |  October 2nd, 2008 at 3:40 am

    Hi,John,your tutorial provide a gread help for me,but I can not finish “multijoystick”.when I key in “import servo”and “import pygame”,and move my “joystick”,my servo motors don’t move like your video,could you tell me what’s wrong ?

    And could you teach me the other way to control a number of motors in the same time just like your video?

  • 11. Freeman  |  October 30th, 2008 at 3:54 pm

    Hi, this is gold. I am in the processes of building a robotic arm controlled by arduino, but as i am new to programing, i am wondering if there are graphical interface add-ons for controlling 6+ servos. (especially ones already written for robotic arms) Thanks in advance

    Freeman

  • 12. Brian  |  October 30th, 2008 at 8:42 pm

    @Freeman: Thanks for stopping by. I haven’t seen a GUI for controlling servos out in the wild, but that doesn’t mean there isn’t one. One of the goals I had in mind when creating servo.py was to allow enough flexibility to add a WxPython or TKinter GUI on top of it. Python is perfect for that kind of thing. Let me know what you come up with!

  • 13. Freeman  |  November 4th, 2008 at 3:37 pm

    hi again, i have purchase my hardware for the robotic arm and am currently trying to run this code, but i can not seem to import the servo.py… in the command line interface it says no module was found. can you point me in the right direction? im running python 2.6

    thanks again

  • 14. Brian  |  November 4th, 2008 at 3:49 pm

    @Freeman: In order for import servo to work, the file servo.py must be in the same directory as the program calling it. So, if you’re calling it from the interactive interpreter, you must run the python command (to launch the interpreter) from the same directory in which servo.py lives. If you’re having trouble with the import serial command, it just means you need to install the pyserial module (see text above for details). Hope that helps.

  • 15. Freeman  |  November 4th, 2008 at 4:07 pm

    thank you, that was the problem, and that i had 2.6 instead of 2.5.2 running

    thanks again, this is a great start for me

  • 16. Freeman  |  November 4th, 2008 at 8:17 pm

    I just want to say thank you again. I have edited your multijoystick code to run five servos with a game pad and it’s working without a hitch. I could not believe that I can get so much done on my first day with the arduino.

    Thanks again

    Freeman

  • 17. Chad  |  November 11th, 2008 at 9:49 pm

    Very nice indeed!!!

    This article has inspired me to try the same thing with my next bot. An arduino controlled rig from a remote laptop(linux of course) via bluetooth. I figure that bluetooth is the best option to send packets.

    Basically a rover similar to the SRV-1 by surveyor but home-brew.

    Wish list: -Full joystick control of a skid-steer tank-type chassis.(constant rotation servo control required) -Joystick hat button to control the pan and tilt of camera (most likely a cheapie until I feel the need for machine vision capabilities) -Additional joystick buttons for whatever I deem geek enough. Search lights. Blue led strobes, flares(JK) -All run through a python to arduino interface.

    -The icing on the cake would be an WxPython gui for graphical representation of current pin states. Eg search lights on. Yes I could see the lights are on but this is for geek cred ;) -While I’m day-dreaming it would be nice to get sensor feedback displayed in a gui or as an overlay on the video feed ala HUD. Items such remaining battery power represented in a graphical or numerical format, compass readings, etc.

    So… any progress on the integration of a WxPython gui with the arduino? :)

  • 18. Brian  |  November 12th, 2008 at 10:08 am

    @Chad: I love it! Your surveyor bot sounds like one of my over-the-top harebrained schemes! ;) Instead of Bluetooth, you should check out XBee/ZigBee modules. The range is better than Bluetooth, and they interface easily with Arduino. (Oh, and they’re relatively cheap too.) You can get the modules from Sparkfun, Adafruit and others, along with XBee shields for Arduino. Have fun and keep me posted!

  • 19. Mitchell  |  December 8th, 2008 at 2:41 pm

    Could this be used with analog / digital inputs such as GPS/Compass and micro switches?

  • 20. Brian  |  December 9th, 2008 at 9:11 am

    @Mitchell: Absolutely! Check out Ladyada’s GPS datalogger shield for Arduino and her great Arduino tutorials for more info.

  • 21. Silas Baronda  |  December 29th, 2008 at 4:04 am

    A lot of your source code can’t be found. servo.py and multijoystick.py in particular.

  • 22. Brian  |  January 1st, 2009 at 10:04 pm

    @Silas: Sorry about that and thanks a bunch for the feedback! I recently switched the site over to a different server and apparently I didn’t have all the bugs worked out. You should be able to grab the code now. Let me know if you still have problems.

  • 23. Adam  |  January 6th, 2009 at 9:02 pm

    thanks!! this is great. I used the arduino code, but instead of python i used visual basic 2005 to make a form control. After i got vb to send ascii characters it worked great!!!

  • 24. Miguel  |  January 14th, 2009 at 11:54 am

    First of all allow me to congratulate you on this excellent work of yours. It’s an excellent tutorial on how to handle servos with Arduino. I have only one question. Can you please explain how can we send ascii equivalents to Arduino, ranging from 0 to 255, when the ascii table of Arduino only goes from 0 to 127?

  • 25. Brian  |  January 15th, 2009 at 1:07 pm

    @Miguel: Thanks for your comments. The short answer to your question is, “No, I can’t explain it very well.”

    The long answer is that although ASCII is a 128-character code, it is a deprecated subset of Unicode, which supports 256 characters. Since the Python chr() function supports 256 characters, and since Arduino’s serial library accepts these values, it works for our purposes.

    I specifically didn’t get into the actual ASCII value conversions in this tutorial, since only the decimal equivalents were important to this application. My brain is exploding just thinking about it right now, so that’s as deep as I’ll get. Check out the Wikipedia page on ASCII if you really want the gory details.

    I don’t necessarily like the way serial communication works, but I tried to explain how to make it work for the purposes of this application. Tom Igoe’s article does a much better job of explaining it than I do.

  • 26. daniel  |  January 31st, 2009 at 1:04 pm

    Excellent article to get started with servos. I really appreciated the post. I’ve been able to follow the tutorial, but when i move one servo the other one kind of shake a little. I’m using Tower Hobbies TS-53 standard servos, and arduino diecimilanove. Any thoughts why this could happen??

    Thanks in advanced!

  • 27. Brian  |  January 31st, 2009 at 1:41 pm

    @Daniel: Thanks for stopping by! I’ve encountered this phenomenon as well, and it can be difficult to troubleshoot.

    Basically what’s happening (I think) is that the servo is “confused” by pulses it is either receiving or NOT receiving from the Arduino. The servos expect a pulse every 20ms or so, or they will start to “drift.” You might want to experiment with the refreshTime variable to see if this changes anything.

    You might also try tweaking the minPulse and maxPulse variables. Start with just one servo and send it commands for full left and full right (0 and 180 degrees). The servo should not “buzz” at either of these settings. If it does, you probably need to shorten the pulse width.

    Sometimes operating multiple servos with rapid command inputs will cause uncommanded servos to buzz. I’m not sure if this is because Arduino is getting mixed signals or what. You’ll notice in the servorandom.py script that I’ve included the variable t = 0.23 #sleep time. As I recall, this was to space out the servo commands so they didn’t all pile up in the buffer. I think if you shorten that time, you’ll start to see servo buzz. Remember, your PC can send data a lot faster than a servo can move. You have to give it some time to do its thing.

    Finally, if you’re using a joystick or something similar as an input device, it’s hard to avoid some uncommanded “jitter.” The joystick is constantly sending position data to the Arduino — even when its centered — and I think this tends to overload the buffer. If you look closely at the video above, you’ll notice that servo #1 (far left) tends to vibrate even when its axis is not being commanded by the joystick. I have not found a solution to that particular problem — in fact, from a vehicle-control standpoint, I think I basically wrote it off as a non-issue.

    Give those suggestions a try and report back if you get time. I’d like to hear how things turn out!

  • 28. daniel  |  February 1st, 2009 at 1:57 pm

    Brian, Thank you very much for your reply, I will try adjusting those things, and try them with the servos in my plane to see if that’s really an issue or not. Thanks a lot!

  • 29. Jim  |  February 4th, 2009 at 7:06 am

    Hi Brian. Thanks for the excellent tutorial. In the comment (#27) you make above about joysticks and the jitter issue. This is often combated using a deadzone, where anything below 1% or 2% of the sticks motion is clamped to Zero.

    In the tutorial you suggest that this method works well if you want to send 8bit data, (i.e. 0-255). Can you describe how you would go about sending more? perhaps a 10bit number or even a float (for GPS NEMA data for instance?)

    Over the next few days i’m going to try and recreate your servo.py lib in processing. I’ll let you know how i get on and send you a link when it’s done.

    cheers Jim

  • 30. Brian  |  February 4th, 2009 at 8:33 am

    Hey Jim, thanks for the tip. I’ll try it out. I haven’t messed around with GPS at all, but it’s next on my list, either with Arduino or with something like a TinyTrak and the APRS network.

    Limor Fried (ladyada) has built a cool-looking GPS shield for Arduino that you might want to check out. I think it’s mostly just a datalogger, and from a quick glance at your website, it looks like you might want to do vehicle control. There’s a bunch of code for parsing NMEA sentences that’s blowing my mind just to look at right now. She’s also got a great forum over there. Limor’s actually an MIT EE grad, so if anyone can answer your question, she can.

  • 31. Jim  |  February 4th, 2009 at 10:48 am

    Yes, respect due to Limor, she’s a great source of knowledge as as well as cool toys :)

  • 32. daniel  |  February 4th, 2009 at 4:15 pm

    Hi Brian, thanks again for all. My next question is if you had tried this wireless with xbee. I’ve been trying to do that, but not with the result i’ve expected. If send moves more than 1 every 3 or 4 seconds it does get the message well. I’m using Xbee ZB 2mW ZigBee Pro RF modules at 9600. (I’ve also tried with 4800 and faster). (I’m actually getting two XBee Pro 50mW Series 2.5 RPSMA in the mail soon). Well thanks in advanced.

  • 33. Brian  |  February 5th, 2009 at 10:35 pm

    Sorry Daniel, I haven’t used the Xbee radios yet. I’m afraid I can’t help with that one.

  • 34. daniel  |  February 6th, 2009 at 11:13 am

    thanks anyway man, I’ll keep trying.

  • 35. phil drummond  |  March 21st, 2009 at 12:57 pm

    This stuff looks great, however it is “asking” for something called “pygame” which as far as I can tell is not a “default” package… dependencies! Please check your dependencies when you project stuff out to the “learning” public. So far, all I have been able to do with this pygame requirement is get fully lost in the Python install nightmare… version this, requirement that… All I wanted was to try your cool application on my BoArduino! NOT re-learn the entire Python development system! By the way, the Sketch loads just fine, the pyserialstuff seems to work, but the multijoystick.py thing stopped me cold.

  • 36. Hugo  |  March 22nd, 2009 at 7:11 am

    Thanks for the nice application. However, I’m not even able to run the python script. Can someone help me troubleshooting this. I’m far from an expert. here is what I get when I start servo.py in command lines:

    C:\Python30>servo.py python
    File "C:\Python30\servo.py", line 40
    print "Servo angle must be an integer between 0 and 180.\n"
    SyntaxError: invalid syntax
  • 37. Brian  |  March 23rd, 2009 at 9:24 pm

    @Phil: The multijoystick.py script depends on three additional modules: servo.py, pyserial, and pygame, each of which are mentioned in the header comments of that script. You’ll have to install all three before that script will work. Specifics about joystick control were beyond the scope of this article, as I mentioned in the introduction.

    For more information about controlling servos with a joystick, see the article entitled “Joystick Control of a Servo.” For detailed instructions on installing Pygame on your system, consult the Pygame documentation.

  • 38. Brian  |  March 23rd, 2009 at 9:32 pm

    @Hugo: Thanks for including your error messages. From the looks of things, you’re running Python 3.0, and it doesn’t seem to like the code syntax I’ve used in the servo.py script. I wrote that script using Python 2.5.x, and Python 3.0 is not backwards compatible with earlier versions. I haven’t played around with 3.0 at all, so I’m not sure of all the differences, but it looks like my syntax needs to be altered a bit to use parentheses, as in this example from the Python 3.0 docs.

    So, instead of:

    print "Servo angle must be an integer between 0 and 180.\n"
    

    Try

    print("Servo angle must be an integer between 0 and 180.\n")
    

    I’m not sure if that will work, since I’m still running 2.5.x, but it’s worth a shot! Let me know what happens. :)

  • 39. Joel  |  March 24th, 2009 at 7:54 pm

    This project looks great, but I can’t get the Python code to work. When I try to ‘import servo’ it returns an ImportError: DLL load failed for importing win32file. What is this? I’m running 2.6.1 and have downloaded pyserial.

  • 40. Brian  |  March 24th, 2009 at 8:57 pm

    @Joel: Thanks for stopping by. I’m afraid I don’t run Python on Windows very often; I’m mostly a Linux guy. But I seem to recall needing the pywin32 module to run these scripts on Windows. Try installing that and see if it helps. Also, you need to launch the Python interpreter from the same directory in which servo.py resides, otherwise Python won’t be able to find it.

  • 41. Jeff  |  April 29th, 2009 at 7:57 am

    I installed pygame and pyserial but when i import them it has importerror:dll load issues. Is it just because I have a newer version of python than the modules support?

  • 42. Jeff  |  April 30th, 2009 at 7:54 pm

    I get this when I try to run the code, everything is installed and I got rid of a bunch of other errors earlier but I still get this one.

    Traceback (most recent call last): File "C:\Python25\Lib\idlelib\servo.py", line 20, in ser = serial.Serial(usbport, 9600, timeout=1) File "C:\Python25\Lib\site-packages\serial\serialutil.py", line 171, in init self.open() File "C:\Python25\Lib\site-packages\serial\serialwin32.py", line 53, in open raise SerialException("could not open port %s: %s" % (self.portstr, msg)) SerialException: could not open port COM5: (5, 'CreateFile', 'Access is denied.')

    Any ideas?

  • 43. Jeff  |  April 30th, 2009 at 8:05 pm

    I figured out the error. In the servo.py implementation it says ser = serial.Serial(usbport, 9600, timeout=1)

    it should be

    ser = serial.Serial(usbport, 9600)

  • 44. foobahr  |  May 3rd, 2009 at 9:44 pm

    I also experienced some jitter issues and am now using the interrupt based library servo routines. It’d be interesting to re-work this code to use those, though I’m not sure about controlling more than two servos.

    More here:

    http://forums.ladyada.net/viewtopic.php?f=25&t=8637&p=51884#p51884

Add a comment




Basic HTML tags and/or Markdown are allowed.