Servo Motor – Precise angular control
A servo motor is a rotary actuator that allows precise control of angular position (typically 0–180°). It consists of a DC motor, a gear train, a potentiometer for position feedback, and a control circuit. The desired angle is communicated via PWM pulses — a 1 ms pulse drives the servo to 0°, 1.5 ms to 90°, and 2 ms to 180° (at 50 Hz / 20 ms period). Servos are widely used in robotics, camera gimbals, pan-tilt mechanisms, and radio-controlled models.
For this interfacing you need the following components:
- Arduino board (Uno, Nano, Mega, etc.)
- Servo motor (SG90, MG90S, MG995, or similar)
- Breadboard and jumper wires
- (Optional) External 5V power supply for high-torque servos
- (Optional) 100 µF capacitor across servo power leads
- USB cable to connect Arduino to your computer
Schematic
Connect the servo motor to the Arduino as follows:
Servo Motor Arduino
----------- -------
Signal (orange) --> Digital Pin 9
VCC (red) --> 5V (external PSU for large servos)
GND (brown) --> GND (common with Arduino)
For small servos (SG90, MG90S): VCC can connect to the Arduino 5V pin directly (current draw ~100–500 mA under load).
For large servos (MG995, MG996R): Connect VCC to an external 5V / 2A power supply and tie the supply’s ground to Arduino GND.
Adding a decoupling capacitor
Place a 100–470 µF electrolytic capacitor between the servo’s VCC and GND lines as close to the servo as possible. The capacitor absorbs voltage spikes generated by the servo motor during sudden direction changes, preventing Arduino resets.
Pin Map
| Servo Wire | Colour | Name | Arduino Connection |
|---|---|---|---|
| Signal | Orange (yellow) | Control signal | Digital Pin 9 (PWM) |
| Power | Red | VCC (5V) | 5V or external PSU |
| Ground | Brown (black) | GND | GND (common) |
Wire colours vary by manufacturer:
- TowerPro SG90/MG90S: Orange (signal), Red (VCC), Brown (GND)
- MG995/MG996R: Yellow (signal), Red (VCC), Brown (GND)
- Futaba/JR standard: White (signal), Red (VCC), Black (GND)
Using other PWM pins
The Arduino Servo library uses hardware timers to generate the 50 Hz PWM signal:
| Board | Available Servo Pins | Timer Used |
|---|---|---|
| Uno / Nano / Mini | Pins 9, 10 | Timer1 |
| Mega | Pins 2–13, 44–46 | Timer5 (up to 12 servos) |
| Leonardo | Pins 9, 10 | Timer1 |
Note: Using the Servo library on pins 9 and 10 on Uno/Nano disables analogWrite() on those pins (but they still work with digitalWrite()). On Mega, up to 12 servos can be controlled without affecting other PWM outputs.
Install necessary Library
The Servo library is built into the Arduino IDE — no installation is required. Include it with:
#include <Servo.h>
If using multiple servos, create separate Servo objects for each:
Servo servo1;
Servo servo2;
Code with complete explanation
This sketch sweeps a servo from 0° to 180° and back, then demonstrates position control via Serial Monitor commands.
#include <Servo.h>
Servo myservo;
const int servoPin = 9;
int angle = 0;
int step = 1;
void setup()
{
Serial.begin(9600);
myservo.attach(servoPin);
Serial.println("Servo Motor Test");
Serial.println("Enter an angle (0–180) to move the servo");
Serial.println("Enter 's' to start sweep mode");
Serial.println("Enter 'd' to detach servo (save power)");
Serial.println();
}
void loop()
{
// Check for serial commands
if (Serial.available())
{
char cmd = Serial.read();
if (cmd == 's')
{
Serial.println("Sweep mode");
sweep();
}
else if (cmd == 'd')
{
myservo.detach();
Serial.println("Servo detached");
}
else if (cmd >= '0' && cmd <= '9')
{
// Read the full number from serial
int target = Serial.parseInt();
target = constrain(target, 0, 180);
myservo.write(target);
Serial.print("Moving to: ");
Serial.print(target);
Serial.println("°");
}
}
}
// Sweep servo from 0 to 180 and back
void sweep()
{
myservo.attach(servoPin);
for (angle = 0; angle <= 180; angle += 1)
{
myservo.write(angle);
delay(15);
}
for (angle = 180; angle >= 0; angle -= 1)
{
myservo.write(angle);
delay(15);
}
}
Code breakdown
#include <Servo.h>— includes the built-in Servo library. No separate installation needed.Servo myservo— creates a Servo object to control a single servo.myservo.attach(pin)— associates the Servo object with a PWM-capable pin. Optionally accepts min and max pulse widths:attach(pin, min, max)where min/max are in µs (default 544 / 2400).myservo.write(angle)— sets the servo to a specific angle (0–180). The library converts the angle to the corresponding PWM pulse width.myservo.writeMicroseconds(pulseWidth)— directly sets the PWM pulse width in microseconds (e.g., 1500 µs = 90°). Useful for fine-tuning or non-standard servos.myservo.read()— returns the last written angle (not necessarily the current physical position).myservo.detach()— stops the servo signal, allowing the servo to relax (saves power and prevents jitter). Callattach()again to re-enable control.myservo.attached()— returnstrueif the Servo object is currently attached to a pin.
Using continuous rotation servos
Some servos (e.g., FS90R, SM-S4303R) are modified for continuous rotation. With these, write(0) drives full speed one direction, write(90) stops, and write(180) drives full speed the opposite direction:
myservo.write(0); // Full speed clockwise
delay(2000);
myservo.write(90); // Stop
delay(1000);
myservo.write(180); // Full speed anticlockwise
delay(2000);
myservo.write(90); // Stop
Adjusting pulse width range
If your servo does not reach the full 0–180° range with the default pulse widths, calibrate it using writeMicroseconds() and then set custom limits in attach():
// Find the actual pulse limits for your servo
// Start with 500 µs, then adjust up/down
myservo.attach(9);
myservo.writeMicroseconds(500); // Should be ~0°
delay(1000);
myservo.writeMicroseconds(1500); // Should be ~90°
delay(1000);
myservo.writeMicroseconds(2500); // Should be ~180°
delay(1000);
// If 500 µs misses 0°, try 544 or 600
myservo.detach();
// Set custom pulse width range
myservo.attach(9, 600, 2400); // min pulse = 600 µs, max = 2400 µs
Controlling multiple servos
#include <Servo.h>
Servo servo1, servo2, servo3;
void setup()
{
servo1.attach(9);
servo2.attach(10);
servo3.attach(11); // Uno — note: pins 9 & 10 share Timer1
servo1.write(0);
servo2.write(90);
servo3.write(180);
}
On a Mega, you can attach up to 12 servos on any of pins 2–13 and 44–46.
Steps to perform this interfacing
- Connect the servo to the Arduino as shown in the schematic. For small servos (SG90), power from the Arduino 5V pin. For large servos, use an external 5V supply.
- Connect a 100–470 µF capacitor across the servo’s VCC and GND near the servo to suppress voltage spikes.
- Open the Arduino IDE and use the built-in Servo library (no installation needed).
- Copy the code into the IDE.
- Select the correct board and port (
Tools > BoardandTools > Port). - Upload the sketch to the Arduino.
- Open the Serial Monitor (
Tools > Serial Monitor, set baud rate to 9600). - Type
sto start the sweep mode — the servo will sweep from 0° to 180° and back. - Enter a number (0–180) to move the servo to a specific angle.
- Type
dto detach the servo (the shaft becomes free to rotate manually).
Caution
- Power supply: Small servos (SG90, MG90S) draw 100–500 mA under load and can be powered from the Arduino 5V pin. Larger servos (MG995, MG996R) can draw 1–2 A under stall conditions and must use an external 5V power supply. Attempting to power a large servo from the Arduino 5V pin will overheat and destroy the onboard voltage regulator.
- Common ground: When using an external power supply, always connect its ground to the Arduino GND. Without a common ground, the control signal has no reference and the servo will behave erratically.
- Stall current: If a servo is physically blocked from reaching its commanded position, it draws maximum current (stall current). Prolonged stalling can overheat and damage the servo motor and driver. Do not hold a servo at its mechanical limit for more than a few seconds.
- Timer conflict: On Uno/Nano, the Servo library uses Timer1, which shares hardware with
analogWrite()on pins 9 and 10. If you use the Servo library,analogWrite()will not work on those pins. Use pins 3, 5, 6, or 11 foranalogWrite()instead, or use a Mega. - Physical limits: Always leave a small margin (5–10°) from the stated 0° and 180° limits. Hitting the mechanical end-stop causes the servo to draw stall current and may strip the plastic gears over time. For plastic-gear servos (SG90), even a brief stall can strip gears.
- Jitter: If the servo jitters or twitches, the cause is usually: (a) insufficient power, (b) noisy power supply (add a capacitor), or (c) calling
attach()repeatedly in the loop. Callattach()once insetup()and call onlywrite()inloop(). - Metal vs plastic gears: SG90 and similar micro servos have plastic gears that strip easily under load. For applications requiring torque or frequent use, choose a metal-gear servo like the MG90S or MG996R.