Arduino Serial Servo Control

UPDATE: Please read Arduino-Python 4-Axis Servo Control for the most current and detailed information on using Arduino to control one or more RC servos.

[blip.tv http://blip.tv/play/wkeg4hMA width=”648″ height=”380″]

One of the cool features of the Arduino platform is its ability to talk to other electronic devices using standard protocols. The big draw of physical computing, in my opinion, is the power it gives you to affect a limitless range of real-world objects with your PC, rather than just boring old monitors and printers.

This short tutorial will demonstrate one way to use Arduino to control a servo motor with a PC, using a USB cable and the Arduino’s serial library. It will in no way attempt to be an introduction to asynchronous serial communication, since such topics are better addressed elsewhere.

RC servos are comprised of a DC motor mechanically linked to a potentiometer. Pulse-width modulation (PWM) signals sent to the servo are translated into position commands by electronics inside the servo. When the servo is commanded to rotate, the DC motor is powered until the potentiometer reaches the value corresponding to the commanded position.

A standard RC servo has three wires: Ground (black or brown), Power (red) and Control (orange, yellow or white) and will move based on pulses sent over the control wire. The control pulses set the angle of the servo horn. The servo expects a pulse every 20 ms in order to gain correct information about the angle. The pulse width maps directly to the servo angle. Most servos will rotate 180°, and expect pulse widths between 1-2 ms or so.

servo-pwm

Image credit: Society of Robots

This project uses a JR Sport ST47 Standard servo, which accepts an input voltage between 4.8 and 6 volts — perfect for the Arduino’s 5V output pin. Connect the servo’s brown and red wires to the Arduino’s Gnd and 5V POWER pins, respectively (colored orange in the diagram below), and connect the servo’s orange control wire to the Arduino’s digital pin #2 (on the green row in the diagram).

arduino_board

Image credit: Arduino.cc

Using the Arduino IDE, upload the following code to the board, which will allow you to control the position of the servo over a serial connection. Pay particular attention to the variables minPulse and maxPulse, as these define the min and max pulse widths for your servo. As mentioned earlier, most servos expect a pulse width between 1-2 ms, however, a range of 0.5 ms to 2.5 ms (500-2500μs) may be required, depending on your servo. Experiment as necessary.

/*
 * NewSerialServo
 * --------------
 * Servo control from the Serial port
 *
 * Alteration of the control interface to use  keys
 * to slew the servo horn left and right.  Works best with
 * the Linux/Mac terminal "screen" program.
 *
 * Created 10 December 2007
 * copyleft 2007 Brian D. Wendt
 * http://principialabs.com/
 *
 * Adapted from code by Tom Igoe
 * http://itp.nyu.edu/physcomp/Labs/Servo
 */

/** Adjust these values for your servo and setup, if necessary **/
int servoPin     =  2;    // control pin for servo motor
int minPulse     =  600;  // minimum servo position
int maxPulse     =  2400; // maximum servo position
int turnRate     =  100;  // servo turn rate increment (larger value, faster rate)
int refreshTime  =  20;   // time (ms) between pulses (50Hz)

/** The Arduino will calculate these values for you **/
int centerServo;         // center servo position
int pulseWidth;          // servo pulse width
int moveServo;           // raw user input
long lastPulse   = 0;    // recorded time (ms) of the last pulse


void setup() {
  pinMode(servoPin, OUTPUT);  // Set servo pin as an output pin
  centerServo = maxPulse - ((maxPulse - minPulse)/2);
  pulseWidth = centerServo;   // Give the servo a starting point (or it floats)
  Serial.begin(9600);
  Serial.println("      Arduino Serial Servo Control");
  Serial.println("Press  to move, spacebar to center");
  Serial.println();
}

void loop() {
  // wait for serial input
  if (Serial.available() > 0) {
    // read the incoming byte:
    moveServo = Serial.read();

    // ASCII '' is 46 (comma and period, really)
    if (moveServo == 44) { pulseWidth = pulseWidth - turnRate; }
    if (moveServo == 46) { pulseWidth = pulseWidth + turnRate; }
    if (moveServo == 32) { pulseWidth = centerServo; }

    // stop servo pulse at min and max
    if (pulseWidth > maxPulse) { pulseWidth = maxPulse; }
    if (pulseWidth = refreshTime) {
    digitalWrite(servoPin, HIGH);   // start the pulse
    delayMicroseconds(pulseWidth);  // pulse width
    digitalWrite(servoPin, LOW);    // stop the pulse
    lastPulse = millis();           // save the time of the last pulse
  }
}

Once you’ve got the code uploaded, you’re ready to go! You can send and receive serial data using the Arudino IDE’s Serial Monitor, or you can use a Linux terminal (as in the video) with the `screen` command, like so:

screen /dev/ttyUSB0 9600

The first element of the screen command specifies the USB port, and the second the serial baud rate (9600). You may need to run ls /dev/tty* to find the correct USB port on your machine.

The theory behind this project can be extended to include a graphical user interface on the PC to control the servo motor, and maybe even the addition of an Ethernet connection for networked control.

Update: (10 Dec 2007) I wasn’t happy with the “Enter Servo Position (0-9):” interface shown in the video, so I revamped the code to allow left/right movements using the keys. This new sketch is the one that is currently displayed here. Tod E. Kurt already has a good implementation of the 0-9 angular-position concept if you prefer it.

References

1. Wikipedia, “Servomechanism
2. Society of Robots, “Actuators and Servos
3. ITP Physical Computing, “Servo Lab
4. ITP Physical Computing, “Serial Lab
5. Tom Igoe, “Serial Communication
6. Tom Igoe, “Interpreting Serial Data

9 responses to “Arduino Serial Servo Control

  1. Some exciting new developments are happening here. After reading Tinkerlog’s super-cool “Arduino XMAS Hitcounter,” I knew I had to learn how to get Python to talk to Arduino. As it turns out, Python’s pySerial module is not part of the standard library, so I had to get it from Sourceforge. Installation was a one-command affair.

    My Python “Hello Arduino” is reproduced below, and it’s designed to provide the ASCII characters expected by the Arduino sketch NewSerialServo, printed above. WHERE can I go from HERE?!?!

    #!/usr/bin/env python
    
    #
    # Hello Arduino!
    # serial-servo.py
    # to be used with Arduino NewSerialServo sketch
    #
    
    import serial
    import time
    
    # open usb serial connection to arduino
    ser = serial.Serial('/dev/ttyUSB0', 9600)
    print "USB port: ", ser.portstr
    
    i = 5
    
    while i > 0:
        # center servo and wait 1 sec
        ser.write(" ")
        print "Center"
        time.sleep(1)
        # move servo 90deg counterclock, wait
        ser.write(",,,,,,,,,")
        print "90 L"
        time.sleep(1)
        # center servo and wait 1 sec
        ser.write(" ")
        print "Center"
        time.sleep(1)
        # move servo 90deg clockwise, wait
        ser.write(".........")
        print "90 R"
        time.sleep(1)
        i = i - 1
    
    # close the connection
    ser.close()
    print "Sequence complete."
    
  2. Here’s another cool little Python script I came up with that’s a little more interesting to watch than the previous one. Hopefully it’s well-documented enough to let you figure out what it does. Try it out!!

    #!/usr/bin/env python
    
    #
    # Hello Arduino!
    # serial-servo-roxxor.py
    # to be used with Arduino NewSerialServo sketch
    #
    
    import serial
    import time
    
    # open usb serial connection to arduino
    ser = serial.Serial('/dev/ttyUSB0', 9600)
    print "nUSB serial port open: ", ser.portstr
    
    # center the servo to start
    ser.write(" ")
    print "Centering...n"
    time.sleep(1)
    
    # compute total length of sequence, in seconds
    sec = 0
    
    # perform 9 iterations, 10deg increments
    i = 9
    while i >= 1:
    
        units = i + 1    # units to pulse servo
        deg   = i * 10   # degrees moved L/R of center
        wait  = i * 0.1  # variable wait time - large deflections need more time
    
        print "Left", deg, "deg"
        for x in range(1,units):
            # move servo x units counterclock
            ser.write(",")
        # wait
        time.sleep(wait)
        #increment time counter
        sec += wait
    
        # center servo and wait
        ser.write(" ")
        print "Center"
        time.sleep(wait)
        #increment time counter
        sec += wait
    
        print "Right", deg, "deg"
        for x in range(1,units):
            # move servo x units clockwise
            ser.write(".")
        # wait
        time.sleep(wait)
        #increment time counter
        sec += wait
    
        # center servo and wait
        ser.write(" ")
        print "Centern"
        time.sleep(wait)
        #increment time counter
        sec += wait
    
        i -= 1
    
    # close the connection
    ser.close()
    print "nPort closed.  Sequence complete in", sec, "seconds.n"
    
  3. This was way cool!

    I used the servo motor in the article with an Arduino starter kit from AdaFruit.com. I was surprised how easy it was to get it all working.

    I was thinking of making a bot with wheels (stepper motor, not really sure how servos fit in) and IR sensor. The software would avoid objects and make some kind of map. It would be nice to connect wirelessly, too.

  4. i opened the serial monitor of arduino and i pressed the arrow keys and spacebar.it is not response at all….wht happen to my arduino?what i need to build this are only the arduino platform and the whole setup right??
    am i missing sths ??sths i need to build this but i didnt know…thanks :)

  5. Hi lawrence,

    I also had your problem and looked the < > signs up in the ASCII tabel. They aren’t 44 and 46, they are 60 and 62. Change them and it will work.

    Hope I helped you and all others who have this problem.

  6. Hi
    Nice tutorial.I am having a single doubt what is ASCII in the code and how are the values for ASCII calculated ? And will this code work for 6 channel radio.
    Thanks in advance

  7. The code as written did not work correctly and, in fact, was missing a closed curley brace. The main issue for me was the statement if(pulsewisth = refreshTime). Refreshing the servo has no correlation to what the pulse width is currently at, so I added a variable currentTime = 40, set lastPulse = 20, and ran refresh if(currentTime – lastPulse >= refreshRate). This forces the refresh to happen at startup, overwrites lastPulse using millis(), and then I overwrite and continually updated currentTime = millis() outside the refresh loop. This places a slight delay on the refresh if you change the pulseWidth since it must wait until currentTime – lastPulse >= refreshRate for it to write the new pulseWidth, but at 50Hz this is not at all noticeable. You can modify things if you don’t like this. The new loop looks like this:

    void loop() {
    // wait for serial input
    if (Serial.available() > 0) {
    // read the incoming byte:
    moveServo = Serial.read();
    Serial.println(pulseWidth);
    // ASCII ” is 46 (comma and period, really)
    if (moveServo == 44) { pulseWidth = pulseWidth – turnRate; }
    if (moveServo == 46) { pulseWidth = pulseWidth + turnRate; }
    if (moveServo == 32) { pulseWidth = centerServo; }
    Serial.println(pulseWidth);
    // stop servo pulse at min and max
    if (pulseWidth > maxPulse) { pulseWidth = maxPulse; }
    Serial.println(pulseWidth);
    }
    if ( (currentTime-lastPulse) >= refreshTime) {
    digitalWrite(servoPin, HIGH); // start the pulse
    delayMicroseconds(pulseWidth); // pulse width
    digitalWrite(servoPin, LOW); // stop the pulse
    lastPulse = millis(); // save the time of the last pulse
    }
    currentTime = millis();
    }

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s