Saturday, January 22, 2011

Up Next: Servo and Serial

Okay, we've got the computer and the arduino board working together. Let's start doing something interesting, like driving a Servo.  Again, I did a little research on the Arduino website, and started with one of the sample sketches.

This has me thinking back to college where I spent many hours getting a Motorola HC12 to drive a servo.  You had to enable the right pins, configure your PWM channels to make sure your duty cycles matched the servo specifications, break out the old monster of an oscilloscope to figure out what you're actually sending, when it doesn't work-- all in assembly code.

So, how do you do it with Arduino?


 
#define SERVO_PIN 11

Servo myservo;  // create servo object to control a servo 
                // a maximum of eight servo objects can be created 

void setup () 
{
  myservo.attach(SERVO_PIN);  // attaches the servo pin to the servo object 

}

void loop ()
{
  myservo.write(45); // go to the first quarter position
  delay(1000);
  myservo.write(90); // go to the middle point
  delay(1000);
  myservo.write(135); // go to the last quarter position
  delay(1000);
}

Are you kidding me?  It almost makes me want to cry when I think how much time I spent debugging a similar procedure just a few years ago.

Okay, now I've got software affecting the physical realm.  This is where robotics starts to get really exciting.  (I said starts...)  So, what to do next?

Let's throw that blinky LED back in and have the servo move a little smoother through it's full range of motion. I'm going to set the LED the same way as the previous post, call a new routine to toggle it, and a couple of loops for the servo.

 
for(pos = 0; pos < 180; pos += 5)  // goes from 0 degrees to 180 degrees 
{                                  // in steps of 5 degrees
  myservo.write(pos);              // tell servo to go to position in variable 'pos' 
  delay(50);                       // waits 50ms for the servo to reach the position 
  toggleLed();
} 

for(pos = 180; pos>=0; pos-=5)     // goes from 180 degrees to 0 degrees 
{                                
  myservo.write(pos);              // tell servo to go to position in variable 'pos' 
  delay(50);                       // waits 50ms for the servo to reach the position 
  toggleLed();
} 
 

Nice, now we've got some smooth motion, and some blinking action too. Okay, now we've got this going back and forth fairly smoothly, ad infinitum. Let's see if we can take some control over the situation here... let's take advantage of the Serial connection.

The Serial interface is nearly as simple as the Servo one. You can read and write to your computer over the USB cable, so when you open the Serial Monitor in the Arduino Environment, you can read what it has to say, and tell it to do things. So, let's make an interface to tell the servo to move somewhere.

 
char buffer[9];      // a buffer to store data coming back from the serial interface
char buffer_pos = 0; // the current position into the buffer

void setup() 
{ 
  // start serial port at 9600 bps:
  Serial.begin(9600);

  ...
} 

void loop() 
{ 
  int read_val;  // variable to store the integer from the Serial.read() method
                 // this is the ASCII value of what is typed in at the Serial
                 // Monitor window
  
  // if we've got something ready from the serial interface
  if ( Serial.available() > 0 )  
  {
    // read in the data, up to the max size of the buffer.
    while( -1 != (read_val = Serial.read() ) && buffer_pos < 8)
    {
      // put the value in the buffer, and increment the position
      buffer[buffer_pos++] = read_val;  
    }
    buffer[buffer_pos] = '\0';       // terminate the string.
    Serial.print("Read in value: "); // add some feedback to the Serial Monitor
    Serial.println(buffer);          //   so we know the command was received
    pos = atoi(buffer);              // turn the input data into an integer value
    pos = (pos < 170 ? pos : 170);   // clamp the input values at reasonable 
    pos = (pos > 0 ? pos : 0);       //  values for your servo.  Mine is finicky ~ 180
    Serial.print("Moving to ");      // Tell Serial Monitor where we're actually going
    Serial.print(pos);
    myservo.write(pos);              // And go there.
    toggleLed();
    delay(1000); 
    buffer_pos = 0;                  // Clean up after ourselves.
    memset(buffer,0,9);
  }
}


Nice, now we have manual control over the servo, and we're able to see where it is going, and where it has been, all this using only a few pins on the Arduino.

Okay, now that we can make it go, let's figure out how to make it stop. Arduino doesn't have a "stop" command that I've been able to find yet, but avr does have power controls. Let's just tell it to go to sleep and reduce as much power as it can.

 

#include <avr/sleep.h>

void loop()
{
  ...

    if (pos == -1)                   // If we've received the "all done" command 
    {
      set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set the sleep mode
      sleep_enable();                // enable sleeping
      sleep_mode();                  // then activate the sleep mode
    }

  ...
}

Okay, now we can conserve our power when we've completed our task.

Let's put everything we've learned here together.

A link for the source code is here.

 
/* Serial Servo Control
 *
 * Derived from:
 *   Sweep by BARRAGAN <http://barraganstudio.com>
 */

#include <Servo.h>
#include <stdlib.h> //atoi
#include <avr/sleep.h>

#define LED_PIN 13
#define SERVO_PIN 11

Servo myservo;       // create servo object to control a servo 
                     // a maximum of eight servo objects can be created 

int pos = 0;         // variable to store the servo position 
char led_state = 0;  // variable to store the state of the LED
char buffer[9];      // buffer for storing the inputs from the Serial
                     //   interface
char buffer_pos = 0; // current writing position for the buffer


void toggleLed()
{
  // If the LED is on
  if (led_state) 
  { 
    // then turn it off.
    digitalWrite(LED_PIN,LOW);
  }
  else
  {
    // otherwise, turn it on
    digitalWrite(LED_PIN,HIGH);
  }
  
  // toggle the LED state variable
  led_state=!led_state;
}

void setup() 
{ 
  // start serial port at 9600 bps:
  Serial.begin(9600);

  pinMode(LED_PIN, OUTPUT);  // enable the LED pin
  myservo.attach(SERVO_PIN); // attaches the servo pin to the servo object 

} 
 
 
void loop() 
{ 
  int read_val;  // variable to store the integer from the Serial.read() method
                 // this is the ASCII value of what is typed in at the Serial
                 // Monitor window
                 
  // if we've got something ready from the serial interface
  if ( Serial.available() > 0 )
  {
    // read in the data, up to the max size of the buffer.
    while( -1 != (read_val = Serial.read() ) && buffer_pos < 8)
    {
      // put the value in the buffer, and increment the position
      buffer[buffer_pos++] = read_val;
    }
    buffer[buffer_pos] = '\0';       // terminate the string.
    Serial.print("Read in value: "); // add some feedback to the Serial Monitor
    Serial.println(buffer);          //   so we know the command was received
    pos = atoi(buffer);              // turn the input data into an integer value
    if (pos == -1)                   // If we've received the "all done" command
    {
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // set the sleep mode
      sleep_enable();                // enable sleeping
      sleep_mode();                  // then activate the sleep mode
    }
    pos = (pos < 170 ? pos : 170);   // clamp the input values at reasonable 
    pos = (pos > 0 ? pos : 0);       //  values for your servo.  Mine is finicky ~ 180
    Serial.print("Moving to ");      // Tell Serial Monitor where we're actually going
    Serial.print(pos);
    myservo.write(pos);              // And go there
    toggleLed();
    delay(10000); 
    buffer_pos = 0;                  // And clean up after ourselves.
    memset(buffer,0,9);
  }
  else
  {
    
    Serial.println("Moving 0 to 180");
    for(pos = 0; pos < 180; pos += 5)  // goes from 0 degrees to 180 degrees 
    {                                  // in steps of 5 degrees
      myservo.write(pos);              // tell servo to go to position in variable 'pos' 
      delay(50);                       // waits 50ms for the servo to reach the position 
      toggleLed();
    } 
    Serial.println("Moving 180 to 0");
    for(pos = 180; pos>=0; pos-=5)     // goes from 180 degrees to 0 degrees 
    {                                
      myservo.write(pos);              // tell servo to go to position in variable 'pos' 
      delay(50);                       // waits 50ms for the servo to reach the position 
      toggleLed();
    } 
  }
} 

No comments:

Post a Comment