Arduino’s Servo Library: Angles, Microseconds, and “Optional” Command Parameters

Arduino Robotics
Arduino’s Servo Library: Angles, Microseconds, and “Optional” Command Parameters

Last Wednesdayย I spentย most of theย morning and part of the afternoon chasing a signalย anomaly that, as it turns out, did not exist. I wouldn’t call the experienceย a waste of time, but the experienceย occupied more of my time than I would have liked. I described the account over at ToolGuyd (An Oscilloscope, an Arduino Servo PWM Signal, and a Wild-Goose Chase) if you’re interested in the whole story.

The result – a better fundamental understanding of Arduino’s servo library, which I will try to share with you with this post. This might seem like beginner stuff, but untilย last week’s complications I have used Arduino’s servo library quite a few timesย with ease and assumedย proficiency.

Arduino ServoWrite

Arduino’s servo library makes it easier to control servos with minimal code and complications.ย Arduino’sย reference pageย for the ServoWrite command, which draws upon the servo library, offersย the following example code:

#include <Servo.h>

Servo myservo;

void setup() 
{ 
  myservo.attach(9);
  myservo.write(90);  // set servo to mid-point
} 

void loop() {}

This sample code instructs a servo, connected to pin 9, to move to its center (90ยฐ) position. For a continuous-rotation servo, this willย halt the servo’s motion.

In practice, attached servosย willย adjustย to their center positions, but perhaps not exactly.

90ยฐ vs. 1500 Microseconds

Most common servosย accept inputs from 1000 ยตs to 2000 ยตs, with 1500 ยตsย corresponding to the center position. For a 0-180ยฐ servo, this would be 90ยฐ.

Previously, I had used the servo library with the writeMicroseconds command, which defines the exact pulse width that you wantย to be sent to a servo. Last Wednesday,ย my troubles all began when Iย instead used theย write command with a parameter of 90ยฐ.

In theory,ย aย write command that instructsย aย servo to adjustย to 90ยฐย should send the same pulses as aย writeMicroseconds command that sends 1500 ยตs pulses. That is, write(90) and writeMicroseconds(1500) should both send 1500 ยตs pulses. But as it turns out, this assumption can lead to problems.

I uploaded the following code to my Uno, and analyzed the signals with an oscilloscope.

#include <Servo.h>
Servo servo1;
Servo servo2;
Servo servo3;
void setup() 
{ 
 servo1.attach(3);
 servo2.attach(4);
 servo3.attach(5, 1000, 2000);
 
 servo1.write(90); // set servo to mid-point (90ยฐ)
 servo2.writeMicroseconds(1500);
 servo3.write(90); // set servo to mid-point (90ยฐ)
} 
void loop() {}

Here is what the outputs of pins 3, 4, and 5 looked like, from top to bottom:

ToolGuyd Analysis of Arduino Servo Control Analog Widths

  • Pin 3, sent to 90ยฐ: 1.472 ms
  • Pin 4, sentย a 1500 ยตs pulse: 1.500 ms
  • Pin 5, sent to 90ยฐ: 1.500 ms

The pulse widths were measuredย using the oscilloscope’s built-inย software, but manual measurements wereย in agreement.

The difference between 1472 ยตs and 1500 ยตs is very small, and might not evenย be enough toย make a difference in servo position. Hereย is a screen captureย of the digital pulses, with measurements shown on the bottom right.

ToolGuyd Analysis of Arduino Servo Control Digital Signals

But, it’s still aย measurable difference:

ToolGuyd Analysis of Arduino Servo Control Digital Closeup

If you look at the signal forย servo3, which also directs the servo to move to its 90ยฐ position, you should see that the pulse width is 1500 ยตs, the same as forย servo2, for which the pulse is designated to beย 1500 ยตs.

The command, write(90), is the same for the first and third servo signals, so why does one send 1.472 ms pulses and the other 1.500 ms ones?

Arduino ServoAttach

The answerย lies with theย ServoAttach command.ย The Arduino’s reference pageย lists two forms of the command:

servo.attach(pin) 
servo.attach(pin, min, max)

The first version is theย minimal code a sketch needs toย designate an i/o pin for servo control. The second includes two very important butย optional parameters that designate the minimum and maximum pulse width ranges for the sketch.

In both the attach command’sย reference page and the servo library itself, it clearly states that the default min and max settings are 544 and 2400 ยตs, respectively.

“Optional” Pulse Width Limits

If, like me, you are used to using theย writeMicroseconds command instead ofย write, then youย might not have given much thought to the minimum and maximumย pulse width parameters. But if you use theย write command andย set servo positions with angles andย degrees, thenย considerย explicitly defining these parameters in your Arduino sketches.

Looking again at my example code, for the third servo signal I overrode the default pulse width limits with 1000 and 2000 ยตs.ย This is why the first and third servo signals sent different pulses using identical commands.

servo3.attach(5, 1000, 2000);

Besides the potential forย slightly off-center signals,ย a servo might not predictably interpret pulse widths above or below the limits it was designed for.ย Under- and over-limit pulses also have the potential to damage a servo.

If a 0-180ยฐ servo is designed to respond toย 1000-2000 ยตs pulses, it interpretsย 1000 ยตs as “0ยฐ” and 2000 ยตs as “180ยฐ.” But, with default pulse width limits range of 544-2400 ยตs, the Arduino will send a signal of ~1000 ยตs for an angle of 44ยฐ.ย Sweepingย from 1000 to 2000 ยตs would thenย only translate to a ~90ยฐ swing of the servo arm instead of a full 180ยฐ. This, and other potential issues can be avoided if microseconds are used instead of angles in degrees, or if the optional pulse width floor and ceiling parameters are defined in pin setup for each servo.

While I feel somewhat foolish about spending a few hours trying to decipher an unexpected 1.472 ms signal and sort out whether my new oscilloscope was defective,ย I have a feeling I’m not the only one who takes Arduino libraries for granted. Iย probably looked at the code a while back, but after using it with predictable results for a while, I simplyย forgot that it’s not a magical black box that makes servos simpler to control. A look at either the library or Arduino’s page for theย attach command would have save me some trouble, but I didn’t think to do either.

The next time your servos act unpredictably in a new project, double check that you set pulse width limits in the pin setup. It might just be that simple.

Stuart Deutsch writes more about tools and workshop topics over atย ToolGuyd.

When I am not testing and reviewing new tools, I am working on robotics, electronics, woodworking, and other types of projects.

I am also interested in microscopy, the physical sciences, and new technologies.

I write about tools and workshop topics at ToolGuyd.com.

View more articles by Stuart Deutsch
Discuss this article with the rest of the community on our Discord server!

ADVERTISEMENT

FEEDBACK