To control a servo with an Arduino, you use the built-in <Servo.h> library to send 50Hz PWM pulses ranging from 500µs to 2500µs. However, writing the code is only 10% of the battle. The real point of failure for most makers is mismatching the servo's stall torque to the physical load, or attempting to power a high-torque metal-gear servo directly from the Arduino's onboard 5V regulator. This guide gives you the exact sizing math, wiring topology, and microsecond-level code needed to run precision angular loads without browning out your microcontroller.
Why Choose a Servo? Motor Type Comparison & Load Profiling
Before writing a single line of code, you must verify that a servo is actually the right actuator for your mechanical load. Hobby servos are closed-loop systems containing a DC motor, a gear train, and an internal potentiometer for position feedback. They excel at high-torque, low-speed, bounded angular movement (typically 0° to 180°). If your application requires continuous rotation, high RPM, or exact linear translation, a servo is the wrong choice.
The table below breaks down how standard RC servos compare to other common actuator types across torque profiles, control complexity, and cost. This data assumes standard 5V-7.4V hobby-grade components sourced in 2026.
| Motor Type | Torque Curve Profile | Control Needs & Driver | Typical Cost (USD) |
|---|---|---|---|
| RC Servo (Standard) | Peak torque at zero speed (stall); drops sharply near max speed. Excellent holding torque. | 50Hz PWM (500-2500µs). Driven directly by microcontroller GPIO via internal H-bridge. | $2 - $15 |
| Stepper Motor (NEMA 17) | Flat torque curve up to mid-range RPM; falls off at high speeds. High holding torque. | Step/Direction pulses. Requires external dedicated driver (e.g., A4988, TMC2209). | $12 - $25 |
| Brushed DC Motor | Linear drop from peak stall torque to zero torque at no-load max RPM. Poor holding torque. | PWM for speed, H-bridge for direction. Requires external motor driver (e.g., L298N, DRV8833). | $3 - $10 |
| Industrial AC Servo | Constant torque across wide speed range; massive peak overload capacity (300% rated). | Dedicated industrial drive, 3-phase power, encoder feedback, EtherCAT/Modbus comms. | $250 - $800+ |
The Verdict: Choose an RC servo when you need simple, high-torque angular positioning under 180° without the overhead of tuning a stepper driver or managing an external H-bridge. Choose a stepper if you need continuous rotation with precise position tracking or open-loop linear motion via lead screws.
Sizing Your Servo: Torque Math and Worked Load Example
The most common mistake in embedded projects is treating a servo's rated "stall torque" as its "working torque." Stall torque is the absolute maximum force the motor can exert before the shaft stops moving and the internal current spikes, potentially melting the plastic gears or burning out the DC motor windings.
Worked Load Example: Robotic Pan/Tilt Camera Mount
Let's size a servo for a tilting camera arm. The arm is 12 cm long from the pivot to the center of mass. The camera and arm hardware weigh 250 grams (0.25 kg) total.
- Calculate Load Torque: Torque = Force × Distance.
0.25 kg × 12 cm = 3.0 kg-cm. - Apply Safety Factor: Because the arm accelerates and decelerates (dynamic load), we use the 2.0 multiplier.
3.0 kg-cm × 2.0 = 6.0 kg-cm required working torque. - Select the Motor:
- A standard SG90 micro servo is rated for 1.8 kg-cm stall torque. It will immediately stall and overheat.
- An MG996R metal-gear servo is rated for ~13 kg-cm stall torque. 50% of 13 kg-cm is 6.5 kg-cm. This is a perfect, safe match.
- A DS3218 digital servo offers 20 kg-cm. This is overkill, adds unnecessary cost, and draws higher idle current, but would work flawlessly.
Wiring, Terminals, and Powering High-Torque Servos
Standard hobby servos use a 3-pin JST or Dupont connector. The pinout is universally standardized across brands like TowerPro, HiTec, and Futaba, though wire colors can occasionally vary. Always verify the pinout on the specific datasheet, but the standard layout (looking at the bottom of the connector with the tab facing you) is:
- Signal (Pin 3): Orange or White. Connects to an Arduino PWM-capable GPIO pin (e.g., Pin 9).
- VCC (Pin 2): Red. Connects to the positive power rail (typically 4.8V to 6.0V for standard servos, up to 7.4V for high-voltage "HV" servos).
- GND (Pin 1): Brown or Black. Connects to the common ground.
Critical Power Warning: Never power an MG996R or larger servo directly from the Arduino's 5V pin. The Arduino's onboard linear regulator (often an NCP1117) can only safely supply ~500mA to 800mA. A large servo under load can pull 1.5A to 2.5A in transient spikes. This will cause a microcontroller brownout (resetting your Arduino) or permanently melt the PCB trace feeding the 5V pin.
The Correct Topology: Use a dedicated 5V/6V power supply (like a 5V 3A buck converter or a bench supply) for the servo's VCC. Connect the power supply's GND directly to the Arduino's GND to establish a common reference voltage for the PWM signal. For high-torque digital servos, solder a 470µF electrolytic capacitor across the VCC and GND wires near the servo horn to absorb transient current spikes and smooth out voltage ripple.
Writing the Arduino Servo Code: PWM, Sweep, and Microsecond Timing
The Arduino Servo Library abstracts the hardware timers to generate the required 50Hz signal. While servo.write(angle) is fine for basic sweeping, it maps 0-180 degrees to a fixed microsecond range that often doesn't match the physical limits of your specific servo, leading to mechanical binding.
For precision applications, use servo.writeMicroseconds(). According to Adafruit's motor selection guidelines, standard servos expect a pulse width of 1000µs for 0° and 2000µs for 180°, but many modern digital servos utilize a wider 500µs to 2500µs range for full travel. The code below demonstrates robust initialization, microsecond-level targeting, and safe detachment to prevent idle jitter.
#include <Servo.h>
// Pin definitions
const int SERVO_PIN = 9; // Must be a PWM-capable pin
const int POT_PIN = A0; // Analog input for manual control
Servo panServo;
// Physical limits determined by testing your specific servo (in microseconds)
const int MIN_PULSE = 550; // ~0 degrees
const int MAX_PULSE = 2450; // ~180 degrees
void setup() {
Serial.begin(115200);
// Attach servo with explicit microsecond limits to prevent mechanical over-travel
panServo.attach(SERVO_PIN, MIN_PULSE, MAX_PULSE);
// Move to a known safe starting position (center)
panServo.writeMicroseconds(1500);
delay(500);
}
void loop() {
// Read potentiometer (0-1023) and map to our precise microsecond range
int potValue = analogRead(POT_PIN);
int targetPulse = map(potValue, 0, 1023, MIN_PULSE, MAX_PULSE);
// Apply a deadband to prevent micro-jitter when the pot is stationary
static int lastPulse = 1500;
if (abs(targetPulse - lastPulse) > 10) {
panServo.writeMicroseconds(targetPulse);
lastPulse = targetPulse;
// Optional: Print for debugging via Serial Plotter
Serial.print("Target Pulse: ");
Serial.println(targetPulse);
}
// Small delay to stabilize ADC readings and reduce servo bus traffic
delay(20);
}
Debugging Failure Signatures: Hum, Overheat, and Stall
When a servo mechanism fails, it rarely does so silently. The physical and electrical symptoms will tell you exactly where the failure lies. Use this diagnostic matrix when your servo hookup isn't behaving as expected.
1. The "Hum" or High-Frequency Jitter
- Symptom: The servo shaft vibrates rapidly at a specific position, accompanied by an audible buzzing.
- Cause A (Electrical): Noisy PWM signal or inadequate power supply decoupling. The internal potentiometer is reading fluctuating voltage, causing the internal H-bridge to rapidly reverse direction to "correct" a phantom error.
- Fix: Add a 470µF capacitor across VCC/GND. Ensure the Arduino GND and Servo PSU GND are tied together with a thick wire (minimum 22 AWG) to prevent ground loops. Keep the PWM signal wire away from high-current motor wires.
- Cause B (Mechanical): Stripped internal nylon gears or a loose potentiometer wiper.
- Fix: Open the servo casing and inspect the gear train. Replace with a metal-gear variant (e.g., MG996R) if the load exceeds the nylon gear rating.
2. Overheating and Thermal Shutdown
- Symptom: The servo casing is hot to the touch (>50°C), smells like hot plastic, or the Arduino randomly resets during movement.
- Cause: The servo is being commanded to a position that is physically blocked (a hard mechanical stop), or the load exceeds the continuous torque rating. The motor is stalled, drawing maximum current (often 1.5A - 2.5A) continuously without generating back-EMF to limit the current.
- Fix: Verify your
MIN_PULSEandMAX_PULSEvalues in code. Commanding a pulse width beyond the physical limits of the potentiometer forces the motor to stall against the internal hard stops. Back off the limits by 50-100µs. Ensure your power supply can handle the stall current without dropping voltage below 4.5V.
3. Stall and Position Drift
- Symptom: The servo moves to the commanded angle, but under load, it slowly droops or fails to reach the final 5° of travel.
- Cause: The load torque exceeds the servo's working torque rating, causing the internal DC motor to stall before reaching the target potentiometer voltage. Alternatively, the internal potentiometer has suffered "dielectric absorption" or physical wear, causing the feedback voltage to drift from the actual shaft position.
- Fix: Recalculate your load torque using the 2.0 safety factor outlined above. If the math checks out but the servo still droops, the internal potentiometer is worn out. Upgrade to a digital servo with a higher torque rating, or switch to a stepper motor with a closed-loop encoder if absolute positional integrity under heavy load is required.






