In short: You position a hobby servo from an Arduino by sending it a 50 Hz pulse whose high-time (roughly 1–2 ms) encodes the target angle, and the Servo library generates that pulse for you so a one-line servo.write(90) parks the shaft at the midpoint.
A servo motor is the workhorse of beginner robotics: robotic arms, pan-tilt camera mounts, RC steering, and automated blinds all lean on it. Unlike a bare DC motor, a hobby servo is a closed-loop package — a small gear-reduced DC motor, a potentiometer feedback element, and an internal controller that drives the shaft to a commanded angle and holds it there. Your Arduino never sees the motor current; it only ever sends a position command. Understanding exactly what that command is turns servo control from copy-paste into something you can debug.
What the servo actually listens to
A standard analog servo expects a pulse-position signal: a pulse that repeats every 20 ms (a 50 Hz frame) whose width, not its frequency or voltage, carries the information. The classic mapping is 1.0 ms → 0°, 1.5 ms → 90° (neutral), and 2.0 ms → 180°. That is only ~5–10 % duty cycle, which is why people get confused calling this "PWM." It is generated by timer hardware like PWM, but the servo decodes pulse width; the duty cycle is incidental.
Many real servos ignore the textbook 1–2 ms limits. A Tower Pro SG90 often needs about 500 µs to 2400 µs to hit its full mechanical range. If your servo refuses to reach 0° or 180°, widen the pulse range with servo.attach(pin, 500, 2400) rather than forcing the angle.
Wiring it correctly (and why power matters)
Every hobby servo has three leads: brown/black = GND, red = V+, and orange/yellow = signal. The signal goes to any Arduino digital pin — the Servo library can drive up to 12 servos on an UNO using Timer1. The trap is power. A micro servo can spike 400–600 mA during a fast move, and the UNO's onboard 5 V regulator is not sized for that. Driving a servo from the Arduino 5 V pin causes brownouts that reset the board mid-sketch.
- Use a separate 5–6 V supply (a 4×AA pack or a bench supply) for V+.
- Tie the grounds together — the external supply GND, servo GND, and Arduino GND must share a common reference, or the signal has no return path.
- Add a 470 µF electrolytic cap across the servo supply rails to absorb inrush spikes.
The minimal sketch
The Servo library hides the timer math. You attach a pin, then call write() with an angle in degrees:
#includeand declareServo s;- In
setup():s.attach(9);binds the servo to pin 9. s.write(angle)takes 0–180;s.writeMicroseconds(1500)gives you raw pulse control for fine tuning or extended-range servos.
A classic "sweep" loops the angle from 0 to 180 and back with a short delay() between steps so the mechanics can keep up. Because the servo moves at a fixed slew rate (often ~0.1 s/60°), stepping faster than the servo can physically travel just means later commands override earlier ones — the horn appears to jump.
Common positioning projects and their gotchas
| Project | Control idea | Watch out for |
|---|---|---|
| Pan-tilt camera | Two servos, map joystick X/Y with map() | Mechanical end-stops; clamp angles |
| Robotic gripper | Angle → jaw open/close | Stall current when gripping — limit close angle |
| Dial/gauge indicator | map(sensor,0,1023,0,180) | ADC noise jitters the needle; smooth input |
| RC steering | Read receiver pulse, mirror to servo | Centre trim via writeMicroseconds |
A positional servo is not a continuous-rotation servo. A 360° "continuous" servo reinterprets the pulse as speed and direction: 1.5 ms is stop, 1.0 ms is full reverse, 2.0 ms is full forward. Sending write(90) to one makes it hold still, not point forward.
For precision work — surgical robotics prototypes, test fixtures — engineers move up to digital servos (300 Hz+ update, tighter deadband) or closed-loop stepper/BLDC systems validated in motor-control toolchains. The same pulse-timing concept scales into motor-controller IP that silicon teams verify in simulators like Synopsys VCS or Siemens Questa before it ever drives real hardware. If you want to go from blinking servos to designing the controllers inside them, structured VLSI and embedded programs from ChipXpert's training bridge that gap.
Smoothing and multi-servo coordination
To avoid the "snap" of a direct write(), ramp toward the target: on each loop iteration move the current angle one step toward the goal and delay(15). For many servos, drive them from a non-blocking loop using millis() timing rather than delay(), so one slow servo doesn't freeze the rest of your program. Beyond a handful of servos, offload timing to a PCA9685 I²C driver — it generates all 16 channels in hardware, freeing the Arduino entirely. Pairing servos with a relay or driver stage is also common; see our guide on driving loads with a relay module and the companion on PWM and analogWrite fundamentals.
Ready to build real motion systems? The CourseTron Embedded Systems track takes you from your first servo sweep to multi-axis motion control and interrupt-driven firmware.
Frequently asked questions
Can I power a servo directly from the Arduino 5 V pin?
For one tiny servo under light load, sometimes — but it is unreliable. Current spikes brown out the regulator and reset the board. Use a separate 5–6 V supply and share grounds.
Why doesn't my servo reach a full 0° or 180°?
The default 1–2 ms range is conservative. Call attach(pin, minUs, maxUs) with values like 500 and 2400 to match your specific servo's mechanical limits.
What is the difference between write() and writeMicroseconds()?
write() takes an angle (0–180) and converts it internally; writeMicroseconds() sets the pulse width directly, giving finer resolution and access to a servo's extended range.
How many servos can one Arduino UNO control?
Up to 12 with the standard Servo library (it uses Timer1, which disables PWM on pins 9 and 10). For more, or to free the CPU, use a PCA9685 I²C PWM driver.