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

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

Micro servo motors are the unsung heroes of countless DIY electronics projects. From robotic arms and animatronic eyes to camera gimbals and model airplanes, these tiny but powerful actuators give life to static creations. If you are just starting with Arduino or looking to refine your servo control skills, this guide will walk you through everything you need to know about controlling the angle, speed, and direction of a micro servo motor.

What Exactly Is a Micro Servo Motor?

Before diving into code and wiring, it helps to understand what makes a micro servo special. Unlike a standard DC motor that spins continuously, a micro servo is a closed-loop system. It consists of a small DC motor, a set of reduction gears, a potentiometer (or a magnetic encoder in higher-end models), and a control circuit.

The potentiometer provides real-time position feedback. When you send a command to move to a specific angle, the control circuit compares the current position (from the potentiometer) with the desired position and drives the motor until they match. This feedback loop is what allows precise angular control.

Common Micro Servo Specifications

  • Operating Voltage: Most micro servos (like the popular SG90 or MG90S) run on 4.8V to 6.0V. 5V from an Arduino is typically fine.
  • Stall Torque: Usually around 1.0 to 1.8 kg·cm at 4.8V. Enough for light loads, not for heavy lifting.
  • Operating Speed: Typically 0.10 to 0.12 seconds per 60 degrees at 4.8V. This is important when we talk about speed control later.
  • Rotation Range: Standard servos rotate 0 to 180 degrees. Some modified "continuous rotation" servos exist, but for angle control, stick with standard ones.

Hardware Setup: Wiring Your Micro Servo to Arduino

Wiring a micro servo is deceptively simple, but a single mistake can fry your servo or your Arduino. Let me break it down clearly.

Pin Identification

A typical micro servo has three wires:

  • Brown or Black Wire: Ground (GND). Connect to Arduino GND.
  • Red Wire: Power (VCC). Connect to 5V pin on Arduino.
  • Orange, Yellow, or White Wire: Signal (PWM). Connect to a PWM-capable digital pin (e.g., pin 9 on Arduino Uno).

The Power Problem

Here is a critical point many beginners miss: do not power a micro servo directly from the Arduino 5V pin if you are running more than one servo or applying any load. The Arduino's onboard voltage regulator can only supply about 500mA. A single micro servo under load can draw 200-300mA. Two servos can easily exceed the limit, causing the Arduino to reset or behave erratically.

For a single servo doing light work (like a pointer or a small flap), the Arduino 5V pin is acceptable. For anything more, use an external 5V power supply (like a battery pack or a regulated wall adapter) and connect the servo power wires directly to that supply. Connect all grounds (Arduino ground and external supply ground) together.

Wiring Diagram (Text Version)

Arduino Uno Micro Servo ----------------- ----------- 5V (Pin) ---> Red wire GND (Pin) ---> Brown wire Pin 9 (PWM) ---> Orange wire

If using external power:

External 5V Supply Micro Servo ------------------ ----------- VCC (positive) ---> Red wire GND (negative) ---> Brown wire

Arduino Uno

GND (Pin) ---> Connect to external supply GND Pin 9 (PWM) ---> Orange wire

Basic Angle Control: The Servo.h Library

Arduino makes servo control remarkably easy with the built-in Servo.h library. This library abstracts away the low-level PWM generation and provides a simple interface.

How Servo Control Works Under the Hood

Servo motors use a specific type of PWM signal. The control pulse is sent every 20 milliseconds (50 Hz). The width of the pulse determines the angle:

  • 1 ms (1000 microseconds): Usually corresponds to 0 degrees.
  • 1.5 ms (1500 microseconds): Usually corresponds to 90 degrees (center).
  • 2 ms (2000 microseconds): Usually corresponds to 180 degrees.

Different servo brands may have slightly different pulse ranges. The Servo.h library handles this, but you can fine-tune with the writeMicroseconds() function if needed.

Your First Sketch: Sweep Back and Forth

cpp

include <Servo.h>

Servo myServo; // Create servo object

int pos = 0; // Variable to store servo position

void setup() { myServo.attach(9); // Attach servo on pin 9 }

void loop() { for (pos = 0; pos <= 180; pos += 1) { // Goes from 0 to 180 degrees myServo.write(pos); // Tell servo to go to position delay(15); // Wait 15ms for servo to reach position } for (pos = 180; pos >= 0; pos -= 1) { // Goes from 180 to 0 degrees myServo.write(pos); delay(15); } }

This code will make your servo sweep smoothly from 0 to 180 degrees and back. The delay(15) is a simple way to control the speed, but it is not very precise. We will improve that later.

Fine-Tuning with writeMicroseconds()

Sometimes a servo does not respond exactly to the 0-180 range. You might find that 0 degrees is actually 5 degrees, or 180 is only 175. You can adjust this by using writeMicroseconds().

cpp myServo.writeMicroseconds(1000); // Should be 0 degrees myServo.writeMicroseconds(1500); // Should be 90 degrees myServo.writeMicroseconds(2000); // Should be 180 degrees

If your servo overshoots or undershoots, adjust these values. For example, if 1000µs gives 5 degrees, try 950µs. If 2000µs gives only 175 degrees, try 2050µs. Be careful not to exceed the mechanical limits of your servo—forcing it past its stop can strip the gears.

Controlling Speed: Beyond Simple Delays

The delay() function in the sweep example is a crude way to control speed. It works, but it blocks the entire Arduino, preventing it from doing anything else. More importantly, it does not give you precise control over the actual angular velocity.

Understanding Servo Speed Limitations

Micro servos have a maximum speed. For an SG90, that is roughly 0.12 seconds per 60 degrees. That translates to about 500 degrees per second. You cannot make a servo move faster than its mechanical limit. You can only make it move slower.

Non-Blocking Speed Control Using millis()

Instead of using delay(), we can use millis() to create a non-blocking timer that moves the servo in steps. This allows the Arduino to perform other tasks while the servo is moving.

cpp

include <Servo.h>

Servo myServo;

int targetAngle = 0; int currentAngle = 0; int stepSize = 1; // Degrees to move per step unsigned long lastMoveTime = 0; int moveInterval = 20; // Milliseconds between steps

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

void loop() { // Every 20ms, move one step toward target if (millis() - lastMoveTime >= moveInterval) { lastMoveTime = millis();

if (currentAngle < targetAngle) {   currentAngle += stepSize;   if (currentAngle > targetAngle) currentAngle = targetAngle; } else if (currentAngle > targetAngle) {   currentAngle -= stepSize;   if (currentAngle < targetAngle) currentAngle = targetAngle; }  myServo.write(currentAngle); 

}

// Change target occasionally (simulating other tasks) static unsigned long lastTargetChange = 0; if (millis() - lastTargetChange >= 3000) { lastTargetChange = millis(); targetAngle = random(0, 181); // New random target every 3 seconds }

// Other non-servo tasks can go here // read sensors, update display, etc. }

Calculating Speed from Step Size and Interval

The actual speed of the servo in this code is:

Speed (degrees/second) = stepSize / (moveInterval / 1000)

With stepSize = 1 and moveInterval = 20, the speed is 1 / 0.02 = 50 degrees per second. To make it faster, increase stepSize or decrease moveInterval. To make it slower, do the opposite.

Smooth Acceleration and Deceleration

For applications like robotic arms, sudden starts and stops can cause jerky motion or even damage. Implementing acceleration and deceleration makes movement look natural and reduces mechanical stress.

cpp

include <Servo.h>

Servo myServo;

int targetAngle = 0; int currentAngle = 0; int speed = 0; // Current speed in degrees per step int maxSpeed = 5; // Maximum step size int acceleration = 1; // How much speed changes per update unsigned long lastMoveTime = 0; int moveInterval = 15;

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

void loop() { if (millis() - lastMoveTime >= moveInterval) { lastMoveTime = millis();

int distance = targetAngle - currentAngle;  if (abs(distance) > 0) {   // Accelerate if we are far from target, decelerate as we approach   if (abs(distance) > 10) {     if (speed < maxSpeed) speed += acceleration;   } else {     if (speed > 1) speed -= acceleration;     if (speed < 1) speed = 1; // Minimum speed to keep moving   }    // Move toward target   if (distance > 0) {     currentAngle += speed;     if (currentAngle > targetAngle) currentAngle = targetAngle;   } else {     currentAngle -= speed;     if (currentAngle < targetAngle) currentAngle = targetAngle;   }    myServo.write(currentAngle); } else {   speed = 0; // Stop when we reach target } 

}

// Change target periodically static unsigned long lastChange = 0; if (millis() - lastChange >= 4000) { lastChange = millis(); targetAngle = random(30, 151); } }

This creates a smooth, natural motion profile. The servo accelerates from a stop, cruises at maximum speed, then decelerates as it approaches the target.

Controlling Direction: It Is Simpler Than You Think

Direction control for a standard micro servo is inherent in the angle command. To move clockwise, you increase the angle. To move counterclockwise, you decrease it.

Direction as a State Machine

If you want to implement explicit direction control (like "go left" or "go right"), you can wrap the servo control in a simple state machine.

cpp enum Direction { STOP, CLOCKWISE, COUNTERCLOCKWISE };

Direction currentDir = STOP; int currentAngle = 90; // Start at center

void setDirection(Direction dir) { currentDir = dir; }

void updateServoBasedOnDirection() { static unsigned long lastUpdate = 0; int stepInterval = 30; // ms between steps

if (millis() - lastUpdate >= stepInterval) { lastUpdate = millis();

switch (currentDir) {   case CLOCKWISE:     if (currentAngle < 180) {       currentAngle++;       myServo.write(currentAngle);     }     break;    case COUNTERCLOCKWISE:     if (currentAngle > 0) {       currentAngle--;       myServo.write(currentAngle);     }     break;    case STOP:     // Do nothing, servo holds position     break; } 

} }

This pattern is useful for joystick-controlled projects or autonomous systems where you want to issue high-level direction commands rather than absolute angles.

Reverse Direction by Inverting the Angle

If your mechanical setup requires reversed direction (e.g., the servo is mounted upside down), you can simply map the angle inversely:

cpp int reverseAngle(int angle) { return 180 - angle; // 0 becomes 180, 180 becomes 0 }

myServo.write(reverseAngle(targetAngle));

Advanced Techniques: Continuous Rotation Servos and Custom PWM

Continuous Rotation Servos

Some micro servos are modified for continuous rotation. These ignore the angle command and instead use the pulse width to control speed and direction:

  • 1500 µs: Stop
  • 1000 µs: Full speed clockwise
  • 2000 µs: Full speed counterclockwise

Values in between give proportional speed. This is essentially a geared DC motor with a built-in driver, but it is not suitable for angle control.

Generating Custom PWM Without the Library

If you want to control multiple servos or need more precise timing, you can generate the PWM signal manually using analogWrite() on a 16-bit timer (like Timer1 on the Arduino Uno). This is more advanced but gives you full control.

cpp // Pseudo-code for custom 50Hz PWM on pin 9 using Timer1 // This requires direct register manipulation // Not recommended for beginners, but powerful for advanced users

void setup() { pinMode(9, OUTPUT); // Configure Timer1 for 50Hz, 16-bit resolution // Set OCR1A for desired pulse width }

I will not dive deep into register-level programming here, but it is worth knowing that the Servo.h library does exactly this under the hood.

Practical Project Example: Pan-and-Tilt Camera Mount

Let me tie everything together with a practical project: a pan-and-tilt camera mount using two micro servos.

Hardware List

  • 2x Micro servos (SG90 or similar)
  • 1x Arduino Uno
  • 1x Pan-tilt bracket kit (cheap on Amazon or eBay)
  • 1x Small camera or smartphone (if bracket supports it)
  • 1x External 5V power supply (2A minimum)
  • Jumper wires

Wiring

Servo 1 (Pan - left/right) Signal -> Pin 9 Power -> External 5V GND -> External GND

Servo 2 (Tilt - up/down) Signal -> Pin 10 Power -> External 5V GND -> External GND

Arduino GND -> External GND

Code: Combined Angle, Speed, and Direction Control

cpp

include <Servo.h>

Servo panServo; Servo tiltServo;

int panAngle = 90; int tiltAngle = 90;

int panTarget = 90; int tiltTarget = 90;

int panSpeed = 2; // Step size per update int tiltSpeed = 2;

unsigned long lastUpdate = 0; int updateInterval = 15; // ms

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

panServo.write(panAngle); tiltServo.write(tiltAngle);

Serial.begin(9600); Serial.println("Enter commands: p[angle] t[angle]"); Serial.println("Example: p45 t60"); }

void loop() { // Handle serial commands for target setting if (Serial.available() > 0) { String cmd = Serial.readStringUntil('\n'); cmd.trim();

if (cmd.startsWith("p")) {   int val = cmd.substring(1).toInt();   panTarget = constrain(val, 0, 180); } else if (cmd.startsWith("t")) {   int val = cmd.substring(1).toInt();   tiltTarget = constrain(val, 0, 180); } 

}

// Non-blocking movement if (millis() - lastUpdate >= updateInterval) { lastUpdate = millis();

// Move pan toward target if (panAngle < panTarget) {   panAngle += panSpeed;   if (panAngle > panTarget) panAngle = panTarget; } else if (panAngle > panTarget) {   panAngle -= panSpeed;   if (panAngle < panTarget) panAngle = panTarget; }  // Move tilt toward target if (tiltAngle < tiltTarget) {   tiltAngle += tiltSpeed;   if (tiltAngle > tiltTarget) tiltAngle = tiltTarget; } else if (tiltAngle > tiltTarget) {   tiltAngle -= tiltSpeed;   if (tiltAngle < tiltTarget) tiltAngle = tiltTarget; }  panServo.write(panAngle); tiltServo.write(tiltAngle); 

} }

You can control this via the Serial Monitor. Type p45 t60 and the camera will pan to 45 degrees and tilt to 60 degrees at a controlled speed.

Adding Directional Commands

For joystick or button control, modify the code to use direction states:

cpp enum PanDir { PANSTOP, PANLEFT, PANRIGHT }; enum TiltDir { TILTSTOP, TILTUP, TILTDOWN };

PanDir panDir = PANSTOP; TiltDir tiltDir = TILTSTOP;

void setPanDirection(PanDir dir) { panDir = dir; } void setTiltDirection(TiltDir dir) { tiltDir = dir; }

// In the update loop: void moveByDirection() { switch (panDir) { case PANLEFT: if (panAngle > 0) panAngle -= panSpeed; break; case PANRIGHT: if (panAngle < 180) panAngle += panSpeed; break; case PAN_STOP: break; }

switch (tiltDir) { case TILTUP: if (tiltAngle < 180) tiltAngle += tiltSpeed; break; case TILTDOWN: if (tiltAngle > 0) tiltAngle -= tiltSpeed; break; case TILT_STOP: break; }

panServo.write(panAngle); tiltServo.write(tiltAngle); }

Troubleshooting Common Micro Servo Issues

Servo Jitters or Oscillates

If your servo shakes or oscillates around the target position, several things could be wrong:

  1. Power supply is inadequate. The servo cannot hold position because voltage drops under load. Use a better power supply.
  2. PWM signal is noisy. Long signal wires can pick up interference. Keep signal wires short or add a 100µF capacitor between servo power and ground.
  3. Mechanical binding. The servo is fighting against something. Check your linkage for friction.

Servo Does Not Move at All

  • Check wiring. The signal wire must go to a PWM pin (marked with ~ on Arduino Uno).
  • Verify power. Measure voltage at the servo connector under load.
  • Test with a simple myServo.write(90) in setup(). If it moves, the issue is in your logic.

Servo Moves Erratically or Stops Mid-Range

This is almost always a power issue. The Arduino's 5V regulator cannot supply enough current for multiple servos. Use an external 5V supply rated for at least 1A per servo.

Servo Gets Hot

If your servo is hot to the touch, it is likely stalled or overloaded. The servo draws maximum current when it is fighting to reach a position it cannot achieve. Reduce the load or adjust the mechanical range so the servo does not hit its physical stops.

Final Thoughts on Micro Servo Control

Mastering micro servo control with Arduino opens up a world of possibilities. Whether you are building a robotic arm, an animatronic head, or a precision pointer, the principles remain the same: understand the feedback loop, respect the power limitations, and implement smooth motion profiles for professional results.

The key takeaways are:

  • Use the Servo.h library for simplicity, but understand the underlying PWM mechanism.
  • Control speed by stepping through angles with timed intervals, not by using delay().
  • Implement acceleration and deceleration for smooth, natural motion.
  • Use external power for anything beyond a single, unloaded servo.
  • Direction control is just angle management—increase to go one way, decrease to go the other.

Now grab a servo, an Arduino, and start building. The only limit is your imagination—and maybe the torque rating of your servo.

Copyright Statement:

Author: Micro Servo Motor

Link: https://microservomotor.com/how-to-connect-a-micro-servo-motor-to-arduino/arduino-control-micro-servo-angle-speed-direction.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