Understanding Servo Motor Control Signals
When you set out to control a servo with Arduino, the physical wiring and power budgeting are just as critical as the C++ code. Unlike standard DC motors, hobby servos contain an integrated closed-loop control system comprising a DC motor, a gear train, and a potentiometer for positional feedback. They do not rely on varying voltage levels for speed or direction; instead, they use Pulse Width Modulation (PWM) at a strict 50Hz frequency (a 20ms period).
The Arduino sends a high pulse within this 20ms window. The width of this pulse dictates the target angle:
- 1.0ms pulse: 0 degrees (minimum position)
- 1.5ms pulse: 90 degrees (center/neutral position)
- 2.0ms pulse: 180 degrees (maximum position)
Understanding this timing is crucial because the Arduino Servo.h library abstracts these microsecond timings into degree values, but hardware variations often require manual calibration using raw microsecond commands.
Choosing Your Servo: SG90 vs. MG996R vs. DS3218
Selecting the correct actuator prevents mechanical failure and power supply overload. Below is a technical comparison of the three most common hobbyist servos available in 2026, including real-world pricing and specifications.
| Model | Stall Torque (at 6V) | Stall Current | Gear Material | Avg. Price (2026) | Best Application |
|---|---|---|---|---|---|
| TowerPro SG90 | 1.8 kg-cm | ~220 mA | Plastic | $2.50 - $3.50 | Pan/tilt camera mounts, lightweight robotic arms, RC plane flaps. |
| TowerPro MG996R | 13.0 kg-cm | ~2.5 A | Metal (Brass/Steel) | $10.00 - $14.00 | Robotic legs, steering mechanisms, heavy-duty robotic grippers. |
| DS3218 (20kg) | 20.0 kg-cm | ~3.0 A | Hardened Steel | $18.00 - $24.00 | Industrial automation prototypes, high-load winches, quadruped joints. |
Source: Component pricing and specifications aggregated from major hobbyist suppliers and the Adafruit Motor Selection Guide.
The Power Trap: Why External Power is Mandatory
The most common point of failure when beginners attempt to control a servo with Arduino is the power supply architecture. Never power an MG996R or DS3218 directly from the Arduino's 5V pin.
Critical Warning: The Arduino Uno's onboard linear voltage regulator (typically an NCP1117) can safely supply a maximum of 800mA, and only if the input voltage is kept below 9V. An MG996R draws up to 2.5A under stall conditions. Connecting it to the Arduino's 5V rail will cause an immediate brownout, triggering the ATmega328P microcontroller to reset continuously, and potentially melting the onboard regulator.
For any servo drawing more than 300mA, you must use an external power supply. A 5V/6V BEC (Battery Eliminator Circuit) or a buck converter like the LM2596 set to 5.0V is the industry-standard solution.
Step-by-Step Wiring Instructions
Follow this exact sequence to wire an external power supply, the Arduino, and the servo safely.
- Establish Common Ground: Connect the Ground (GND) terminal of your external 5V/6V power supply directly to one of the Arduino's GND pins. Without a common ground reference, the PWM signal from the Arduino will be unreadable to the servo's internal controller.
- Wire the Servo Power: Connect the servo's Red wire (VCC) to the positive terminal of the external power supply. Connect the servo's Brown or Black wire (GND) to the external power supply's ground.
- Wire the Signal Line: Connect the servo's Orange or Yellow wire (Signal) to a PWM-capable digital pin on the Arduino (e.g., Pin 9). Note: While the
Servo.hlibrary can generate software PWM on any digital pin, using hardware PWM pins (marked with a ~ on the Uno, like 9, 10, or 11) yields the most stable, jitter-free signal. - Add Decoupling Capacitance: Solder or plug a 470µF to 1000µF electrolytic capacitor (rated at 10V or higher) across the VCC and GND lines as close to the servo as possible. This absorbs inductive voltage spikes and current transients during rapid direction changes.
Arduino C++ Code Implementation
The official Arduino Servo Library handles the 50Hz timer interrupts automatically. Below is a robust implementation that includes a smooth sweep function and a hard-stop safety delay.
#include <Servo.h>
// Initialize servo object
Servo myServo;
// Define pin and safety limits
const int SERVO_PIN = 9;
const int MIN_ANGLE = 10; // Avoid mechanical hard-stops at 0
const int MAX_ANGLE = 170; // Avoid mechanical hard-stops at 180
void setup() {
Serial.begin(115200);
// Attach servo, overriding default microsecond limits for precise calibration
myServo.attach(SERVO_PIN, 544, 2400);
// Center the servo on boot
myServo.write(90);
delay(1000);
}
void loop() {
// Sweep from MIN to MAX
for (int pos = MIN_ANGLE; pos <= MAX_ANGLE; pos += 1) {
myServo.write(pos);
delay(15); // 15ms delay for smooth motion
}
// Sweep from MAX to MIN
for (int pos = MAX_ANGLE; pos >= MIN_ANGLE; pos -= 1) {
myServo.write(pos);
delay(15);
}
}
Advanced Calibration: Using writeMicroseconds()
The write(angle) function is convenient, but it assumes a perfect linear mapping between 544µs and 2400µs. In reality, manufacturing tolerances in the internal potentiometer mean that write(0) might command the servo past its physical hard-stop, causing a continuous stall current draw and stripping the plastic gears on an SG90.
For precision applications, use writeMicroseconds(). By manually sending raw pulse widths, you can map the exact mechanical limits of your specific unit.
// Example: Finding the true mechanical center
myServo.writeMicroseconds(1500); // Exactly 1.5ms pulse
Refer to advanced calibration techniques detailed in the Arduino Electronics Documentation to map custom sensor inputs to microsecond outputs using the map() function.
Troubleshooting Common Failure Modes
When your servo behaves erratically, the issue is rarely the code. Use this diagnostic matrix to identify hardware and signal failures.
1. Servo Jittering or Twitching
- Cause: Noisy power supply, USB polling interference, or a damaged signal wire.
- Fix: Ensure the 470µF decoupling capacitor is installed. If powering the Arduino via USB while monitoring Serial data, USB polling can introduce microsecond-level jitter to the software PWM. Switch to an external DC barrel jack power supply for the Arduino itself.
2. Arduino Resetting Randomly
- Cause: Voltage brownout. The servo's startup surge drops the system voltage below the ATmega328P's brownout detection threshold (typically 2.7V or 4.3V depending on fuse settings).
- Fix: Separate the power rails entirely. Use a high-current BEC (minimum 5A rating for MG996R servos) and verify the common ground connection.
3. Continuous Rotation Servos Won't Stop
- Cause: Continuous rotation servos lack the internal potentiometer.
write(90)is supposed to stop them, but the internal trim potentiometer is uncalibrated. - Fix: Use
writeMicroseconds()to find the exact dead-band. Usually, a value between 1480µs and 1520µs will bring the motor to a complete halt.
Frequently Asked Questions
Can I control multiple servos with a single Arduino?
Yes. The Servo.h library supports up to 12 servos on an Arduino Uno (which uses hardware timers) and up to 48 on an Arduino Mega. However, every servo added increases the total current draw. A 6-servo robotic arm using MG996Rs can draw 15A under stall conditions, requiring a heavy-duty power supply and thick gauge (14 AWG) wiring for the power distribution bus.
Why is my servo buzzing loudly when holding a position?
A loud buzz indicates the servo is fighting a continuous load or oscillating around the target position (hunting). This happens if the load exceeds the servo's holding torque, or if the internal potentiometer has dead spots. Reduce the mechanical load, implement a physical brake, or upgrade to a digital servo (like the DS3218) which uses a higher internal PWM frequency (typically 330Hz to 560Hz) to apply power in smaller, more precise increments, eliminating the audible analog hum.






