Learn

Control a servo motor with an Arduino and a potentiometer

  • 192parts on the bench
  • 25boards running now
  • 1.00xreal time, on every board
Arduino Uno: servo faderlive0.000 s 0.00x
Click to open it in the editor
The servo example: a slide potentiometer on A0 sets the angle of a servo on pin 9. Open it in the editor to move the slider.

A hobby servo is the easiest way to make an Arduino move something to an exact angle: three wires, one signal pin, and a number from 0 to 180. The circuit above is an SG90-style servo on pin 9 of an Arduino Uno, with a slide potentiometer on A0 setting where the horn points. It is running in your browser on a simulated ATmega328P.

Open it in the editor and drag the slider while it runs: the horn walks to the new angle at the speed a real SG90 does, and the serial monitor prints the reading and the angle each time it changes.

What you need

  • An Arduino Uno
  • An SG90 (or similar) hobby servo
  • A 10 k potentiometer (a slide pot here; a rotary one is wired the same way)
  • A breadboard and jumper wires

Wiring

Part and pin
Goes to
Note
Servo brown wire (GND)
GND
Servo red wire (VCC)
5 V
Below 4 V the servo does not move at all
Servo orange wire (signal)
Pin 9
Any digital pin works
Potentiometer end 1
5 V
Potentiometer end 3
GND
Potentiometer wiper
A0
Reads 0 to 1023 across the travel

What a servo actually listens to

The servo’s signal wire does not take PWM in the sense an LED does. The duty cycle means nothing to it. What it measures is the width of each pulse: about 1 millisecond puts the horn at one end, 2 milliseconds at the other, 1.5 milliseconds in the middle, repeated about fifty times a second. The servo holds whatever it was last told and walks there at its own speed, roughly a tenth of a second for sixty degrees.

That is also why you cannot drive a servo with analogWrite() on an Uno. The Uno’s PWM runs at about 490 or 980 hertz, far too fast for a servo to read as a pulse width. The Servo library makes the pulse itself.

The code, explained

The sketch reads the potentiometer four times and averages the readings, so the horn does not twitch on the last, noisy bit of the converter. map() turns 0 to 1023 into 0 to 180 degrees, write() sends the matching pulse, and the loop waits 20 milliseconds, which is the servo’s own frame period. The angle is printed only when it changes, so the serial monitor reads as a sentence rather than a waterfall.

The loop, from the servo example (trimmed)
void loop() {
  long sum = 0;
  for (int i = 0; i < 4; i++) {
    sum += analogRead(FADER);
  }
  int reading = (int)(sum / 4);
  int angle = (int)map(reading, 0, 1023, 0, 180);

  horn.write(angle);
  delay(20);
}

The same thing with the Servo library

The built-in example uses Mokxi’s own servo header, but the Arduino Servo library’s calls compile as they stand, on every board that compiles in the browser. The classic sweep sketch below runs unchanged. Two small differences are worth knowing: attach(pin) with no widths uses 1000 to 2000 microseconds, the SG90’s window, where the upstream library uses 544 to 2400; and each write() sends one pulse rather than starting a timer, so a sketch that needs the servo held under load calls refresh() from loop().

For an ESP32 tutorial, ESP32Servo.h is the same class under the name those tutorials use.

The Servo library sweep, as it compiles here
#include <Servo.h>

Servo myservo;

void setup() {
  myservo.attach(9);
}

void loop() {
  for (int pos = 0; pos <= 180; pos++) {
    myservo.write(pos);
    delay(15);
  }
}

Try it in the editor

Open the circuit in the editor and change the map() call to map(reading, 0, 1023, 45, 135). The slider now covers only the middle ninety degrees, which is how you give a knob a finer feel over a smaller range.

Then select the servo and look at its properties: min_pulse, max_pulse, range and speed. Set the speed to 60 degrees a second and move the slider quickly; the horn now crawls after it, the way a large, slow servo would. Setting min_pulse to 544 and max_pulse to 2400 makes it match the upstream Servo library’s default window.

To drive a second servo, drag another one onto the canvas, wire its signal to pin 10, and add a second Servo object in the sketch.

Common mistakes

Powering the servo from the board and expecting it to be fine. On a real bench an SG90 can pull a quarter of an amp when it stalls, enough to brown out an Uno running from USB, and it is the most common reason a servo project resets itself. The simulator does not model supply current, so a circuit that would sag on your desk runs cleanly here. If you build it for real, give the servo its own 5 volt supply and join the grounds.

Forgetting the common ground. The signal is measured against the servo’s own ground, so if the two supplies share nothing the pulse means nothing.

Asking for angles the servo cannot reach. A pulse outside the servo’s window is ignored rather than obeyed, just as a real SG90 strains against its end stop. If the horn stops short of 0 or 180, check the pulse widths the library is sending.

Updating the servo in a tight loop with no delay. Sending pulses faster than every 20 milliseconds gains nothing, because the servo only updates at its own frame rate.

Questions

Which Arduino pins can drive a servo?

Any digital pin. The library makes the pulse in software, so the servo does not need one of the PWM pins marked with a tilde.

Can I drive two servos at once?

Yes. Create two Servo objects on two pins and write to each. Give real servos their own supply once there is more than one, because the current adds up.

Why does my servo jitter?

Usually because the reading it follows is noisy. Averaging a few analogRead() samples, as this sketch does, or ignoring changes of a degree or two, steadies it. On real hardware a sagging supply causes jitter too.

What is the difference between a servo and a stepper motor?

A servo goes to an angle and holds it, with its own feedback inside, usually over 180 degrees. A stepper turns by counted steps, as far as you like, and has no idea where it is unless you count. The stepper tutorial covers the 28BYJ-48.

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.