575  Servo Motors

Learning Objectives

After completing this chapter, you will be able to:

  • Understand servo motor construction and operating principles
  • Control servo position using PWM pulse width
  • Interface single and multiple servos with ESP32
  • Implement smooth motion profiles using interpolation
  • Build coordinated multi-servo systems (robotic arms, pan-tilt)
  • Choose between servo and stepper motors for positioning applications

575.1 Servo Motor Fundamentals

Servo motors provide precise angular positioning (typically 0-180 degrees) using PWM signals. Unlike DC motors that spin continuously, servos move to specific angles and hold position.

Characteristics:

  • Precise position control
  • Built-in feedback loop
  • Limited rotation range (standard servos: 0-180 degrees)
  • Easy to control with PWM
  • Self-correcting (closed-loop)

575.1.1 Internal Components

Servo motors integrate a complete feedback control system in a compact package:

  1. DC Motor Core: Provides rotational force
  2. Gear Reduction System: Amplifies torque, reduces speed
  3. Potentiometer: Position feedback sensor
  4. Control Circuit: Compares commanded vs actual position

The internal potentiometer continuously measures shaft position, and the control circuit compares this to the commanded position from the PWM signal. This closed-loop design maintains position accuracy even under varying loads.

575.1.2 PWM Position Control

Servo motors use pulse width (not duty cycle) to determine position:

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085', 'secondaryColor': '#E67E22', 'tertiaryColor': '#ECF0F1'}}}%%
flowchart LR
    subgraph Servo[Servo PWM Timing at 50Hz 20ms period]
        P1[1ms pulse<br/>0 degrees<br/>5% duty cycle]
        P2[1.5ms pulse<br/>90 degrees<br/>7.5% duty cycle]
        P3[2ms pulse<br/>180 degrees<br/>10% duty cycle]
    end

    MCU[Microcontroller<br/>sends 50Hz PWM] --> P1
    MCU --> P2
    MCU --> P3

    P1 -.-> S1[Servo arm<br/>rotates to 0 deg]
    P2 -.-> S2[Servo arm<br/>rotates to 90 deg]
    P3 -.-> S3[Servo arm<br/>rotates to 180 deg]

    style MCU fill:#2C3E50,stroke:#16A085,color:#fff
    style P1 fill:#E67E22,stroke:#2C3E50,color:#fff
    style P2 fill:#16A085,stroke:#2C3E50,color:#fff
    style P3 fill:#E74C3C,stroke:#2C3E50,color:#fff
    style S1 fill:#ECF0F1,stroke:#2C3E50,color:#2C3E50
    style S2 fill:#ECF0F1,stroke:#2C3E50,color:#2C3E50
    style S3 fill:#ECF0F1,stroke:#2C3E50,color:#2C3E50

Figure 575.1: Servo Motor Position Control: Pulse Width Determines Angle (50Hz)

Key parameters:

  • 50 Hz frequency (20ms period) - Standard for hobby servos
  • 1ms pulse = 0 degrees (5% duty cycle)
  • 1.5ms pulse = 90 degrees (7.5% duty cycle, center position)
  • 2ms pulse = 180 degrees (10% duty cycle, maximum rotation)

Important: Servo libraries handle the timing. You just write servo.write(90) for 90 degrees!

575.2 Servo vs Stepper Comparison

WarningTradeoff: Servo Motor vs. Stepper Motor for Positioning

Option A: Servo motor (SG90/MG996R): Range 0-180 degrees, resolution ~1 degree, holding torque 1.8-10 kg-cm, power 5V @ 100-500mA active, cost $2-15, closed-loop feedback maintains position under load, simple PWM control (1 GPIO pin)

Option B: Stepper motor (NEMA17): Range unlimited rotation, resolution 1.8 degrees/step (200 steps/rev), micro-stepping achieves 0.05 degrees, holding torque 40+ kg-cm, power 12V @ 400mA-2A, cost $8-25, open-loop control requires calibration/homing, requires driver board + 2-4 GPIO pins

Decision Factors: For IoT applications needing simple angular positioning within 180 degrees (smart vents, valve actuators, pan-tilt cameras), servos win on simplicity and cost. For continuous rotation, high precision (<0.1 degree), or high torque (3D printer axes, CNC, telescope mounts), steppers are mandatory. Servos consume power only when moving; steppers draw holding current continuously. For battery IoT, this makes servos 5-10x more energy efficient for intermittent positioning tasks.

575.3 Basic Servo Control

575.3.1 Single Servo Control

#include <ESP32Servo.h>

Servo myServo;

#define SERVO_PIN 18

void setup() {
  Serial.begin(115200);

  // Attach servo (ESP32 uses different timers)
  myServo.attach(SERVO_PIN, 500, 2400);  // Min/max pulse width in microseconds

  Serial.println("Servo Controller Ready");
}

void loop() {
  // Sweep from 0 to 180 degrees
  for (int pos = 0; pos <= 180; pos++) {
    myServo.write(pos);
    Serial.print("Position: ");
    Serial.println(pos);
    delay(15);
  }

  delay(1000);

  // Sweep back from 180 to 0 degrees
  for (int pos = 180; pos >= 0; pos--) {
    myServo.write(pos);
    Serial.print("Position: ");
    Serial.println(pos);
    delay(15);
  }

  delay(1000);
}

575.3.2 Multi-Servo Robotic Arm

#include <ESP32Servo.h>

// Define servos
Servo baseServo;      // Base rotation
Servo shoulderServo;  // Shoulder joint
Servo elbowServo;     // Elbow joint
Servo gripperServo;   // Gripper

// Servo pins
#define BASE_PIN 18
#define SHOULDER_PIN 19
#define ELBOW_PIN 21
#define GRIPPER_PIN 22

// Robot position structure
struct RobotPosition {
  int base;
  int shoulder;
  int elbow;
  int gripper;
};

// Predefined positions
RobotPosition homePos = {90, 90, 90, 90};
RobotPosition pickPos = {45, 45, 45, 90};
RobotPosition placePos = {135, 45, 45, 45};

void setup() {
  Serial.begin(115200);

  // Attach servos
  baseServo.attach(BASE_PIN, 500, 2400);
  shoulderServo.attach(SHOULDER_PIN, 500, 2400);
  elbowServo.attach(ELBOW_PIN, 500, 2400);
  gripperServo.attach(GRIPPER_PIN, 500, 2400);

  // Move to home position
  moveToPosition(homePos, 1000);

  Serial.println("Robotic Arm Ready");
  Serial.println("Commands: h=home, p=pick, l=place, s=sequence");
}

void loop() {
  if (Serial.available()) {
    char cmd = Serial.read();

    switch(cmd) {
      case 'h':
        Serial.println("Moving to home position");
        moveToPosition(homePos, 1500);
        break;

      case 'p':
        Serial.println("Moving to pick position");
        moveToPosition(pickPos, 1500);
        break;

      case 'l':
        Serial.println("Moving to place position");
        moveToPosition(placePos, 1500);
        break;

      case 's':
        Serial.println("Running pick and place sequence");
        pickAndPlaceSequence();
        break;
    }
  }
}

void moveToPosition(RobotPosition target, int duration) {
  // Get current positions
  int currentBase = baseServo.read();
  int currentShoulder = shoulderServo.read();
  int currentElbow = elbowServo.read();
  int currentGripper = gripperServo.read();

  int steps = duration / 20;  // 20ms per step (50Hz)

  // Smooth interpolation
  for (int i = 0; i <= steps; i++) {
    float progress = (float)i / (float)steps;

    int base = currentBase + (target.base - currentBase) * progress;
    int shoulder = currentShoulder + (target.shoulder - currentShoulder) * progress;
    int elbow = currentElbow + (target.elbow - currentElbow) * progress;
    int gripper = currentGripper + (target.gripper - currentGripper) * progress;

    baseServo.write(base);
    shoulderServo.write(shoulder);
    elbowServo.write(elbow);
    gripperServo.write(gripper);

    delay(20);
  }
}

void pickAndPlaceSequence() {
  // Move to pick position
  moveToPosition(pickPos, 1500);
  delay(500);

  // Close gripper
  gripperServo.write(45);
  delay(500);

  // Move to place position
  moveToPosition(placePos, 2000);
  delay(500);

  // Open gripper
  gripperServo.write(90);
  delay(500);

  // Return home
  moveToPosition(homePos, 1500);
}

575.4 Smooth Motion with Interpolation

For professional-quality motion, use smooth acceleration/deceleration profiles:

// Ease-in-out interpolation for smooth motion
float easeInOut(float t) {
  return t < 0.5 ? 2 * t * t : 1 - pow(-2 * t + 2, 2) / 2;
}

void smoothMove(Servo& servo, int startPos, int endPos, int duration) {
  int steps = duration / 20;

  for (int i = 0; i <= steps; i++) {
    float t = (float)i / (float)steps;
    float easedT = easeInOut(t);

    int pos = startPos + (endPos - startPos) * easedT;
    servo.write(pos);
    delay(20);
  }
}

575.5 Interactive Lab: Servo Control

NoteTry It Yourself: Control a Servo Motor

What you’ll do: Control a servo motor’s position using buttons and potentiometer.

What you’ll learn:

  • How PWM pulse width controls servo position
  • Smooth motion using delay and interpolation
  • Reading analog input to control servo

Estimated time: 10 minutes

Interactive Challenges:

  1. Potentiometer Control: Use a potentiometer to control servo position (0-180 degrees)
  2. Button Presets: Add buttons for preset positions (0, 45, 90, 135, 180 degrees)
  3. Smooth Sweep: Implement smooth back-and-forth sweeping motion

575.6 Common Servo Specifications

Model Voltage Torque Speed Weight Use Case
SG90 4.8-6V 1.8 kg-cm 0.1s/60 deg 9g Light loads, prototyping
MG90S 4.8-6V 2.2 kg-cm 0.1s/60 deg 13.4g Metal gears, higher torque
MG996R 4.8-7.2V 11 kg-cm 0.17s/60 deg 55g Robotic arms, heavy loads
DS3218 4.8-6.8V 21 kg-cm 0.16s/60 deg 60g Industrial, waterproof
WarningPower Supply Warning

Do NOT power multiple servos from the ESP32 5V pin! Use an external 5V 2A power supply. Connect ESP32 GND to power supply GND (common ground).

SG90: Up to 600mA stall current each MG996R: Up to 2.5A stall current each

A 3-servo arm with MG996R servos may need 5V @ 5A or more!

575.7 Pan-Tilt Camera Mount

A common IoT application is a pan-tilt camera mount:

#include <ESP32Servo.h>

Servo panServo;   // Horizontal rotation
Servo tiltServo;  // Vertical rotation

#define PAN_PIN 18
#define TILT_PIN 19

// Position limits
#define PAN_MIN 0
#define PAN_MAX 180
#define TILT_MIN 0
#define TILT_MAX 90  // Limit tilt to prevent looking backward

int panPos = 90;   // Start center
int tiltPos = 45;  // Start mid-height

void setup() {
  Serial.begin(115200);

  panServo.attach(PAN_PIN, 500, 2400);
  tiltServo.attach(TILT_PIN, 500, 2400);

  panServo.write(panPos);
  tiltServo.write(tiltPos);

  Serial.println("Pan-Tilt Camera Ready");
  Serial.println("Commands: w=up, s=down, a=left, d=right, c=center");
}

void loop() {
  if (Serial.available()) {
    char cmd = Serial.read();

    switch(cmd) {
      case 'w':  // Tilt up
        tiltPos = min(tiltPos + 10, TILT_MAX);
        break;
      case 's':  // Tilt down
        tiltPos = max(tiltPos - 10, TILT_MIN);
        break;
      case 'a':  // Pan left
        panPos = min(panPos + 10, PAN_MAX);
        break;
      case 'd':  // Pan right
        panPos = max(panPos - 10, PAN_MIN);
        break;
      case 'c':  // Center
        panPos = 90;
        tiltPos = 45;
        break;
    }

    panServo.write(panPos);
    tiltServo.write(tiltPos);

    Serial.print("Pan: ");
    Serial.print(panPos);
    Serial.print(" Tilt: ");
    Serial.println(tiltPos);
  }
}

575.8 What’s Next?

Now that you understand servo motors, you’re ready to explore stepper motors for precise step-by-step positioning.

Continue to Stepper Motors →