Using Arduino to Control the Angle, Speed, and Direction of a Micro Servo Motor

How to Connect a Micro Servo Motor to Arduino / Visits:9

Micro servo motors are the unsung heroes of countless DIY electronics projects, robotics builds, and automation hacks. These tiny, lightweight actuators pack a surprising amount of torque and precision into a package smaller than your thumb. Whether you’re building a robotic arm, a camera gimbal, a model airplane control surface, or a simple animatronic prop, understanding how to control a micro servo motor with an Arduino is a foundational skill that opens up endless creative possibilities.

In this deep dive, we’ll go far beyond the basic “sweep” sketch. You’ll learn not just how to set an angle, but how to precisely control the speed of rotation, change direction on the fly, and handle multiple servos simultaneously. We’ll cover the hardware essentials, the pulse-width modulation (PWM) theory behind servo control, and provide multiple code examples that you can adapt for your own projects. By the end, you’ll have a complete toolkit for commanding micro servos with confidence.

Why Micro Servos? A Quick Look at the Hardware

Before we jump into code, let’s appreciate what makes a micro servo unique. The most common models, like the TowerPro SG90 or MG90S, weigh around 9 grams and can produce about 1.5 to 2 kg·cm of torque. That’s enough to lift a small object or rotate a lightweight mechanism, but not enough to break a finger.

Anatomy of a Micro Servo

  • DC Motor: The core that spins, but lacks positional feedback on its own.
  • Gear Train: Reduces the high speed of the DC motor into high torque, low-speed rotation.
  • Potentiometer: Connected to the output shaft, it provides continuous position feedback.
  • Control Circuit: Compares the desired position (from the PWM signal) with the actual position (from the potentiometer) and drives the motor to correct any error.

This closed-loop system is what makes servos so easy to use. You don’t need to count steps or worry about missed movements—just send a standard 50 Hz PWM signal with a pulse width between 1 ms and 2 ms, and the servo will hold that position against moderate external forces.

Power Considerations

Micro servos typically operate at 4.8V to 6V. When idle, they draw very little current, but under load or during rapid movement, they can spike to 500 mA or more. Never power a micro servo directly from the Arduino’s 5V pin if you’re running more than one servo or expecting significant torque. The onboard voltage regulator can overheat and fail. Instead, use a separate 5V power supply (like a UBEC or a battery pack) and connect the servo’s power and ground to that supply, with the Arduino’s ground tied to the same ground. The signal line from the Arduino to the servo carries only logic-level current and is perfectly safe.

The Core Concept: PWM and Servo Signal Timing

All micro servos use the same fundamental signaling protocol, though the exact pulse width range can vary slightly between manufacturers. The standard is:

  • Pulse width of 1.0 ms → 0° (full counterclockwise)
  • Pulse width of 1.5 ms → 90° (center)
  • Pulse width of 2.0 ms → 180° (full clockwise)

These pulses are sent every 20 ms (50 Hz). The servo’s internal circuit measures the width of each pulse and compares it to the potentiometer’s voltage. If they don’t match, it drives the motor to reduce the error. This happens continuously, so the servo actively maintains its position.

Why Not Just Use analogWrite()?

The Arduino’s built-in analogWrite() function generates a PWM signal at approximately 490 Hz or 980 Hz (depending on the pin), which is far too fast for servo control. The servo expects a 50 Hz signal, and the pulse width must be precisely controlled. Using analogWrite() would either not work at all or cause erratic behavior.

Instead, we use either the Servo.h library (which handles the timing in software) or direct register manipulation for more advanced control.

Setting Up Your First Micro Servo Control

Let’s start with the simplest possible circuit and code. You’ll need:

  • An Arduino (Uno, Nano, Mega, etc.)
  • A micro servo (SG90 or similar)
  • A breadboard and jumper wires
  • An external 5V power supply (optional but recommended)

Wiring Diagram

  1. Servo Brown wire → GND (connect to both Arduino GND and external supply GND)
  2. Servo Red wire → 5V (from external supply; if using Arduino power, connect to 5V pin)
  3. Servo Orange/Yellow wire → Digital pin 9 on Arduino

If you’re using an external supply, connect its positive to the servo red wire and its negative to both the servo brown wire and the Arduino GND. This common ground is critical—without it, the signal has no reference.

Basic Angle Control with Servo.h

cpp

include <Servo.h>

Servo myServo; // Create a servo object

void setup() { myServo.attach(9); // Attach the servo to pin 9 myServo.write(90); // Move to center position (90°) }

void loop() { // Sweep from 0 to 180 degrees for (int angle = 0; angle <= 180; angle += 1) { myServo.write(angle); delay(15); // Wait 15 ms for the servo to reach the position }

// Sweep back from 180 to 0 degrees for (int angle = 180; angle >= 0; angle -= 1) { myServo.write(angle); delay(15); } }

This is the classic “sweep” sketch. It works, but the delay is fixed and the movement speed is not directly controlled—it’s merely a consequence of how quickly we change the angle. If you increase the delay, the servo moves slower. If you decrease it, the servo moves faster, but you risk overwhelming the servo’s ability to keep up.

Controlling Speed: The Art of Incremental Movement

The write() function tells the servo to go to a specific angle immediately. The servo will attempt to get there as fast as its motor and gearing allow. To control speed, we need to send a sequence of intermediate positions, with each step separated by a controlled delay. This is essentially what we did in the sweep sketch, but we can make it more elegant and reusable.

Speed Control Function

cpp

include <Servo.h>

Servo myServo;

int currentAngle = 90; // Store the current position

void setup() { myServo.attach(9); myServo.write(currentAngle); Serial.begin(9600); }

void loop() { // Move to 0° at slow speed moveToAngle(0, 30); // 30 ms per degree → about 6 seconds for 180° delay(1000);

// Move to 180° at medium speed moveToAngle(180, 10); // 10 ms per degree → about 1.8 seconds delay(1000);

// Move to 90° at fast speed moveToAngle(90, 3); // 3 ms per degree → about 0.54 seconds delay(2000); }

void moveToAngle(int targetAngle, int delayPerDegree) { // Determine direction if (targetAngle > currentAngle) { // Moving clockwise for (int angle = currentAngle; angle <= targetAngle; angle += 1) { myServo.write(angle); delay(delayPerDegree); } } else { // Moving counterclockwise for (int angle = currentAngle; angle >= targetAngle; angle -= 1) { myServo.write(angle); delay(delayPerDegree); } } currentAngle = targetAngle; // Update stored position }

This approach works well for many applications, but it has a limitation: the delay() function blocks the entire Arduino. While the servo is moving, you can’t read sensors, update an LCD, or check for button presses. For non-blocking speed control, we need to use millis() or a timer-based approach.

Non-Blocking Speed Control Using millis()

cpp

include <Servo.h>

Servo myServo;

int currentAngle = 90; int targetAngle = 90; int delayPerDegree = 10; // milliseconds per degree unsigned long lastMoveTime = 0;

void setup() { myServo.attach(9); myServo.write(currentAngle); }

void loop() { // Example: change target based on time or sensor // Here we just toggle between 0 and 180 every 5 seconds static unsigned long lastToggle = 0; if (millis() - lastToggle > 5000) { lastToggle = millis(); targetAngle = (targetAngle == 0) ? 180 : 0; }

// Move one step toward target if enough time has passed if (millis() - lastMoveTime >= delayPerDegree) { lastMoveTime = millis();

if (currentAngle < targetAngle) {   currentAngle++;   myServo.write(currentAngle); } else if (currentAngle > targetAngle) {   currentAngle--;   myServo.write(currentAngle); } // If currentAngle == targetAngle, do nothing 

}

// Other non-blocking tasks can go here // readSensor(); // updateDisplay(); }

Now your Arduino can move the servo smoothly while simultaneously handling other tasks. The speed is determined by delayPerDegree—a smaller value means faster movement.

Direction Control: Beyond Simple CW/CCW

Direction control is inherently tied to angle. Setting a target angle automatically determines the direction (clockwise or counterclockwise). But what if you want to reverse direction mid-motion? Or what if you want to control the servo like a continuous rotation motor?

Standard Servo: Reversing Mid-Motion

To reverse direction, you simply change the target angle to a value on the opposite side of the current position. In the non-blocking example above, if you change targetAngle from 180 to 0 while the servo is still moving toward 180, the code will immediately start decrementing currentAngle toward 0. The servo will reverse direction smoothly, though there may be a slight mechanical backlash.

Continuous Rotation Servos: A Special Case

Some micro servos are modified for continuous rotation (often called “360° servos” or “continuous rotation servos”). These have the mechanical stop removed and the potentiometer replaced with a fixed resistor network. With these, the PWM signal no longer controls position but rather speed and direction:

  • 1.0 ms pulse → Full speed clockwise
  • 1.5 ms pulse → Stop
  • 2.0 ms pulse → Full speed counterclockwise

You can control these with the Servo.h library by using writeMicroseconds() instead of write():

cpp

include <Servo.h>

Servo myServo;

void setup() { myServo.attach(9); myServo.writeMicroseconds(1500); // Stop delay(1000); }

void loop() { // Spin clockwise at medium speed myServo.writeMicroseconds(1300); // Between stop and full CW delay(2000);

// Stop myServo.writeMicroseconds(1500); delay(1000);

// Spin counterclockwise at full speed myServo.writeMicroseconds(1700); delay(2000);

// Stop myServo.writeMicroseconds(1500); delay(1000); }

The exact values for “stop” and “full speed” vary by servo. You may need to calibrate by sending different pulse widths and observing the behavior. Some continuous rotation servos have a trim pot that lets you adjust the stop point.

Advanced Techniques: Multiple Servos and Smooth Motion Profiles

Controlling Multiple Micro Servos

The Servo.h library can control up to 12 servos on an Uno (and more on a Mega). Each servo object uses its own timer, and the library manages the PWM generation in the background.

cpp

include <Servo.h>

Servo servo1; Servo servo2; Servo servo3;

void setup() { servo1.attach(9); servo2.attach(10); servo3.attach(11);

servo1.write(0); servo2.write(90); servo3.write(180); }

void loop() { // Move all three to new positions with different speeds // Using non-blocking approach would be better for real projects

for (int pos = 0; pos <= 180; pos += 1) { servo1.write(pos); servo2.write(180 - pos); // Mirror movement servo3.write(pos / 2); // Half range delay(10); }

delay(1000);

for (int pos = 180; pos >= 0; pos -= 1) { servo1.write(pos); servo2.write(180 - pos); servo3.write(pos / 2); delay(10); } }

Warning: When controlling multiple servos simultaneously, the power draw adds up. A single SG90 can draw 200-300 mA under load. Three servos moving at once could draw nearly 1A. Always use an external power supply rated for the total current.

Smooth Motion with Acceleration and Deceleration

Simple linear speed control works, but real-world motion often benefits from acceleration and deceleration profiles. A servo that starts and stops instantly can cause mechanical stress and jerky movements. We can implement a simple trapezoidal speed profile.

cpp

include <Servo.h>

Servo myServo;

int currentAngle = 90; int targetAngle = 90; int speed = 0; int maxSpeed = 15; // Maximum ms per degree step int minSpeed = 3; // Minimum ms per degree step (fastest) int acceleration = 1; // How much speed changes per step unsigned long lastMoveTime = 0;

void setup() { myServo.attach(9); myServo.write(currentAngle); }

void loop() { // Change target periodically static unsigned long lastToggle = 0; if (millis() - lastToggle > 4000) { lastToggle = millis(); targetAngle = (targetAngle == 20) ? 160 : 20; }

// Non-blocking movement with acceleration if (millis() - lastMoveTime >= speed) { lastMoveTime = millis();

if (currentAngle < targetAngle) {   currentAngle++;   // Accelerate as we move away from start   if (speed > minSpeed) {     speed -= acceleration;   } } else if (currentAngle > targetAngle) {   currentAngle--;   if (speed > minSpeed) {     speed -= acceleration;   } } else {   // At target, decelerate gradually (optional)   if (speed < maxSpeed) {     speed += acceleration;   } }  myServo.write(currentAngle); 

} }

This creates a motion that starts slow, speeds up, and then (if we added logic to detect approaching the target) slows down again. For a full trapezoidal profile, you’d need to calculate the halfway point and begin decelerating.

Fine-Tuning with writeMicroseconds()

The write() function accepts angles from 0 to 180 and internally maps them to pulse widths. But not all servos have the exact same range. Some might respond to 0.5 ms to 2.5 ms, giving you up to 270° of rotation. Others might have a narrower range. To get the most out of your micro servo, use writeMicroseconds() to send raw pulse widths.

cpp

include <Servo.h>

Servo myServo;

void setup() { myServo.attach(9); Serial.begin(9600); Serial.println("Testing pulse width range..."); }

void loop() { // Test the full range that your servo actually supports // Start with standard values, then adjust

myServo.writeMicroseconds(1000); // Should be ~0° delay(2000);

myServo.writeMicroseconds(1500); // Should be ~90° delay(2000);

myServo.writeMicroseconds(2000); // Should be ~180° delay(2000);

// Try extended range if your servo supports it myServo.writeMicroseconds(600); // Might go beyond 0° delay(2000);

myServo.writeMicroseconds(2400); // Might go beyond 180° delay(2000); }

Observe the servo’s behavior. If it buzzes or doesn’t move when you send 1000 µs, try 700 µs. If it reaches a hard stop before 2000 µs, reduce the upper limit. Some micro servos can rotate 210° or more with the right pulse widths.

Common Pitfalls and How to Avoid Them

Jittery Movement

If your servo twitches or vibrates, the most common causes are:

  • Insufficient power: The servo is drawing more current than the supply can provide. Use a separate 5V supply with at least 1A capacity.
  • Noisy signal lines: Long wires between the Arduino and servo can pick up interference. Keep signal wires short (under 20 cm if possible) and use twisted pairs.
  • Incorrect ground: The servo ground and Arduino ground must be connected. Without a common ground, the signal voltage has no reference.

Servo Not Moving at All

  • Check wiring: Brown to GND, Red to 5V, Signal to a PWM-capable pin (marked with ~ on Arduino boards).
  • Verify the pin number in attach() matches your wiring.
  • Make sure the servo is not mechanically jammed. Try rotating the horn gently by hand (with power off).
  • Test with a different servo to rule out a dead unit.

Overheating

If your micro servo gets hot, it’s likely stalled or under excessive load. A stalled servo draws maximum current and can burn out its motor or control circuit. Ensure the servo’s range of motion is not mechanically blocked. Also, avoid commanding the servo to hold a position near its mechanical limits for extended periods.

Real-World Application: A Two-Servo Pan-Tilt Mechanism

Let’s put everything together with a practical example. A pan-tilt camera mount uses two micro servos—one for horizontal (pan) and one for vertical (tilt). We’ll control both with speed and direction, using a non-blocking approach.

cpp

include <Servo.h>

Servo panServo; Servo tiltServo;

// Pan variables int panCurrent = 90; int panTarget = 90; int panSpeed = 8; // ms per degree unsigned long panLastMove = 0;

// Tilt variables int tiltCurrent = 90; int tiltTarget = 90; int tiltSpeed = 10; unsigned long tiltLastMove = 0;

void setup() { panServo.attach(9); tiltServo.attach(10);

panServo.write(panCurrent); tiltServo.write(tiltCurrent);

Serial.begin(9600); Serial.println("Pan-Tilt Ready"); }

void loop() { // Simulate random target changes every 3 seconds static unsigned long lastChange = 0; if (millis() - lastChange > 3000) { lastChange = millis(); panTarget = random(30, 151); // 30° to 150° tiltTarget = random(45, 136); // 45° to 135° Serial.print("New targets - Pan: "); Serial.print(panTarget); Serial.print(" Tilt: "); Serial.println(tiltTarget); }

// Move pan servo if (millis() - panLastMove >= panSpeed) { panLastMove = millis(); if (panCurrent < panTarget) { panCurrent++; panServo.write(panCurrent); } else if (panCurrent > panTarget) { panCurrent--; panServo.write(panCurrent); } }

// Move tilt servo if (millis() - tiltLastMove >= tiltSpeed) { tiltLastMove = millis(); if (tiltCurrent < tiltTarget) { tiltCurrent++; tiltServo.write(tiltCurrent); } else if (tiltCurrent > tiltTarget) { tiltCurrent--; tiltServo.write(tiltCurrent); } }

// You could add sensor reading or serial commands here }

This example demonstrates independent speed control for two servos. You can adjust panSpeed and tiltSpeed to make one axis move faster than the other. The random() function generates new targets, but in a real project, you’d replace that with joystick input, face tracking, or pre-programmed sequences.

Final Thoughts on Micro Servo Control

Mastering micro servo control with Arduino is about more than just copying a library example. It’s understanding the timing, the power requirements, and the limits of the hardware. Once you can control angle, speed, and direction independently, and do it without blocking your main program loop, you have a powerful tool for robotics, animatronics, and automation.

The techniques shown here—incremental movement, non-blocking control, acceleration profiles, and raw pulse width manipulation—apply to any servo, from the tiniest 3.7g micro servo to larger hobby servos. The only difference is the power budget and the mechanical load.

Experiment with different delay values, try adding a potentiometer to control speed in real-time, or build a multi-axis robot arm. The micro servo is a gateway component. Once you can command it precisely, you can make almost anything move.

Copyright Statement:

Author: Micro Servo Motor

Link: https://microservomotor.com/how-to-connect-a-micro-servo-motor-to-arduino/control-micro-servo-angle-speed-direction-arduino.htm

Source: Micro Servo Motor

The copyright of this article belongs to the author. Reproduction is not allowed without permission.

About Us

Lucas Bennett avatar
Lucas Bennett
Welcome to my blog!

Tags