To write reliable arduino servo code, you must first match the servo’s physical torque curve and control architecture to your actual mechanical load. A standard SG90 micro servo handles under 1.8 kg-cm of torque, while a high-voltage digital servo demands specific PWM frequencies and dedicated power rails. Unlike stepper motors—which hold position via continuous coil energization and open-loop step counting—servos use closed-loop feedback (usually a potentiometer or magnetic encoder) to correct positional errors in real-time. Treating these two motor types as interchangeable will result in stripped gears, overheated drivers, or oscillating control loops.

This guide bridges the gap between hardware selection and software implementation, giving you the exact sizing math, wiring protocols, and C++ code needed to drive servos without burning out your microcontroller.

The Hardware First: Choosing the Right Servo for Your Load

Before writing a single line of code, you must size the motor. The golden rule of servo sizing is the 2x Safety Factor. Never size a servo based on the exact calculated load; always multiply your required stall torque by at least 2.0 to account for dynamic acceleration, friction, and voltage sag under load.

Worked Load Example: Robotic Arm Joint

Suppose you are building a robotic arm that needs to lift a 200g (0.2 kg) payload at the end of a 15cm (0.15m) forearm lever.

  • Force (F): Mass × Gravity = 0.2 kg × 9.81 m/s² = 1.96 Newtons.
  • Torque (τ): Force × Distance = 1.96 N × 0.15 m = 0.294 Nm (approximately 3.0 kg-cm).
  • Sized Torque: 3.0 kg-cm × 2.0 (Safety Factor) = 6.0 kg-cm minimum required.

A standard SG90 (1.8 kg-cm) will strip its plastic gears instantly. You need a motor in the 10–13 kg-cm range, like the MG996R.

Motor Type Comparison Matrix

Servo Type Torque Curve & Profile Control Needs & Feedback Typical Cost (2026)
RC Analog (e.g., SG90) Low torque (1-2 kg-cm), linear drop-off near limits. Plastic gears. Standard 50Hz PWM (1000-2000µs). Internal pot feedback. $2 - $5
RC Digital (e.g., MG996R) High holding torque (10-15 kg-cm), sharp stall curve. Metal gears. Standard 50Hz PWM, higher current spikes. Internal pot feedback. $8 - $15
Smart/Bus (e.g., Dynamixel XL430) Programmable torque limits, high precision. Metal/steel gears. Serial bus (TTL/RS485). Digital magnetic encoder, PID tuning. $45 - $60
Continuous Rotation (e.g., FS90R) Torque translates to speed, not position. No mechanical hard stops. 50Hz PWM (pulse width dictates speed/direction). No position feedback. $6 - $10

Wiring and Terminal Identification for RC and Smart Servos

The most common cause of bricked Arduinos in servo projects is routing motor current through the microcontroller’s onboard 5V regulator. Servos draw massive current spikes when starting or stalling.

Standard 3-Wire RC Servo Pinout

  • Brown or Black (GND): Must be tied to both the external power supply ground and the Arduino GND to establish a common reference.
  • Red (VCC): Power input. Typically 4.8V to 6.0V for standard servos, up to 8.4V for high-voltage (HV) variants. Never connect this to the Arduino 5V pin if the servo draws >500mA.
  • Orange, Yellow, or White (Signal): PWM control line. Connects to an Arduino digital pin capable of hardware PWM (e.g., pins 3, 5, 6, 9, 10, 11 on the Uno).

Smart Servo Pinout (Dynamixel TTL)

Smart servos abandon PWM for serial communication. The standard 3-pin TTL connector uses GND, VCC (12V for XL430), and Data. The Data line requires a half-duplex UART connection, usually facilitated by a dedicated USB-to-Dynamixel controller (like the U2D2) or a custom Arduino TX/RX circuit with a 74HC241 buffer to handle the direction switching.

⚠️ Power Isolation Warning: For any servo drawing over 1A at stall, use a dedicated buck converter (like an LM2596 set to 6.0V) powered by a 12V battery or wall supply. Add a 100µF electrolytic capacitor and a 0.1µF ceramic capacitor across the VCC and GND lines as close to the servo terminals as possible to suppress voltage spikes.

The Decision Tree: Which Servo and Driver Do You Actually Need?

Use this decision path to terminate your hardware selection. Do not over-engineer with smart servos if simple PWM suffices, and do not under-spec RC servos for high-shock loads.

Application Profile Required Motor Type Driver / Controller Demand
Load < 2 kg-cm, simple positioning (e.g., radar sweep, light latch) SG90 Micro Analog Direct Arduino 5V pin (if only 1 servo) or USB power.
Load 2-15 kg-cm, high shock/vibration (e.g., robotic arm, pan-tilt) MG996R Metal Gear Digital External 6V 3A+ PSU, direct Arduino PWM pin.
Multi-joint robotics, requires exact position readback & PID tuning Dynamixel XL430-W250 U2D2 Controller or custom UART buffer, 12V PSU.
Linear actuation, drive wheels, continuous winching FS90R Continuous Rotation External 6V PSU, direct Arduino PWM pin.

The Default Recommendation: For 90% of intermediate DIY robotic arms, camera gimbals, and heavy-duty pan-tilt mechanisms, the MG996R driven by an external 6V 5A switching power supply is the definitive, most cost-effective pick. It provides massive torque, survives mechanical shocks that shatter plastic gears, and requires no complex serial bus libraries.

Bulletproof Arduino Servo Code for Precision Control

Basic tutorials show you how to use servo.write(angle). However, for precision work, you should use servo.writeMicroseconds() to bypass the library's internal mapping inaccuracies. Furthermore, leaving a servo attached and continuously sending PWM signals when it is resting against a mechanical load causes the motor to "hunt," drawing continuous current and overheating the internal H-bridge.

The following code implements a high-resolution sweep with an automatic detach() routine to save power and prevent gear wear once the target is reached.

#include <Servo.h>

// Hardware configuration
const int SERVO_PIN = 9;       // Must be a PWM-capable pin
const int MIN_PULSE = 500;     // Calibrate these for your specific servo
const int MAX_PULSE = 2500;    // Standard is 544-2400, but physical limits vary
const int NEUTRAL_PULSE = 1500;

Servo myServo;

// Target tracking
int currentPulse = NEUTRAL_PULSE;
int targetPulse = NEUTRAL_PULSE;
bool isMoving = false;

void setup() {
  Serial.begin(115200);
  
  // Attach with explicit physical limits to prevent mechanical binding
  myServo.attach(SERVO_PIN, MIN_PULSE, MAX_PULSE);
  
  // Initialize to neutral and immediately detach to prevent startup jitter
  myServo.writeMicroseconds(NEUTRAL_PULSE);
  delay(500); 
  myServo.detach();
  
  Serial.println("Servo initialized and detached. Ready for commands.");
}

void loop() {
  // Example: Move to 90 degrees (approx 1500us) then 180 degrees (approx 2500us)
  if (!isMoving && millis() > 5000) {
    targetPulse = 2000; // Command a specific microsecond position
    isMoving = true;
    myServo.attach(SERVO_PIN, MIN_PULSE, MAX_PULSE); // Re-attach before moving
  }
  
  if (isMoving) {
    // Smooth interpolation (simple P-controller)
    if (currentPulse < targetPulse) {
      currentPulse += 5; // Step size dictates speed
    } else if (currentPulse > targetPulse) {
      currentPulse -= 5;
    }
    
    myServo.writeMicroseconds(currentPulse);
    
    // Check if target reached
    if (abs(currentPulse - targetPulse) <= 5) {
      currentPulse = targetPulse;
      myServo.writeMicroseconds(currentPulse); // Final exact command
      delay(100); // Allow time to settle
      
      // CRITICAL: Detach to stop PWM signal, preventing hunting and overheating
      myServo.detach(); 
      isMoving = false;
      Serial.println("Target reached. Servo detached to save power.");
    }
    delay(20); // 50Hz update rate equivalent
  }
}

Note on calibration: The Arduino Servo Library defaults to 544µs and 2400µs. However, many modern digital servos can physically travel further. Use a serial monitor to slowly increment microseconds until the servo hums, then back off by 50µs to set your true MIN_PULSE and MAX_PULSE limits.

Diagnosing Failure Signatures: Hum, Overheat, and Stall

When your hardware and code are deployed, the servo will communicate its health through physical and auditory signatures. Here is how to diagnose the three most common failure modes.

1. The "Hum" or Jitter at Rest

Symptom: The servo vibrates rapidly or hums when it should be holding still.
Root Cause: Ground loops, noisy power supplies, or insufficient PWM signal current. If the Arduino and the servo share a long, thin ground wire, the servo's current spikes cause "ground bounce," which the servo interprets as a fluctuating PWM signal.
Fix: Ensure a star-ground topology where the heavy servo ground and the Arduino ground meet at a single point near the power supply. Add a 100µF decoupling capacitor at the servo terminals. If using long signal wires, add a 1kΩ pull-down resistor between the Signal pin and GND.

2. Overheat and Smell of Ozone/Hot Plastic

Symptom: The servo casing is too hot to touch, and it draws maximum current continuously.
Root Cause: The servo is being commanded to a position that is mechanically impossible (e.g., commanding 180° when a physical hard stop exists at 170°). The internal H-bridge applies maximum voltage to the DC motor trying to reach the unreachable target, resulting in a dead short across the motor windings.
Fix: Recalibrate your MIN_PULSE and MAX_PULSE variables in the code. Implement the detach() function shown in the code block above immediately after a movement completes. For critical applications, upgrade to a smart servo like the Dynamixel XL430, which features programmable current limits and automatic shutdown on overload.

3. Stall and "Clicking"

Symptom: The servo output shaft does not move, but you hear a rhythmic clicking or grinding from inside the housing.
Root Cause: Exceeded stall torque resulting in stripped gears, or the internal potentiometer wiper has lost contact due to mechanical shock.
Fix: If it's an RC servo with plastic gears, the motor is likely fine but the gearbox is destroyed; replace the gear set or upgrade to a metal-gear variant (MG996R). If it's a metal-gear servo clicking, the load has exceeded the motor's physical stall torque. You must either increase the gear reduction ratio mechanically, reduce the payload, or select a higher-torque motor. Never use software to "push harder" through a mechanical stall; you will burn out the DC motor windings.