Servo Motor · Astro Tech Blog

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.

Servo motor

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 WireColourNameArduino Connection
SignalOrange (yellow)Control signalDigital Pin 9 (PWM)
PowerRedVCC (5V)5V or external PSU
GroundBrown (black)GNDGND (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:

BoardAvailable Servo PinsTimer Used
Uno / Nano / MiniPins 9, 10Timer1
MegaPins 2–13, 44–46Timer5 (up to 12 servos)
LeonardoPins 9, 10Timer1

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). Call attach() again to re-enable control.
  • myservo.attached() — returns true if 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

  1. 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.
  2. Connect a 100–470 µF capacitor across the servo’s VCC and GND near the servo to suppress voltage spikes.
  3. Open the Arduino IDE and use the built-in Servo library (no installation needed).
  4. Copy the code into the IDE.
  5. Select the correct board and port (Tools > Board and Tools > Port).
  6. Upload the sketch to the Arduino.
  7. Open the Serial Monitor (Tools > Serial Monitor, set baud rate to 9600).
  8. Type s to start the sweep mode — the servo will sweep from 0° to 180° and back.
  9. Enter a number (0–180) to move the servo to a specific angle.
  10. Type d to 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 for analogWrite() 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. Call attach() once in setup() and call only write() in loop().
  • 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.