Overview
Servo motors are precision actuators that can rotate to specific angular positions. Unlike regular motors that just spin, servos provide position feedback and exact angular control - making them perfect for robotics, camera gimbals, robotic arms, and any application requiring precise movement.
In this lesson, you'll learn how to control servo motors using PWM signals from the ATmega328P microcontroller, with the servo connected to Digital Pin D9 on your Vumbua Synapse Kit.
Servo motors are used in:
- Robotics: Robot arms, walking robots
- RC Models: Steering in cars, control surfaces in planes
- Automation: Automatic doors, valve control
- Camera Systems: Pan-tilt camera mounts
- Industrial: CNC machines, 3D printers
- Home Automation: Smart blinds, pet feeders
Prerequisites
Before starting, make sure you have completed:
Completed Lessons
- Lesson 1: Blinking LED (PWM basics)
- Lesson 4: Potentiometer - Analog Input
- Lesson 8: LCD Display
- Understanding of PWM and analogWrite()
Hardware Requirements
- Vumbua Synapse Kit with SG90 Servo
- USB Type-C cable
- Computer with Arduino IDE
- Potentiometer (for advanced control)
The servo motor connects to the Servo Header on D9 of the Vumbua board. The header provides 5V power, GND, and signal connection - no external power supply needed for small servos like the SG90!
Learning Objectives
By the end of this lesson, you will be able to:
- Understand how servo motors work internally
- Connect and power servo motors correctly
- Use the Servo library for precise control
- Control servo angle with
write()function - Create smooth sweeping motions
- Control multiple servos simultaneously
- Use potentiometer for analog position control
- Understand continuous rotation servos
- Build robotics and automation projects
What is a Servo Motor?
Components of a Servo
A standard hobby servo contains:
- DC Motor: Provides rotation power
- Gear Reduction: Increases torque
- Potentiometer: Provides position feedback
- Control Circuit: Compares position with command
- Output Shaft: Connects to servo horn
How Servos Work
The servo control system works in a closed loop:
- MCU sends PWM pulse (1-2ms duration)
- Servo's internal circuit reads pulse width
- Potentiometer measures current position
- Circuit compares desired vs. actual position
- Motor turns until positions match
- Servo holds position against resistance
Types of Servos
| Type | Rotation | Torque | Application |
|---|---|---|---|
| Standard | 0-180° | 1-20 kg·cm | Robotics, RC models |
| Continuous | 360° continuous | Low to medium | Wheels, conveyors |
| Digital | 0-180° | High | Industrial, robotics |
| Micro | 0-180° | 0.5-2 kg·cm | Small projects, drones |
Servo Pin Connections
The SG90 servo has 3 wires with standard colors:
Never power servos directly from Arduino pins! Arduino pins can only supply 20-40mA, but servos can draw 100-500mA during movement. Always use:
- External power supply for multiple or large servos
- Capacitor across power lines to prevent voltage drops
- Separate power source for motors and MCU
For SG90 with Vumbua: Single servo is safe with board's 5V regulator.
PWM Servo Control Theory
Servos use a special form of PWM with precise timing requirements:
Servo PWM Signal Timing
Key Timing Parameters
| Parameter | Value | Description |
|---|---|---|
| Pulse Cycle | 20ms (50Hz) | Repeat rate of PWM signal |
| Min Pulse Width | 1.0ms | Corresponds to 0° position |
| Neutral Pulse | 1.5ms | Corresponds to 90° position |
| Max Pulse Width | 2.0ms | Corresponds to 180° position |
Calculating Position
The relationship between pulse width and angle is linear:
Where pulse width is in microseconds (1000-2000µs)
Using the Servo Library
Arduino provides a built-in Servo library that handles the complex PWM timing:
servo_library_basics.ino
/* Vumbua Synapse Kit - Lesson 9: Servo Library Basics Basic servo control using Arduino Servo library */ // Include the Servo library #include <Servo.h> // Create a servo object to control a servo Servo myServo; // Pin where servo is connected (D9 on Vumbua) const int servoPin = 9; void setup() { // Attach the servo on pin 9 to the servo object myServo.attach(servoPin); // Initialize Serial communication Serial.begin(9600); Serial.println("Servo Library Test"); Serial.println("Servo connected to pin D9"); } void loop() { // Nothing here yet - we'll add code in next sections }
Servo Library Functions
1. attach() - Connect Servo
// Basic attach servo.attach(pin); // With custom pulse range servo.attach(pin, minPulse, maxPulse);
Connects servo to specified pin. Optional min/max pulse in microseconds (default: 544-2400µs).
2. write() - Set Position
// Set angle in degrees (0-180) servo.write(angle); // Set pulse width in microseconds (500-2500) servo.writeMicroseconds(pulseWidth);
Sets servo position. Most servos accept 0-180°, but some have different ranges.
3. read() - Read Position
// Get last written angle
int angle = servo.read();
Returns last position set with write(), not actual servo position.
4. attached() & detach()
// Check if servo is attached if (servo.attached()) { // Servo is connected } // Disconnect servo (pulse stops) servo.detach();
Useful for power saving or reconfiguring pins.
Basic Servo Control Program
Let's create a complete program to control the servo through different positions:
servo_basic_control.ino
/* Vumbua Synapse Kit - Lesson 9: Basic Servo Control Move servo to specific positions with delays */ #include <Servo.h> Servo myServo; const int servoPin = 9; void setup() { myServo.attach(servoPin); Serial.begin(9600); Serial.println("Basic Servo Control Program"); Serial.println("Servo will move through positions: 0° → 90° → 180° → 90°"); } void loop() { // Move to 0 degrees (minimum position) Serial.println("Moving to 0°"); myServo.write(0); delay(1000); // Wait 1 second for movement // Move to 90 degrees (center position) Serial.println("Moving to 90°"); myServo.write(90); delay(1000); // Move to 180 degrees (maximum position) Serial.println("Moving to 180°"); myServo.write(180); delay(1000); // Return to center Serial.println("Returning to 90°"); myServo.write(90); delay(1000); // Demonstrate microsecond control Serial.println("Using writeMicroseconds()"); Serial.println("1000µs = 0°, 1500µs = 90°, 2000µs = 180°"); myServo.writeMicroseconds(1000); // 0 degrees delay(1000); myServo.writeMicroseconds(1500); // 90 degrees delay(1000); myServo.writeMicroseconds(2000); // 180 degrees delay(1000); myServo.writeMicroseconds(1500); // Back to center delay(1000); }
Testing the Program
1. Connect the Servo
Connect the SG90 servo to the Servo Header on your Vumbua Synapse Kit:
- Brown wire → GND (outer pin)
- Red wire → 5V (middle pin)
- Orange wire → Signal (inner pin, D9)
2. Upload and Observe
Upload the code and watch the servo move through the sequence:
- 0° position (fully counter-clockwise)
- 90° position (center)
- 180° position (fully clockwise)
- Return to center
- Repeat with microsecond control
3. Monitor Serial Output
Open Serial Monitor (Tools → Serial Monitor) to see position messages:
Servo will move through positions: 0° → 90° → 180° → 90°
Moving to 0°
Moving to 90°
...
Potentiometer Position Control
Combine servo control with potentiometer input from Lesson 4 for interactive control:
servo_pot_control.ino
/* Vumbua Synapse Kit - Lesson 9: Potentiometer Servo Control Control servo position with potentiometer */ #include <Servo.h> Servo myServo; const int servoPin = 9; // Servo on pin D9 const int potPin = A0; // Potentiometer on A0 int potValue; // Raw potentiometer reading int servoAngle; // Calculated servo angle int lastAngle = -1; // Last position to avoid redundant updates void setup() { myServo.attach(servoPin); Serial.begin(9600); Serial.println("Potentiometer Servo Controller"); Serial.println("Turn potentiometer to control servo position"); Serial.println("=============================================="); } void loop() { // Read potentiometer value (0-1023) potValue = analogRead(potPin); // Map potentiometer value to servo angle (0-180) servoAngle = map(potValue, 0, 1023, 0, 180); // Only update servo if position changed if (servoAngle != lastAngle) { // Move servo to new position myServo.write(servoAngle); lastAngle = servoAngle; // Calculate percentage for display int potPercentage = map(potValue, 0, 1023, 0, 100); // Display status Serial.print("Potentiometer: "); Serial.print(potValue); Serial.print(" ("); Serial.print(potPercentage); Serial.print("%) | Servo Angle: "); Serial.print(servoAngle); Serial.println("°"); } // Small delay for stability delay(20); }
Advanced Features to Add
servo_advanced_control.ino
/* Vumbua Synapse Kit - Lesson 9: Advanced Servo Control Added features: smoothing, constraints, LED feedback */ #include <Servo.h> Servo myServo; const int servoPin = 9; const int potPin = A0; const int ledPin = 13; // Built-in LED // Smoothing variables const int numReadings = 10; int readings[numReadings]; int readIndex = 0; int total = 0; int average = 0; // Angle constraints const int minAngle = 20; // Minimum safe angle const int maxAngle = 160; // Maximum safe angle void setup() { myServo.attach(servoPin); pinMode(ledPin, OUTPUT); Serial.begin(9600); // Initialize smoothing array for (int i = 0; i < numReadings; i++) { readings[i] = 0; } Serial.println("Advanced Servo Control with Smoothing"); Serial.println("Angle constrained to " + String(minAngle) + "° - " + String(maxAngle) + "°"); } void loop() { // Read potentiometer with smoothing total = total - readings[readIndex]; readings[readIndex] = analogRead(potPin); total = total + readings[readIndex]; readIndex = (readIndex + 1) % numReadings; average = total / numReadings; // Map to servo angle with constraints int servoAngle = map(average, 0, 1023, minAngle, maxAngle); // Constrain to safe limits servoAngle = constrain(servoAngle, minAngle, maxAngle); // Move servo myServo.write(servoAngle); // LED feedback based on position if (servoAngle < 90) { digitalWrite(ledPin, HIGH); } else { digitalWrite(ledPin, LOW); } // Display status every 100ms static unsigned long lastDisplay = 0; if (millis() - lastDisplay > 100) { lastDisplay = millis(); Serial.print("Raw: "); Serial.print(average); Serial.print(" | Angle: "); Serial.print(servoAngle); Serial.print("° | Limits: ["); Serial.print(minAngle); Serial.print("° - "); Serial.print(maxAngle); Serial.println("°]"); } delay(10); // Fast response for smooth control }
Sweep Function - Automatic Scanning
Create smooth automatic sweeping motion for scanning applications:
servo_sweep.ino
/* Vumbua Synapse Kit - Lesson 9: Servo Sweep Function Create smooth back-and-forth sweeping motion */ #include <Servo.h> Servo myServo; const int servoPin = 9; int pos = 0; // Current position int increment = 1; // Degrees to move each step int delayTime = 15; // Delay between steps (ms) // Sweep range const int minPos = 30; const int maxPos = 150; void setup() { myServo.attach(servoPin); Serial.begin(9600); Serial.println("Servo Sweep Function"); Serial.println("Sweeping from " + String(minPos) + "° to " + String(maxPos) + "°"); } void loop() { // Move servo one step myServo.write(pos); // Display position every 10 degrees if (pos % 10 == 0) { Serial.print("Position: "); Serial.print(pos); Serial.println("°"); } // Wait for servo to reach position delay(delayTime); // Update position for next step pos = pos + increment; // Reverse direction at limits if (pos <= minPos || pos >= maxPos) { increment = -increment; // Reverse direction // Optional: pause at ends delay(500); Serial.println("Direction reversed!"); } }
Variable Speed Sweep
servo_variable_sweep.ino
/* Vumbua Synapse Kit - Lesson 9: Variable Speed Sweep Control sweep speed with potentiometer */ #include <Servo.h> Servo myServo; const int servoPin = 9; const int potPin = A0; int pos = 0; int increment = 1; int delayTime; // Speed settings const int minSpeed = 5; // Fastest (5ms delay) const int maxSpeed = 50; // Slowest (50ms delay) void setup() { myServo.attach(servoPin); Serial.begin(9600); Serial.println("Variable Speed Servo Sweep"); Serial.println("Turn potentiometer to control sweep speed"); } void loop() { // Read potentiometer for speed control int potValue = analogRead(potPin); delayTime = map(potValue, 0, 1023, minSpeed, maxSpeed); // Move servo myServo.write(pos); delay(delayTime); // Update position pos = pos + increment; // Reverse direction at limits if (pos <= 0 || pos >= 180) { increment = -increment; } // Display status every 50 cycles static int cycleCount = 0; cycleCount++; if (cycleCount % 50 == 0) { int speedPercentage = map(delayTime, minSpeed, maxSpeed, 100, 0); Serial.print("Position: "); Serial.print(pos); Serial.print("° | Delay: "); Serial.print(delayTime); Serial.print("ms | Speed: "); Serial.print(speedPercentage); Serial.println("%"); } }
Controlling Multiple Servos
Learn to control two or more servos simultaneously for robotic applications:
multiple_servos.ino
/* Vumbua Synapse Kit - Lesson 9: Multiple Servo Control Control two servos for robotic arm or pan-tilt mechanism */ #include <Servo.h> // Create two servo objects Servo servoBase; // Base rotation (pan) Servo servoArm; // Arm elevation (tilt) // Servo pins const int basePin = 9; // Base servo on D9 const int armPin = 10; // Arm servo on D10 // Potentiometers for control const int potBasePin = A0; // Base control const int potArmPin = A1; // Arm control void setup() { // Attach servos to pins servoBase.attach(basePin); servoArm.attach(armPin); Serial.begin(9600); Serial.println("Dual Servo Control System"); Serial.println("Potentiometer 1 (A0) controls base"); Serial.println("Potentiometer 2 (A1) controls arm"); } void loop() { // Read both potentiometers int basePot = analogRead(potBasePin); int armPot = analogRead(potArmPin); // Map to servo angles with constraints int baseAngle = map(basePot, 0, 1023, 0, 180); int armAngle = map(armPot, 0, 1023, 30, 150); // Limited range for arm // Apply constraints for safety baseAngle = constrain(baseAngle, 0, 180); armAngle = constrain(armAngle, 30, 150); // Move both servos servoBase.write(baseAngle); servoArm.write(armAngle); // Display status static unsigned long lastUpdate = 0; if (millis() - lastUpdate > 200) { lastUpdate = millis(); Serial.print("Base: "); Serial.print(baseAngle); Serial.print("° | Arm: "); Serial.print(armAngle); Serial.println("°"); } delay(20); }
Multiple servos can draw significant current! When controlling more than one servo:
- Use external power supply (5-6V, 2A minimum for 2-3 servos)
- Connect power directly to servo power pins, not through Arduino
- Keep Arduino ground connected to external supply ground
- Add large capacitor (470-1000µF) across power lines near servos
- Consider using a dedicated servo controller board for complex projects
Continuous Rotation Servos
Some servos can be modified for continuous rotation - acting like gear motors:
continuous_servo.ino
/* Vumbua Synapse Kit - Lesson 9: Continuous Rotation Servo Control speed and direction of continuous rotation servo */ #include <Servo.h> Servo continuousServo; const int servoPin = 9; const int potPin = A0; void setup() { continuousServo.attach(servoPin); Serial.begin(9600); Serial.println("Continuous Rotation Servo Control"); Serial.println("Potentiometer controls speed and direction"); Serial.println("Center = Stop, Left = Reverse, Right = Forward"); } void loop() { int potValue = analogRead(potPin); // Map potentiometer to continuous servo range // 0 = Full reverse, 90 = Stop, 180 = Full forward int servoValue = map(potValue, 0, 1023, 0, 180); // For finer control around center (stop position) if (servoValue > 85 && servoValue < 95) { servoValue = 90; // Dead zone for precise stop } // Control continuous servo continuousServo.write(servoValue); // Calculate speed percentage (-100% to +100%) int speedPercent = map(servoValue, 0, 180, -100, 100); // Display status static int lastSpeed = 0; if (abs(speedPercent - lastSpeed) > 5) { lastSpeed = speedPercent; Serial.print("Servo Value: "); Serial.print(servoValue); Serial.print(" | Speed: "); Serial.print(speedPercent); if (speedPercent > 0) { Serial.println("% Forward"); } else if (speedPercent < 0) { Serial.println("% Reverse"); } else { Serial.println("% Stopped"); } } delay(50); }
Project Ideas
Robotic Arm
Build a simple 2-degree-of-freedom robotic arm:
- Base servo for rotation
- Arm servo for elevation
- Potentiometers for manual control
- Add gripper with third servo
- Programmable positions
Pan-Tilt Camera Mount
Create a camera positioning system:
- Servo for pan (horizontal)
- Servo for tilt (vertical)
- Joystick or dual potentiometer control
- Preset positions for surveillance
- Smooth scanning mode
Automatic Pet Feeder
Build an automated feeding system:
- Servo controls food dispenser
- Real-time clock for scheduling
- LCD display for status
- Button for manual feeding
- Portion size control
Solar Tracker
Create a sun-following system:
- Two servos for dual-axis tracking
- Light sensors for position feedback
- Maximum power point tracking
- Manual override capability
- Energy monitoring
ATmega328P Timers & PWM
Understanding how the ATmega328P generates PWM signals for servo control:
Timer1 - Servo Control Timer
The Servo library uses Timer1 (16-bit timer) to generate precise 50Hz PWM signals:
- Clock Source: 16MHz system clock
- Prescaler: Typically 8 or 64
- Frequency: 50Hz (20ms period)
- Resolution: 1µs (with 8 prescaler)
PWM Generation
Timer1 operates in Fast PWM Mode or Phase Correct PWM:
- Timer counts from 0 to TOP value
- Output pin set HIGH at BOTTOM
- Output pin cleared when counter matches OCR1A
- Cycle repeats at TOP value
For servos: TOP = 40000 (with prescaler 8), giving 20ms period
Servo Library Limitations
The Servo library can control up to 12 servos on most Arduino boards:
- Uses Timer1 which interferes with PWM on pins 9 & 10
- Each servo requires ~200 bytes of RAM
- Servo updates occur in background (interrupt-driven)
- Not all pins support servo control equally
Troubleshooting Common Issues
- Check Power: Servo needs 5V, 500mA+ during movement
- Verify Connections: Brown=GND, Red=5V, Orange=Signal
- Test Voltage: Measure at servo connector during movement
- Add Capacitor: 470µF across servo power lines
- Check Code: Ensure
servo.attach(pin)is called
- Mechanical Limits: Some servos don't reach full 180°
- Adjust Pulse Range:
servo.attach(pin, 1000, 2000) - Check for Obstructions: Servo horn hitting something
- Test with writeMicroseconds(): Try 1000-2000µs directly
- Servo Type: Some servos are 90° or 270° range
- Power Issue: Servo draws too much current
- Solution: Use external power supply for servo
- Add Decoupling: 100µF capacitor on Arduino 5V line
- Check USB Cable: Some cables can't deliver enough power
- Limit Current: Move servos one at a time
Video Demonstration
Servo Control Video Tutorial
Watch our detailed video walkthrough showing servo control in action!
Learn about proper connections, power considerations, and advanced techniques.
Watch on OLSPACE YouTubeWhat's Next?
Congratulations! You've mastered servo motor control. Here's what you've accomplished:
- ✓ Learned servo motor principles and PWM control
- ✓ Controlled servo position with
write()function - ✓ Created smooth sweeping motions
- ✓ Used potentiometer for analog position control
- ✓ Controlled multiple servos simultaneously
- ✓ Understood continuous rotation servos
- ✓ Learned about power requirements and safety
In the next lesson, you'll explore distance measurement:
Lesson 10: Ultrasonic Distance Sensor
Learn to measure distance using ultrasonic waves! Create parking sensors, object detection systems, and simple radar using the HC-SR04 ultrasonic sensor. Combine with servo motors to create scanning distance measurement systems.
Resources & Downloads
Download Complete Code
Get all the code files for this lesson: