A servo (or servo motor) is a closed-loop rotary or linear actuator that uses positional feedback to precisely control angular position, velocity, and acceleration. Unlike open-loop motors that blindly step or spin when voltage is applied, a servo continuously monitors its output shaft via an internal potentiometer or optical encoder. It compares this actual position to the commanded position and adjusts its internal H-bridge drive to eliminate any error. If you are building a robotic arm, a camera gimbal, or an automated valve controller, understanding what is a servo and how it differs from other motor types is the critical first step before writing a single line of code.
The Core Mechanics and Control Loop
Inside a standard hobby servo (like the ubiquitous SG90 or MG996R), you will find three main components: a brushed DC motor, a gear train, and a feedback potentiometer. The control circuitry reads the voltage from the potentiometer wiper, which changes as the output shaft rotates. An internal error amplifier compares this voltage to the incoming PWM (Pulse Width Modulation) signal from your microcontroller. If the shaft is not at the target angle, the amplifier drives the DC motor in the correct direction until the error reaches zero.
Industrial AC servos (like those from Yaskawa or Delta) replace the brushed DC motor and potentiometer with a 3-phase brushless motor and high-resolution optical encoders, but the fundamental closed-loop principle remains identical. The drive demands a target position and uses complex PID (Proportional-Integral-Derivative) tuning to move the load smoothly without overshooting.
Motor Type Comparison: Servo vs. Stepper vs. Brushless DC
A common mistake in embedded design is treating steppers and servos as interchangeable. They are not. Steppers excel at holding torque at zero speed but lose torque rapidly as speed increases. Servos maintain high torque across their entire speed range but require more complex control. Below is a direct comparison to help you select the right actuator for your load profile.
| Motor Type | Torque Curve Profile | Control Needs | Relative Cost | Best Load Profile |
|---|---|---|---|---|
| RC/Hobby Servo | High stall torque, drops at high speed | 50Hz PWM signal (1-2ms pulse) | $5 - $50 | Low-speed, high-torque angular positioning (robot arms, pan/tilt) |
| Industrial AC Servo | Flat, high torque from 0 to rated RPM | Dedicated servo drive (EtherCAT, analog, or pulse/direction) | $300 - $2000+ | High-speed, high-precision CNC, industrial automation, dynamic conveyors |
| Stepper Motor | Maximum at stall, drops sharply with speed | Step/Direction pulses via driver (e.g., A4988, TMC2209) | $15 - $80 | Low-speed positioning, 3D printers, holding a load statically |
| Brushless DC (BLDC) | High speed, low holding torque without active commutation | 3-phase ESC with Hall sensors or sensorless back-EMF | $20 - $150 | Continuous high-speed rotation (drones, cooling fans, RC vehicles) |
Sizing Rule of Thumb and Worked Load Example
Never size a motor based purely on the static weight of your load. You must account for dynamic forces, acceleration, and mechanical inefficiencies. The golden rule of thumb for servo sizing is to calculate the maximum required holding torque at the joint, then multiply by a safety factor of 2.0 to 2.5.
Worked Load Example: Robotic Arm Joint
Imagine you are designing the shoulder joint for a small robotic arm. The arm segment is 20 cm (0.2 m) long, and it needs to lift a payload of 500 g (0.5 kg) at maximum extension. The arm itself weighs 200 g (0.2 kg), with its center of mass at 10 cm (0.1 m).
- Calculate Payload Torque: Force = mass × gravity = 0.5 kg × 9.81 m/s² = 4.9 N. Torque = 4.9 N × 0.2 m = 0.98 Nm.
- Calculate Arm Torque: Force = 0.2 kg × 9.81 m/s² = 1.96 N. Torque = 1.96 N × 0.1 m = 0.196 Nm.
- Total Static Torque: 0.98 + 0.196 = 1.176 Nm.
- Convert to kg-cm: 1.176 Nm / 0.09806 ≈ 12 kg-cm.
- Apply Safety Factor (2.5x): 12 kg-cm × 2.5 = 30 kg-cm.
Selection: You need a servo rated for at least 30 kg-cm at your operating voltage. A standard MG996R (13 kg-cm) will immediately stall and overheat. You should select a high-torque digital servo like the DS3218 (25kg-cm) if you can reduce the arm length slightly, or step up to a LewanSoul LX-16A bus servo (17kg-cm) and use a counterweight, or move to a NEMA 17 stepper with a 50:1 planetary gearbox for this specific joint.
Wiring, Terminals, and ESP32 Integration
Hobby servos use a standardized 3-pin JR or Futaba connector. Industrial servos use high-density D-sub or aviation connectors. For embedded prototyping, you will almost exclusively deal with the 3-pin hobby standard.
| Wire Color (Standard) | Function | ESP32 Connection | Notes & Warnings |
|---|---|---|---|
| Brown or Black | GND (Ground) | ESP32 GND | Must share a common ground with the servo power supply and ESP32. |
| Red | VCC (Power) | External 5V-6V Supply | Never power large servos from the ESP32 5V/VIN pin. Use a dedicated buck converter. |
| Orange, Yellow, or White | Signal (PWM) | Any GPIO (e.g., GPIO 13) | ESP32 outputs 3.3V logic. Most modern servos accept 3.3V, but use a logic level shifter for 5V-only industrial servos. |
What Driver or Controller Does a Servo Demand?
Hobby servos demand a 50Hz PWM signal where the pulse width (typically 1000µs to 2000µs) dictates the angle. The ESP32 handles this natively using its LEDC (LED Control) or MCPWM peripherals via the ESP32Servo library. You do not need an external motor driver board for hobby servos; the microcontroller drives the signal wire directly, while the external power supply drives the motor.
Industrial AC servos, however, demand a dedicated servo drive (amplifier). The ESP32 cannot drive an industrial servo directly. You would need to output high-speed step/direction pulses to the drive's opto-isolated inputs, or communicate via an industrial bus like CANopen or RS-485 Modbus.
Failure Signatures: Hum, Overheat, and Stall
When debugging servo circuits, listen and feel for these specific failure modes:
- Humming / Jittering: The servo vibrates in place without moving to the target. This is almost always caused by a noisy PWM signal, a ground loop, or insufficient current from the power supply causing voltage ripple. Fix: Add a 470µF electrolytic capacitor across the servo VCC and GND, and ensure thick ground wires.
- Overheating: The casing is too hot to touch. This happens when the servo is commanded to a position it cannot physically reach (binding linkage), causing it to draw continuous stall current. Fix: Check mechanical limits and implement a software timeout to detach the servo after reaching the target.
- Stalling / Brownout: The servo stops moving, and the ESP32 randomly reboots. This occurs when a large servo draws peak stall current (often 2A to 5A), dragging the shared power rail voltage down below the ESP32's brownout threshold (usually ~2.4V on the 3.3V rail). Fix: Use completely separate power supplies for the logic and the motors, tied only at a single star-ground point.
ESP32 Servo Control Code
Below is a complete, copy-pasteable ESP32 sketch using the ESP32Servo library. It includes pin definitions and a detach sequence to prevent idle jitter and overheating.
#include <ESP32Servo.h>
// Pin Definitions
const int SERVO_PIN = 13;
const int MIN_US = 500; // Minimum pulse width in microseconds
const int MAX_US = 2500; // Maximum pulse width in microseconds
Servo myServo;
void setup() {
Serial.begin(115200);
// Allow allocation of all timers
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
// Attach servo with specific pulse width limits
myServo.setPeriodHertz(50); // Standard 50Hz servo
myServo.attach(SERVO_PIN, MIN_US, MAX_US);
Serial.println("Servo attached. Starting sweep...");
}
void loop() {
// Sweep from 0 to 180 degrees
for (int pos = 0; pos <= 180; pos += 1) {
myServo.write(pos);
delay(15); // Wait 15ms between steps for smooth motion
}
delay(1000); // Hold at 180 for 1 second
// Sweep back to 0
for (int pos = 180; pos >= 0; pos -= 1) {
myServo.write(pos);
delay(15);
}
// CRITICAL: Detach servo when idle to prevent jitter and overheat
myServo.detach();
Serial.println("Servo detached to save power and prevent heat.");
delay(5000); // Wait 5 seconds before re-attaching for next cycle
myServo.attach(SERVO_PIN, MIN_US, MAX_US);
}
Reference: For deeper understanding of the ESP32's underlying PWM hardware used by this library, consult the official Espressif LEDC API documentation.
Frequently Asked Questions
What is a servo vs a stepper motor?
A servo is a closed-loop system that uses feedback (an encoder or potentiometer) to verify its position and correct errors in real-time. A stepper motor is an open-loop system that moves in discrete physical steps based on magnetic coils; it assumes it has reached the target position without verifying it. If a stepper encounters a load higher than its holding torque, it will silently miss steps and lose positional accuracy. A servo will detect the error, increase current to fight the load, or trigger a fault alarm if it cannot reach the target. Use steppers for low-cost, low-speed precision (like 3D printers); use servos for dynamic, high-speed, or high-torque applications where missing a step is catastrophic.
What is a continuous rotation servo used for?
A continuous rotation servo is a modified standard servo where the internal potentiometer has been disconnected or removed, and the mechanical hard stops on the gear train have been filed away. Instead of moving to a specific angle based on PWM pulse width, the pulse width dictates the speed and direction of rotation. A 1500µs pulse stops the motor, 1000µs spins it full speed counter-clockwise, and 2000µs spins it full speed clockwise. They are commonly used for simple differential-drive robot wheels where precise distance tracking is not required, though they lack the positional holding ability of a standard servo.
What is a servo motor's typical PWM frequency?
Standard RC and hobby servos expect a PWM frequency of exactly 50 Hz, which means a new pulse is sent every 20 milliseconds (20,000µs). Within that 20ms window, the actual HIGH time (pulse width) dictates the position: typically 1000µs for 0 degrees, 1500µs for 90 degrees, and 2000µs for 180 degrees. Some modern digital servos can handle higher frequencies (up to 333Hz or 560Hz) for faster update rates, but sending a 50Hz signal is universally safe for all hobby servos. For industrial AC servos controlled via pulse/direction, the frequency can range from 10 kHz up to 4 MHz depending on the drive's opto-isolator limits.
What is a servo jittering and how do I fix it?
Jittering (a rapid, buzzing oscillation around the target position) is caused by the servo's internal control loop overreacting to noise. The most common culprits are: 1) A noisy power supply causing voltage ripple that the potentiometer reads as positional change. 2) A loose mechanical linkage (slop in the gears or horn). 3) Microcontroller timer interrupts interfering with the software-generated PWM signal. To fix it, first add a large decoupling capacitor (470µF to 1000µF) directly across the servo's VCC and GND wires. Second, ensure your microcontroller is using hardware-based PWM timers (like the ESP32's LEDC peripheral) rather than software bit-banging (like the basic Arduino Servo.h library on an Uno). Finally, tighten all mechanical linkages to remove physical backlash.






