Zipwhip Blog

Free Trial

Posts by Topic

see all

Manual Control of a Servo on the Arduino for the Zipwhip TextSpresso Machine


Control a servo without using the Arduino servo library.

The Arduino has a great servo library, but we found while making our TextSpresso machine that the servo library wouldn't play nice with our stepper motors. We happily connected our stepper motors up to pins 2 through 9 because we were using a stepper motor shield. Then we connected the servos to much higher pin numbers like 26 and 27. To our dismay whenever we sent commands to the steppers, it would cause massive gyrations on the servos.

This had us investigate what was going on and to do that we had to dig under the covers of the servo library. Not many newbies know about the timer capabilities of the Arduino. The servo library hides us from the gory details. Well, it turns out that the servo library relies on a timer callback to get the precision control of the servos. This callback means the Arduino can't be processing any other code at the exact moment the callback occurs. If the Arduino is busy, then all hell breaks lose on the exact timing of pulses being sent to the servo.

Servos expect a 20 millisecond (ms) pulse. To set the servo at 0 degrees, you send it a high voltage for the 1st ms of the 20 ms pulse. To set the servo at 180 degrees, you send it a high voltage for the 1st 2 ms of the 20 ms pulse. Pretty easy actually. Anywhere in between 1.0 ms and and 2.0 ms gives you different degrees between 0 and 180.

We wrote the sample code below to help you manually control your servo. It just takes a millisecond value and sends it to the servo and then pauses for the remainder of the pulse. I'm actually not sure why the Arduino IDE doesn't give a manual example like this because it's so easy to do. We couldn't find any sample code on the Internet either for "manual servo control on the Arduino". We hope you find this helpful.

<span style="color:#008000;">// Manual servo control </span>
<span style="color:#008000;">// Copyright 2012 by Zipwhip. </span>
<span style="color:#008000;">// You are free to use and modify this code in your own software.</span>

#define SERVO_PIN         39  <span style="color:#008000;">// Any pin on the Arduino or Mega will work.</span>

void setup()
{
  pinMode(SERVO_PIN, OUTPUT);

}

int lenMicroSecondsOfPeriod = 20 * 1000; <span style="color:#008000;">// 20 milliseconds (ms)</span>
int lenMicroSecondsOfPulse = 1.8 * 1000; <span style="color:#008000;">// 1.0 ms is 0 degrees</span>

void loop()
{

 <span style="color:#008000;">// Servos work by sending a 20 ms pulse.  </span>
 <span style="color:#008000;">// 1.0 ms at the start of the pulse will turn the servo to the 0 degree position</span>
 <span style="color:#008000;">// 1.5 ms at the start of the pulse will turn the servo to the 90 degree position </span>
 <span style="color:#008000;">// 2.0 ms at the start of the pulse will turn the servo to the 180 degree position </span>
 <span style="color:#008000;">// Turn voltage high to start the period and pulse</span>
 digitalWrite(SERVO_PIN, HIGH);

 <span style="color:#008000;">// Delay for the length of the pulse</span>
 delayMicroseconds(lenMicroSecondsOfPulse);

 <span style="color:#008000;">// Turn the voltage low for the remainder of the pulse</span>
 digitalWrite(SERVO_PIN, LOW);

 <span style="color:#008000;">// Delay this loop for the remainder of the period so we don't</span>
 <span style="color:#008000;">// send the next signal too soon or too late</span>
 delayMicroseconds(lenMicroSecondsOfPeriod - lenMicroSecondsOfPulse); 

}

Manually Sweeping the Servo from 0 to 180 Degrees

Taking the example above further and trying to mimic the Arduino sample sweep code for a servo we produced the test code below. This let us fully test our manual control of the servo. We found that for our HiTec HS-422 servo that the 0 degree position was at about a 0.5 ms pulse and that the full 180 degrees was around a 2.2 ms pulse. You may find something different for your servo but you can adjust the control variables in the code below until you're happy.

We also found that a pulse length of about 25 ms worked better for our servo. The standard is 20 ms so I'm not surprised we found something a bit longer than the expected pulse worked better. When we went shorter on the pulse like 18 ms we found the servo acted really weird. That's probably because the voltage was getting applied prior to the servo finishing it's measurements and that would throw off pulse lengths overall.

// Manually Sweeping the Servo from 0 to 180 Degrees
// Copyright 2012 by Zipwhip.
// You are free to use and modify this code in your own software.

#define SERVO_PIN         39  // 26 or 39. Any pin on the Arduino or Mega will work.

int lenMicroSecondsOfPeriod = 25 * 1000;       // 25 milliseconds (ms). found much better smoothness at 25, not 20 ms.
int lenMicroSecondsOfPulse = 0;                // used in the while loop below
int lenMicroSecondsOfPulseStart = 0.5 * 1000;  // 0 degrees
int lenMicroSecondsOfPulseEnd = 2.2 * 1000;    // 180 degrees
int lenMicroSecondsOfPulseStep = 0.01 * 1000;   // .1 millisecond. That's 200 increments b/w 1.0 and 2.0

void setup()
{
  pinMode(SERVO_PIN, OUTPUT);

  // Setup our start point for our main loop
  lenMicroSecondsOfPulse = lenMicroSecondsOfPulseStart + lenMicroSecondsOfPulseStep;

}

void loop()
{

 // Servos work by sending a 20 ms pulse.
 // 1.0 ms at the start of the pulse will turn the servo to the 0 degree position
 // 1.5 ms at the start of the pulse will turn the servo to the 90 degree position
 // 2.0 ms at the start of the pulse will turn the servo to the 180 degree position

 // Do a while loop starting at our start pulse and incrementing each time thru the loop
 // Stop when we reach our final end point
 while (lenMicroSecondsOfPulse = lenMicroSecondsOfPulseStart)
 {
   // Turn voltage high to start the period and pulse
   digitalWrite(SERVO_PIN, HIGH);

   // Delay for the length of the pulse
   delayMicroseconds(lenMicroSecondsOfPulse);

   // Turn the voltage low for the remainder of the pulse
   digitalWrite(SERVO_PIN, LOW);

   // Delay this loop for the remainder of the period so we don't
   // send the next signal too soon or too late
   delayMicroseconds(lenMicroSecondsOfPeriod - lenMicroSecondsOfPulse); 

   // Increment our pulse
   lenMicroSecondsOfPulse += lenMicroSecondsOfPulseStep;

 }

 // Now reverse the step so we go in the opposite direction
 lenMicroSecondsOfPulseStep = lenMicroSecondsOfPulseStep * -1;
 lenMicroSecondsOfPulse += lenMicroSecondsOfPulseStep;

 // delay for a few seconds and do it all again
 delay(2 * 1000);

}

2 comments

Comment