Lesson 3: Servo Motors

Table of Contents

  1. Materials
  2. Servo motors
    1. What is a servo motor?
    2. Standard vs. continuous rotation servos
    3. The SG90 micro servo
  3. Servo PWM vs. analogWrite() PWM
  4. The Arduino Servo library
    1. Power considerations
  5. Let’s make stuff!
    1. Activity 1: Servo sweep
    2. Activity 2: Potentiometer-controlled servo
    3. Activity 3: Sensor-driven servo gauge
  6. Exercises
  7. Lesson Summary
  8. Resources
  9. Next Lesson

This lesson is in draft form. The circuit diagrams and videos have not yet been inserted; however, the content is complete and can be followed.

In the previous two lessons, we controlled pixels—on an OLED screen and on an LED strip. Now let’s make things move! Servo motors let you precisely control angular position, making them essential components in robotics, RC vehicles, pan/tilt camera mounts, automated door locks, and countless other projects. Best of all, like the OLED and addressable LEDs, servos have a built-in control circuit—so the wiring is simple and a library handles the tricky signal timing.

In this lesson, you will learn:

  • What servo motors are and how they differ from regular DC motors
  • How the internal feedback loop (motor + gears + potentiometer + control circuit) enables precise position control
  • The difference between servo PWM signals and analogWrite() PWM—and why they’re not the same thing
  • How to use the Arduino Servo library to control servo position
  • How to wire a servo to Arduino (no transistor needed!)
  • Power considerations for servos and when to use an external supply
  • How to create interactive servo projects driven by sensor input

Materials

You will need the following materials for this lesson:

Arduino Servo Motor Breadboard Potentiometer
Arduino Uno or Leonardo SG90 micro servo motor Breadboard 10K potentiometer
Arduino Uno, Leonardo, or similar SG90 or SG92R micro servo (9g) Breadboard 10KΩ Potentiometer

You will also need jumper wires and, for Activity 3, a couple of tactile buttons.

Our kits include a 9g micro servo—either the TowerPro SG92R (available from Adafruit) or the Miuzei SG90 (available from Amazon). Both are SG90-class servos with nearly identical specs and wiring. Any 9g micro servo with a standard 3-pin connector will work for this lesson.

Servo motors

What is a servo motor?

A servo motor is a motor that can rotate to a specific angle and hold that position. Unlike a regular DC motor (which just spins continuously when you apply voltage), a servo knows where its output shaft is pointing and actively works to maintain that position—even if you try to push it away.

How does it do this? A servo packs four components into one compact package:

  1. A small DC motor that provides rotational force.
  2. A gear reduction system that slows the motor down while increasing torque (turning force).
  3. A potentiometer mechanically coupled to the output shaft that measures the shaft’s current angle.
  4. A control circuit that continuously compares the desired position (from your Arduino’s signal) with the actual position (from the potentiometer) and drives the motor until they match.

This is called a closed-loop feedback system—the control circuit constantly monitors and corrects the position. You tell the servo “go to 90°” and it figures out how to get there and stay there.

Built-in smarts! Notice the pattern across this module: the OLED has a built-in SSD1306 display controller, addressable LEDs have built-in WS2812B driver chips, and servos have a built-in feedback control circuit. Each device handles the complex low-level work internally, and you communicate with it through a simple signal. In the next lesson, we’ll encounter a component that doesn’t have built-in intelligence—and you’ll appreciate the difference!

Standard vs. continuous rotation servos

There are two types of hobby servos:

  • Standard (positional) servos rotate to a specific angle, typically between 0° and 180°, and hold that position. The angle is determined by the signal you send. This is what we’ll use in this lesson.
  • Continuous rotation servos spin freely like a regular motor, but you control their speed and direction rather than their position. The same PWM signal that means “go to 90°” on a standard servo means “stop” on a continuous rotation servo, while “0°” means “full speed clockwise” and “180°” means “full speed counter-clockwise.”

For this lesson, we’ll focus on standard (positional) servos, specifically the popular SG90 micro servo.

The SG90 micro servo

The SG90 is a class of tiny, inexpensive servo motors that weigh only 9 grams. Despite their small size, they’re surprisingly capable for learning projects. You’ll find SG90-class servos from many manufacturers (TowerPro, Miuzei, and others)—they all share the same basic form factor, connector, and specs:

Attribute Rating
Weight 9g
Operating voltage 4.8V - 6.0V
Stall torque 1.8 kg·cm (4.8V)
Operating speed 0.09 sec/60° (4.8V)
Rotation range ~180°
Idle current ~6mA
Running current (no load) ~200-500mA
Stall current ~500-700mA

The SG90 has a standard 3-pin connector with color-coded wires: orange/yellow (signal), red (power), and brown/black (ground).

Servo PWM vs. analogWrite() PWM

Before we start wiring, let’s clear up a common source of confusion. You’ve already used analogWrite() to control LED brightness. Servos are also controlled by a “PWM” signal. But these are fundamentally different kinds of PWM, and mixing them up is a common mistake.

  analogWrite() PWM Servo PWM
What varies The duty cycle (fraction of time HIGH) The pulse width (duration of the HIGH pulse)
Frequency Fixed at 490 Hz or 980 Hz (depending on pin) Fixed at 50 Hz (one pulse every 20ms)
What it controls Power delivery (brightness, motor speed) Position (angle)
Signal meaning 0% duty cycle = off, 100% = full power ~1ms pulse = 0°, ~1.5ms = 90°, ~2ms = 180°
Arduino function analogWrite(pin, 0-255) servo.write(0-180) via the Servo library

With analogWrite(), a 50% duty cycle delivers 50% of the available power—useful for dimming LEDs or slowing motors. With servo PWM, the duty cycle doesn’t matter for power delivery. Instead, the servo’s control circuit measures the width of each pulse to determine the target angle.

Do not use analogWrite() to control servos! The analogWrite() function produces PWM at 490 Hz or 980 Hz—much too fast for servos, which expect 50 Hz. Sending the wrong signal can cause erratic behavior or damage. Always use the Arduino Servo library, which generates the correct 50 Hz signal for you.

The Arduino Servo library

The Arduino Servo library ships with the Arduino IDE—no installation needed. It handles all the precise signal timing so you can simply tell the servo which angle you want.

Wiring diagram showing an SG90 servo connected to an Arduino Uno with three wires: orange signal wire to Pin 3, red power wire to 5V, and brown ground wire to GND Figure. Wiring the SG90 servo requires just three connections. No transistor or external components needed—the servo has its own built-in driver circuit.

No transistor needed! In the vibromotor lesson, you’ll learn that raw DC motors need a transistor because they draw more current than a GPIO pin can supply. Servos are different—the signal wire carries only a control signal (a few milliamps), not the motor’s power. The servo’s internal driver circuit handles the heavy lifting. The motor power comes directly from the 5V pin, not through the Arduino’s GPIO.

Power considerations

A single SG90 servo under light load can usually be powered from the Arduino’s 5V pin (via USB). However, servos can draw significant current, especially during rapid movements or when holding position against a load:

State Typical current
Idle (holding position, no load) ~6mA
Moving (no load) ~200-500mA
Stall (load exceeds torque) ~500-700mA

The Arduino’s USB power supply provides about 500mA total. A single SG90 moving under light load is usually fine, but if your servo jitters, stalls, or your Arduino resets, you may be hitting the current limit.

If your Arduino resets when the servo moves, the servo is drawing too much current from USB. Power the servo from an external 5V supply (like a USB phone charger rated 1A+). Connect the supply’s 5V directly to the servo’s red wire and the supply’s GND to both the servo’s brown wire and the Arduino’s GND (shared ground). Do not connect the external 5V to the Arduino’s 5V pin.

For projects with multiple servos, you will almost certainly need an external power supply. Two or more servos moving simultaneously can easily exceed 1A. For larger builds, consider a dedicated servo driver board like the PCA9685, which provides its own power bus and can control up to 16 servos via I2C.

Let’s make stuff!

Now that we understand how servos work and have one wired up, let’s build some projects! As with the previous lessons, we’ll start simple and progressively add interactivity.

Activity 1: Servo sweep

Just as we started with blinking an LED and lighting up NeoPixels, let’s start with the simplest possible servo program: sweeping back and forth between 0° and 180°. This confirms your wiring is correct and that the library is communicating with the servo.

#include <Servo.h>

const int SERVO_PIN = 3;
Servo myServo;

void setup() {
  myServo.attach(SERVO_PIN);
}

void loop() {
  // Sweep from 0° to 180°
  for (int angle = 0; angle <= 180; angle++) {
    myServo.write(angle);
    delay(15);  // Wait for the servo to reach the position
  }

  // Sweep back from 180° to 0°
  for (int angle = 180; angle >= 0; angle--) {
    myServo.write(angle);
    delay(15);
  }
}

The delay(15) gives the servo time to reach each position before advancing to the next degree. Try changing the delay—a shorter delay means faster sweeping, but if it’s too short, the servo can’t keep up and will jitter. What happens if you change the range to for (int angle = 30; angle <= 150; ...)?

Avoid driving to the mechanical limits. If your servo makes a grinding or buzzing sound at 0° or 180°, it’s hitting its mechanical stops and stalling. This draws high current and can strip the plastic gears over time. Try reducing your range to 10-170° or experiment to find your servo’s actual safe limits.

Activity 2: Potentiometer-controlled servo

Now let’s add a potentiometer to directly control the servo’s position—turn the knob, and the servo follows. This is the same analogRead()map() → output pattern from the OLED ball-size demo and the NeoPixel color wheel. You’ll see this pattern again in the vibromotor lesson too—it’s one of the most fundamental patterns in physical computing!

The circuit

Use the same servo wiring as before, and add a 10KΩ potentiometer with its wiper connected to A0.

The code

#include <Servo.h>

const int SERVO_PIN = 3;
const int POT_PIN = A0;

Servo myServo;

void setup() {
  myServo.attach(SERVO_PIN);
  Serial.begin(9600);
}

void loop() {
  // Read the potentiometer (0-1023)
  int potVal = analogRead(POT_PIN);

  // Map to servo angle (0-180)
  int angle = map(potVal, 0, 1023, 0, 180);

  // Move the servo
  myServo.write(angle);

  // Debug output
  Serial.print("Pot: ");
  Serial.print(potVal);
  Serial.print(" -> Angle: ");
  Serial.println(angle);

  delay(15);
}

This is essentially the Arduino’s built-in “Knob” example, which you can also find in the Arduino IDE under File → Examples → Servo → Knob. Turn the potentiometer and watch the servo track your input in real time. Try replacing the potentiometer with a force-sensitive resistor or a photoresistor—squeeze to point, or let light control the angle!

Activity 3: Sensor-driven servo gauge

For our final activity, let’s build a physical gauge—a servo-powered pointer that displays sensor data in the real world, like an analog speedometer or a VU meter needle. This is the physical output equivalent of the OLED analog graph and the NeoPixel level meter. Where the OLED drew data on screen and the NeoPixels lit up LEDs proportionally, here we’ll sweep a physical pointer across a scale.

We’ll read an analog sensor on A0 and map it to the servo’s range. To make it more interesting, we’ll add two buttons: one to “freeze” the gauge at its current reading (like a max-hold feature on a multimeter), and one to reset it.

The circuit

Use the same servo + potentiometer wiring, and add two tactile buttons on Pins 8 and 9 using INPUT_PULLUP.

The code

#include <Servo.h>

const int SERVO_PIN = 3;
const int SENSOR_PIN = A0;
const int FREEZE_BTN = 8;
const int RESET_BTN = 9;

Servo gaugeServo;
bool isFrozen = false;
int frozenAngle = 0;

void setup() {
  gaugeServo.attach(SERVO_PIN);
  pinMode(FREEZE_BTN, INPUT_PULLUP);
  pinMode(RESET_BTN, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  // Check freeze button (INPUT_PULLUP reads LOW when pressed)
  if (digitalRead(FREEZE_BTN) == LOW) {
    isFrozen = !isFrozen;  // Toggle freeze state
    if (isFrozen) {
      frozenAngle = gaugeServo.read();
      Serial.print("Frozen at: ");
      Serial.println(frozenAngle);
    } else {
      Serial.println("Unfrozen");
    }
    delay(300);  // Simple debounce
  }

  // Check reset button
  if (digitalRead(RESET_BTN) == LOW) {
    isFrozen = false;
    gaugeServo.write(0);
    Serial.println("Reset to 0");
    delay(300);
  }

  // Update gauge if not frozen
  if (!isFrozen) {
    int sensorVal = analogRead(SENSOR_PIN);
    int angle = map(sensorVal, 0, 1023, 0, 180);
    gaugeServo.write(angle);

    Serial.print("Sensor: ");
    Serial.print(sensorVal);
    Serial.print(" -> Angle: ");
    Serial.println(angle);
  }

  delay(15);
}

Try attaching a small pointer (a piece of cardboard, a toothpick, or a 3D-printed needle) to the servo horn to create a visual gauge. You could print or draw a scale on paper behind it to complete the analog meter look. This is a great example of physical data visualization—the same sensor data that we graphed on the OLED screen is now embodied as physical motion.

Connecting it all together: At this point, you could combine the servo gauge with an OLED display to show the numeric reading on screen while the servo shows it physically, or with NeoPixels to add color coding (green for low, red for high). Multimodal output—visual, spatial, and haptic—is a powerful tool in physical computing and HCI!

Exercises

Want to go further? Here are some challenges to reinforce what you’ve learned:

  • Servo-powered door lock. Use a button to toggle the servo between “locked” (0°) and “unlocked” (90°) positions. Add an LED that turns green when unlocked and red when locked. You could even attach the servo to a small latch mechanism.
  • Light tracker. Mount two photoresistors on either side of a servo. Read both sensors and turn the servo toward whichever side detects more light—a simple solar tracker!
  • Servo + OLED dashboard. Display the current servo angle, sensor reading, and servo state (frozen/unfrozen) on the OLED display while the gauge moves. This combines two lessons into a multimodal output project.
  • Animatronic face. If you have two servos, mount them to control the horizontal and vertical movement of a pair of eyes (drawn or 3D-printed). Use two potentiometers—or an accelerometer—as input for a fun animatronic project.

Lesson Summary

In this lesson, you learned how to control servo motors for precise angular positioning. The key concepts were:

  • Servo motors contain a DC motor, gears, a position-sensing potentiometer, and a control circuit in one package. This closed-loop feedback system lets you command a specific angle and the servo figures out how to get there.
  • Standard servos rotate to a specific angle (typically 0-180°) and hold position. Continuous rotation servos control speed and direction instead.
  • Servo PWM is fundamentally different from analogWrite() PWM. Servos expect a 50 Hz signal where the pulse width (1-2ms) encodes the target angle. analogWrite() produces 490-980 Hz PWM with varying duty cycles for power control. Never use analogWrite() to drive a servo.
  • The Arduino Servo library generates the correct 50 Hz signal for you. Like the NeoPixel library, it works on any digital pin—no hardware PWM required.
  • Unlike raw DC motors (covered in the next lesson), no transistor is needed because the signal wire carries only a low-current control signal. The servo’s internal circuit handles motor power.
  • The Servo library uses Timer1, which disables analogWrite() on Pins 9 and 10 (Uno) or Pins 9, 10, and 11 (Leonardo). Plan your pin assignments accordingly when combining servos with other PWM outputs.
  • For multiple servos or heavy loads, use an external 5V power supply with a shared ground connection to the Arduino—the same principle we’ll encounter again in the vibromotor lesson.

Resources

Next Lesson

In the next lesson, we will shift from components with built-in intelligence to a raw DC motor—the vibromotor. You’ll learn why transistors, flyback diodes, and resistor calculations become necessary when a component doesn’t have its own driver circuit.

Previous: Addressable LEDs Next: Vibromotors


This website was developed by Professor Jon E. Froehlich and the Makeability Lab using Just the Docs. If you found the website useful or use it in your teaching, we'd love to hear from you: jonf@cs.uw.edu. This website and all code is open source (website GitHub, Arduino GitHub, p5js GitHub). You can find the MakeabilityLab_Arduino_Library here. Found an error? File a GitHub Issue.

Made with ♡ by the The Makeability Lab logo which is a large geometric M with an embedded L