A hobby servo is the easiest actuator to control from an Arduino: three wires, one library, one line of code to set an angle. It’s also where most robotic arm projects start. This guide covers the wiring, the power supply that keeps it from misbehaving, and working code — from a simple sweep to knob and serial control.
How a servo is controlled
A hobby servo reads a stream of pulses, one roughly every 20 ms (50 per second). The width of each pulse sets the angle: around 1.5 ms is the centre, shorter pulses turn one way and longer ones the other. The servo’s internal circuit compares that target with a potentiometer on its output shaft and drives the motor until they match — the closed loop explained in how robotic arms work.
The Arduino Servo library generates those pulses for you. By default it maps
write(0) to a 544 µs pulse and write(180) to 2400 µs, and it starts a newly attached
servo at 1500 µs. Real servos vary, so you’ll often narrow that range — see
calibrating the range below.
Wiring
| Servo wire | Colour (typical) | Connects to |
|---|---|---|
| Ground | Brown or black | Supply ground and Arduino GND |
| Power (+) | Red | 5–6V supply positive |
| Signal | Orange, yellow or white | Any Arduino digital pin |
The rule that saves the most debugging time: the servo supply’s ground must be joined to the Arduino’s ground. The signal is measured relative to ground; without a shared reference the servo twitches or ignores you.
Powering it properly
For a single SG90 with nothing attached, the Arduino’s 5V pin will do for a quick test. For anything more, give the servos their own supply:
- A 5–6V supply sized for the stall current of every servo that can move at once — a 5V 10A adapter covers a typical arm.
- A 1000 µF capacitor across the servo power rail to absorb the current spikes of servos starting together.
- Thicker wire on the power rail than on the signal wires.
The full sizing maths is in powering a robotic arm, and the wiring diagram shows the complete circuit.
Example 1 — sweep
The first sketch every servo should run: it proves the wiring, power and code are all working before you bolt anything to the horn.
#include <Servo.h>
Servo servo;
void setup() {
servo.attach(9); // signal wire on pin 9
}
void loop() {
for (int angle = 0; angle <= 180; angle++) {
servo.write(angle);
delay(15); // ~15 ms per degree: a slow, safe sweep
}
for (int angle = 180; angle >= 0; angle--) {
servo.write(angle);
delay(15);
}
}
If the servo buzzes or strains at either end, it’s being pushed past its mechanical stop — limit the loop to, say, 10–170 until you’ve calibrated it.
Example 2 — control with a potentiometer
A 10K potentiometer gives you a hand-driven servo — and one knob per joint is the simplest possible controller for a robotic arm.
#include <Servo.h>
Servo servo;
const int POT_PIN = A0; // pot wiper; outer legs to 5V and GND
void setup() {
servo.attach(9);
}
void loop() {
int raw = analogRead(POT_PIN); // 0–1023
int angle = map(raw, 0, 1023, 0, 180);
servo.write(angle);
delay(15);
}
Example 3 — set the angle from the Serial Monitor
Type an angle into the Serial Monitor (with “Newline” selected) and the servo moves to it. The same pattern lets a Python script or a Raspberry Pi drive the servo — see robotic arm with Python.
#include <Servo.h>
Servo servo;
void setup() {
Serial.begin(9600);
servo.attach(9);
Serial.println("Send an angle 0-180");
}
void loop() {
if (Serial.available()) {
int angle = Serial.parseInt();
if (Serial.read() == '\n') { // consume the newline
angle = constrain(angle, 0, 180);
servo.write(angle);
Serial.print("Moved to ");
Serial.println(angle);
}
}
}
Calibrating the pulse range
Because servos differ, write(0) and write(180) may not match your servo’s real end
points. Find them with writeMicroseconds(): step the pulse down from 1500 until the
servo stops moving or starts to strain, note the value, and repeat upward. Then attach
with those limits so write() maps 0–180 onto the range your servo actually has:
servo.attach(9, 600, 2350); // your measured min and max pulse, in µs
One more detail worth knowing: servo.read() returns the last angle you wrote, not
the servo’s real position. A servo that’s stalled or overloaded still reports the
commanded angle. If you need true position, add feedback — see
encoders and position feedback.
More than one servo
Create one Servo object per servo and attach each to its own pin. The library handles
up to 12 servos on an Uno (48 on a Mega), but while it’s running analogWrite() stops
working on pins 9 and 10. For a full arm, a
PCA9685 driver is the better route: it generates the
pulses for up to 16 servos over two I2C wires and has its own servo power terminal. The
Arduino robotic arm code guide has multi-joint,
record-and-playback and smooth-motion sketches.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Servo jitters or twitches | Missing shared ground, or weak supply | Join grounds; use a separate 5–6V supply |
| Arduino resets when the servo moves | Servo powered from the 5V pin | Move servo power to its own supply |
| Buzzing at the end of travel | Driven past the mechanical stop | Narrow the angle or pulse range |
| Doesn’t move at all | Not attached, wrong pin, or no power | Check attach(), the pin number and servo voltage |
analogWrite stopped working | Servo library uses pins 9/10’s timer | Move PWM outputs to other pins |
Choosing the servo
An SG90 is ideal for learning and for light joints; a metal-gear MG996R is the usual choice for an arm’s shoulder and elbow. To work out what each joint of your own arm needs, use the robot arm torque calculator, and see servo motors for robotic arms for the servo classes. If you don’t have an Arduino yet, an Arduino starter kit with a servo included is the easiest way to start.
Frequently asked questions
How do I connect a servo motor to an Arduino?
A hobby servo has three wires. Brown or black is ground, red is power (+) and orange, yellow or white is the signal. Connect the signal wire to an Arduino digital pin, connect the servo's power and ground to a 5–6V supply, and join that supply's ground to the Arduino's GND. Then use the Servo library: attach() the pin and write() an angle.
Can the Arduino's 5V pin power a servo?
Only a single small servo with little or no load, and only for a quick test. Servos draw current spikes when they start and far more when loaded or stalled, which can pull the 5V rail down and reset the board. For anything real — several servos, or any servo lifting something — use a separate 5–6V supply and share the ground.
Which Arduino pins can control a servo?
Any digital pin. The Servo library generates the pulses itself using a hardware timer, so the pin doesn't need to be PWM-capable. On an Uno it can drive up to 12 servos, but while it's in use analogWrite() stops working on pins 9 and 10.
Why does my servo buzz or jitter?
Buzzing at the end of travel usually means the servo is being driven past its mechanical limit — narrow the angle or pulse range. Jitter while moving or holding is almost always power: an undersized supply, a missing shared ground, or servos powered from the Arduino's 5V pin.