Using Arduino to Control the Position, Speed, and Direction of a Micro Servo Motor
If you’ve ever dabbled in robotics, animatronics, or even just wanted to build a tiny robotic arm that waves at you, you’ve likely encountered the humble micro servo motor. These compact, affordable devices are the unsung heroes of countless DIY projects—from pan-tilt camera mounts to automatic pet feeders. But here’s the thing: most tutorials only show you how to sweep a servo back and forth at a fixed speed. What if you need precise, variable-speed control? Or you want to reverse direction mid-sweep without jitter? Or you’re trying to coordinate multiple servos for a smooth, synchronized motion?
In this deep dive, we’ll go far beyond the basics. You’ll learn how to use an Arduino (Uno, Nano, or any compatible board) to command a micro servo motor with granular control over its position, speed, and direction. We’ll break down the internal mechanics of these tiny motors, explore the limitations of standard libraries, and build custom solutions that give you professional-grade control. By the end, you’ll be able to program a micro servo to move like a well-oiled machine—smooth, responsive, and predictable.
Why Micro Servos Are Different (And Why You Should Care)
Before we dive into code, let’s talk hardware. A micro servo—like the ubiquitous SG90, MG90S, or the tiny 9g variants—isn’t just a miniature DC motor. It’s a complete closed-loop system: a DC motor, a gear train, a potentiometer (position feedback), and a control circuit all crammed into a plastic case weighing less than a quarter. The magic happens through Pulse Width Modulation (PWM) .
The 50 Hz Pulse Secret
Standard micro servos expect a 50 Hz control signal (a pulse every 20 milliseconds). The width of that pulse—typically between 1 ms and 2 ms—determines the shaft angle: - 1 ms → 0 degrees (usually full counterclockwise) - 1.5 ms → 90 degrees (center) - 2 ms → 180 degrees (full clockwise)
But here’s where it gets interesting: not all micro servos are created equal. Some cheap clones respond to a slightly different pulse range (e.g., 0.5 ms to 2.5 ms). And the dead band—the small range of pulse width changes that produce no movement—varies wildly. This is why you’ll see jitter when you try to hold a position with raw PWM.
The Hidden Limitation: Speed
Most Arduino servo libraries (like the built-in Servo.h) abstract away the PWM details. You just write myservo.write(90) and the servo moves to 90 degrees. But what about speed? The standard library doesn’t offer a speed parameter. It simply tells the servo to go to a position as fast as possible. That’s fine for simple tasks, but for realistic robotic movements—like a slow, deliberate head turn—you need incremental positioning.
That’s where our custom approach comes in.
Setting Up Your Hardware: Minimalist and Clean
You don’t need a breadboard jungle for this. Here’s the simplest setup:
Components List
- Arduino board (Uno, Nano, Leonardo, etc.)
- One micro servo (SG90 or similar)
- Jumper wires (female-to-male for direct connection)
- External 5V power supply (optional but recommended for multiple servos)
Wiring Diagram
- Servo Brown/Black wire → Arduino GND
- Servo Red wire → Arduino 5V (or external 5V for heavy loads)
- Servo Orange/Yellow wire → Arduino digital pin 9 (or any PWM-capable pin)
Pro tip: If you’re powering more than one micro servo from the Arduino’s 5V pin, you’ll likely brown out the board during high-torque movements. Use a separate 5V supply and connect the grounds together.
The Code: From Basic Sweep to Precision Control
We’ll start with the classic sweep to verify your wiring, then progressively add speed and direction control.
Step 1: The Basic Sweep (Verification)
cpp
include <Servo.h>
Servo myServo; int pos = 0;
void setup() { myServo.attach(9); // Attach to pin 9 }
void loop() { for (pos = 0; pos <= 180; pos += 1) { myServo.write(pos); delay(15); // Wait 15ms for servo to reach position } for (pos = 180; pos >= 0; pos -= 1) { myServo.write(pos); delay(15); } }
This works, but the movement is jerky and the speed is fixed by the delay(15). If you change the delay, you change the speed—but only crudely. The servo still moves at its maximum internal speed between each 1-degree step.
Step 2: Introducing Variable Speed with Incremental Steps
The key insight: to control speed, you control the time between small angular increments. Instead of jumping directly to the target position, you move in small steps with a controlled delay.
cpp
include <Servo.h>
Servo myServo; int currentAngle = 90; // Starting position int targetAngle = 90; int stepDelay = 20; // Milliseconds per degree (lower = faster)
void setup() { myServo.attach(9); myServo.write(currentAngle); Serial.begin(9600); }
void loop() { // Example: move to 0 degrees slowly, then to 180 quickly moveTo(0, 30); // 30ms per degree → slow delay(1000); moveTo(180, 5); // 5ms per degree → fast delay(1000); moveTo(90, 15); // Medium speed delay(1000); }
void moveTo(int target, int speedDelay) { if (target > currentAngle) { for (int i = currentAngle; i <= target; i++) { myServo.write(i); delay(speedDelay); } } else { for (int i = currentAngle; i >= target; i--) { myServo.write(i); delay(speedDelay); } } currentAngle = target; }
Now you have real speed control. But there’s a subtle issue: the delay() function blocks the entire Arduino. While the servo is moving, you can’t read sensors, update an OLED display, or handle serial commands. For non-blocking control, we need a different approach.
Step 3: Non-Blocking Speed Control (The Professional Way)
Instead of delay(), we use millis() to check the elapsed time. This allows the Arduino to multitask—essential for real-world projects.
cpp
include <Servo.h>
Servo myServo;
// State variables int currentAngle = 90; int targetAngle = 90; int stepDelay = 20; // ms per degree unsigned long lastMoveTime = 0; bool moving = false; int direction = 0; // 1 = increasing, -1 = decreasing
void setup() { myServo.attach(9); myServo.write(currentAngle); Serial.begin(9600); }
void loop() { // Example: update target every 3 seconds static unsigned long lastTargetChange = 0; if (millis() - lastTargetChange > 3000) { lastTargetChange = millis(); // Random target between 0 and 180 targetAngle = random(0, 181); stepDelay = random(5, 50); // Random speed Serial.print("New target: "); Serial.print(targetAngle); Serial.print(" at speed delay: "); Serial.println(stepDelay); startMoving(); }
// Non-blocking update updateServoPosition();
// You can do other stuff here, like reading a sensor }
void startMoving() { if (targetAngle != currentAngle) { moving = true; direction = (targetAngle > currentAngle) ? 1 : -1; lastMoveTime = millis(); } }
void updateServoPosition() { if (!moving) return;
if (millis() - lastMoveTime >= stepDelay) { lastMoveTime = millis(); currentAngle += direction;
// Write the new position myServo.write(currentAngle); // Check if we've reached the target if (direction == 1 && currentAngle >= targetAngle) { currentAngle = targetAngle; myServo.write(currentAngle); moving = false; } else if (direction == -1 && currentAngle <= targetAngle) { currentAngle = targetAngle; myServo.write(currentAngle); moving = false; } } }
This is a state machine for servo control. The updateServoPosition() function runs every loop iteration, but only updates the servo when enough time has passed. Meanwhile, the rest of your code (sensor reading, serial communication, etc.) runs freely.
Advanced Direction Control: Beyond Simple Forward/Reverse
Direction control isn’t just about moving clockwise or counterclockwise. In complex systems, you might need: - Instant direction reversal (e.g., a servo that oscillates like a metronome) - Direction-dependent speed profiles (fast one way, slow the other) - Holding position against external force (torque control)
Instant Reversal with Ramping
Here’s a function that reverses direction smoothly, without jerking:
cpp void reverseDirection() { // Store current position as the new target for reversal int reverseTarget = (direction == 1) ? currentAngle - 20 : currentAngle + 20; // Clamp to valid range reverseTarget = constrain(reverseTarget, 0, 180);
targetAngle = reverseTarget; startMoving();
// Wait until reversal is complete (non-blocking would be better in real code) while (moving) { updateServoPosition(); } }
Direction-Dependent Speed
Imagine a robotic arm that lifts slowly but drops quickly (gravity assist). You can modify the updateServoPosition() function to use different stepDelay values based on direction:
cpp void updateServoPosition() { if (!moving) return;
if (millis() - lastMoveTime >= stepDelay) { lastMoveTime = millis(); currentAngle += direction;
// Direction-dependent speed override if (direction == 1) { // Moving clockwise (example: slower) myServo.write(currentAngle); delayMicroseconds(500); // Extra fine-tuning } else { // Moving counterclockwise (example: faster) myServo.write(currentAngle); // No extra delay } // ... rest of the update logic } }
Real-World Optimization: Calibration and Smoothness
Micro servos are mechanical devices with tolerances. Here’s how to get buttery-smooth motion.
Calibrating the Pulse Width Range
Different servos have different min/max pulse widths. The Servo.h library assumes 544 to 2400 microseconds by default, but you can fine-tune:
cpp myServo.attach(9, 600, 2400); // Custom min/max pulse widths
To find your servo’s exact range, write a test sketch that sweeps from 0 to 180 using myServo.write(), and note where the servo actually stops moving. Adjust the pulse limits accordingly.
Eliminating Jitter with Smoothing
Jitter often comes from the servo’s internal controller struggling with rapid pulse changes. A simple fix: only send a new pulse when the position changes by at least 2 degrees.
cpp int lastWrittenAngle = -1;
void writeSmooth(int angle) { if (abs(angle - lastWrittenAngle) >= 2) { myServo.write(angle); lastWrittenAngle = angle; } }
Using the Servo’s Full Range (180° vs 270°)
Some micro servos (like the MG996R) can rotate more than 180 degrees with modified pulse ranges. But be careful: forcing a standard 180° servo beyond its limits can strip the gears. Always check the datasheet.
Putting It All Together: A Multi-Servo Motion Controller
Let’s combine position, speed, and direction control into a reusable class. This is the foundation for animatronics, robotic arms, or any multi-joint system.
cpp class SmartServo { private: Servo servo; int currentAngle; int targetAngle; int stepDelay; unsigned long lastMoveTime; bool moving; int direction; int pin;
public: SmartServo(int p) : pin(p), currentAngle(90), targetAngle(90), stepDelay(15), moving(false), direction(0) {}
void begin() { servo.attach(pin); servo.write(currentAngle); } void setTarget(int angle, int speed) { targetAngle = constrain(angle, 0, 180); stepDelay = constrain(speed, 1, 100); if (targetAngle != currentAngle) { moving = true; direction = (targetAngle > currentAngle) ? 1 : -1; lastMoveTime = millis(); } } void update() { if (!moving) return; if (millis() - lastMoveTime >= stepDelay) { lastMoveTime = millis(); currentAngle += direction; servo.write(currentAngle); if (direction == 1 && currentAngle >= targetAngle) { currentAngle = targetAngle; servo.write(currentAngle); moving = false; } else if (direction == -1 && currentAngle <= targetAngle) { currentAngle = targetAngle; servo.write(currentAngle); moving = false; } } } bool isMoving() { return moving; } int getCurrentAngle() { return currentAngle; } int getTargetAngle() { return targetAngle; } };
// Usage in setup/loop: SmartServo servo1(9); SmartServo servo2(10);
void setup() { servo1.begin(); servo2.begin(); }
void loop() { servo1.setTarget(45, 10); servo2.setTarget(135, 20);
while (servo1.isMoving() || servo2.isMoving()) { servo1.update(); servo2.update(); // Other tasks here } delay(2000); }
Troubleshooting Common Micro Servo Issues
Even with perfect code, hardware quirks can ruin your day. Here’s what to check:
Servo Twitching or Oscillating
- Cause: Power supply noise or insufficient current.
- Fix: Add a 470 µF electrolytic capacitor between 5V and GND near the servo. Use a separate power supply for multiple servos.
Servo Not Moving to Full Range
- Cause: Incorrect pulse width limits.
- Fix: Use
myServo.attach(pin, minPulse, maxPulse)with values from your calibration.
Servo Gets Hot
- Cause: Stall current (holding position under load) or incorrect PWM frequency.
- Fix: Reduce load, or add a heatsink. Never force a servo beyond its torque rating.
Servo Moves in Wrong Direction
- Cause: The
write()function maps 0-180 to 1-2 ms pulses, but your servo’s 0 position might be the opposite end. - Fix: Reverse the mapping by swapping min and max pulse values:
myServo.attach(9, 2400, 600).
Beyond the Basics: What’s Next?
Now that you can control a single micro servo with precision, consider these extensions:
- Acceleration/Deceleration Profiles: Instead of constant speed, add acceleration ramps for ultra-smooth starts and stops. Use an easing function (e.g., cubic ease-in-out) to calculate the step delay dynamically.
- Position Feedback: Some micro servos have a feedback wire (white or yellow) that outputs the potentiometer voltage. Read it with
analogRead()to verify actual position. - I2C Servo Controllers: For large projects, offload servo timing to a PCA9685 module. It handles 16 servos with no CPU overhead.
- Wireless Control: Add an HC-05 Bluetooth module or ESP8266 to control your servos from a smartphone app.
The micro servo is a gateway to physical computing. With the techniques you’ve learned here—non-blocking control, variable speed, direction management, and calibration—you’re no longer limited to simple sweeps. You can choreograph complex, lifelike movements that bring your projects to life.
So go ahead. Hook up that servo, load the code, and watch it move with intention. Then start experimenting. Change the speed mid-motion. Reverse direction on a sensor trigger. Link multiple servos in a sequence. The only limit is the number of PWM pins on your Arduino—and your imagination.
Copyright Statement:
Author: Micro Servo Motor
Source: Micro Servo Motor
The copyright of this article belongs to the author. Reproduction is not allowed without permission.
Recommended Blog
- How to Connect a Micro Servo Motor to Arduino MKR IoT Bundle
- How to Connect a Micro Servo Motor to Arduino MKR WAN 1300
- How to Connect a Micro Servo Motor to Arduino MKR NB 1500
- How to Connect a Micro Servo Motor to Arduino MKR1000
- Using Arduino to Control the Rotation Angle, Speed, and Direction of a Micro Servo Motor
- Creating a Servo-Controlled Automated Pet Feeder with Arduino
- How to Connect a Micro Servo Motor to Arduino Nano
- Troubleshooting Common Issues When Connecting Micro Servos to Arduino
- How to Connect a Micro Servo Motor to Arduino MKR WAN 1310
- Building a Servo-Controlled Automated Pet Feeder with Arduino
About Us
- Lucas Bennett
- Welcome to my blog!
Hot Blog
- How to Build a Remote-Controlled Car with a 3D-Printed Chassis
- The Impact of Gear Materials on Servo Motor Heat Generation
- How Gear Teeth Design Influences Servo Motor Operation
- Vector's Micro Servo Motors: Compact and Lightweight for Pan-Tilt Systems
- Best Micro Servo Motors for DIY Electronics Projects
- Operating Voltage Ranges for Micro Servos Explained
- The Best Micro Servo Motors for Arduino Projects: Brand Recommendations
- Using Raspberry Pi to Control Servo Motors in Automated Packaging and Labeling Systems
- Advances in Acoustic Management for Micro Servo Motors
- Micro Servo Motor Explained: A Simple Guide for Students
Latest Blog
- Using Arduino to Control the Position, Speed, and Direction of a Micro Servo Motor
- Comparing Micro Servo Brands for Robotics Projects
- Using a Joystick to Control Your Micro Servo Robotic Arm
- Best Practices for Testing Micro Servos Before Drone Integration
- Building Your First Remote-Controlled Car: A Beginner's Guide
- The Role of Voltage and Current in Motor Torque and Speed
- Micro Servo vs Standard Servo: Signal Noise Sensitivity
- The Role of Micro Servo Motors in Smart Grids
- How to Connect a Micro Servo Motor to Arduino MKR IoT Bundle
- Brushless vs Brushed Micro Servos for Long-Lasting RC Boat Use
- How to Clean and Maintain Your RC Car's Motor
- Understanding the Basics of Control Circuit Design
- Best Micro Servo Motors for DIY Electronics Projects
- The Importance of Signal Integrity in PCB Design
- How to Prevent Binding in RC Car Steering with Micro Servos
- Micro Servo Motors in Smart Healthcare Systems: Enhancing Patient Care
- Voltage Drop at Wire Leads: Spec vs Real-World Conditions
- How to Calibrate Servo Motors for Precise Control with Raspberry Pi
- Micro Servo Motor Gear Types: Plastic vs Metal Gears
- Smart Micro Servo Motors: The Next Generation of Automation