Arduino Servo Control Guide: SG90, PWM, and PCA9685

Quick Summary

Arduino servo motor guide covering SG90 micro servos, PWM signal basics, and multi-servo control with the PCA9685 I2C driver. Walks through wiring servos directly to Arduino pins, controlling angle with the Servo library, and scaling up to 16 or more servos using the PCA9685 module. Tested code for all configurations included.

Want to make a servo motor move precisely to any angle with your Arduino? Whether you are building a robotic arm, a pan-tilt camera mount, or an automated door lock, Arduino servo control is one of the first skills worth learning. Yet many beginners run into the same problems: jittery movement, wrong wiring, and confusion about PWM signals and power requirements.

In this tutorial, you will learn how servo motors actually work on the inside, how to wire and program an SG90 or MG996R servo with Arduino, and how to scale up to controlling multiple servos with a PCA9685 driver board. As a result, by the end you will have completed three hands-on projects and gained enough confidence to put servos into any build.

In addition, all wiring diagrams and code examples are included below, so you can follow along step by step. And if you want to see these projects in action, there is a video tutorial at the top of this page.

What this servo tutorial covers

Specifically, by the end of this guide you will be able to:

  • Understand how servo motors work internally (DC motor, gears, potentiometer feedback, and closed-loop control)
  • Also, explain how PWM signals control servo position at 50 Hz
  • Then, compare SG90 and MG996R servos and choose the right one for your project
  • Wire and program an Arduino servo sweep project from scratch
  • Control a servo with a potentiometer using analogRead and map functions
  • Furthermore, use the PCA9685 servo driver to control up to 16 servos simultaneously
  • Power multiple servos safely without damaging your Arduino
  • Finally, troubleshoot common servo problems like jitter, overheating, and brownouts

How servo motors work: the basics

A servo motor is a type of motor that rotates to a specific angle and holds that position. Unlike a regular DC motor that just spins continuously, a servo uses a closed-loop feedback system to achieve precise angle control. Because of this, servos are the go-to choice for applications where exact positioning matters, like robotic arms, steering mechanisms, and camera gimbals.

Internal components of a servo motor

If you open up a typical hobby servo, you will find four main components working together inside:

  • DC Motor: First, this is the core actuator that converts electrical energy into rotational motion. It provides the raw spinning force.
  • Gear Train: Next, a set of reduction gears (plastic in the SG90, metal in the MG996R) that reduces the motor’s high RPM down to a slower, higher-torque output at the shaft.
  • Potentiometer: Then, a small variable resistor connected to the output shaft. It continuously reports the shaft’s current angle back to the control circuit.
  • Control Circuit: Finally, a small PCB inside the servo that compares the desired position (from the PWM signal) with the actual position (from the potentiometer) and drives the motor to close the gap.
Servo motor intact and disassembled showing DC motor gears and potentiometer
Servo motor intact and disassembled, internal view
Disassembled servo motor showing internal gear train control board and potentiometer
Servo motor disassembled showing internal components

Closed-loop control: how the servo knows where to go

Every servo relies on closed-loop control. In short, here is how it works step by step:

  1. First, the Arduino sends a PWM signal that encodes the desired angle.
  2. Then, the servo’s control circuit reads the pulse width and calculates the target position.
  3. Next, the control circuit reads the potentiometer to determine the shaft’s current position.
  4. After that, if there is a difference between the target and the current position, the circuit drives the DC motor in the correct direction.
  5. Finally, once the potentiometer reading matches the target, the motor stops and holds position.

As a result, the servo continuously corrects itself, holding the exact angle even under external load. This feedback loop is what makes servos so dependable in robotics.

Closed-loop control system block diagram for servo motor positioning with feedback
Closed-loop control system diagram for servo motor positioning

Servo PWM signal explained: how pulse width controls angle

Pulse Width Modulation (PWM) is how your Arduino talks to a servo motor. Instead of sending a voltage level, the Arduino sends a series of digital pulses at a fixed frequency. In essence, the width of each pulse tells the servo which angle to move to.

For standard hobby servos, the PWM signal runs at 50 Hz, meaning one pulse goes out every 20 milliseconds. Within each 20 ms window, only the HIGH portion (the pulse width) matters. Typically, a pulse width between 1.0 ms and 2.0 ms maps to the full range of servo motion from 0 to 180 degrees.

PWM signal waveform showing pulse width modulation at 50 Hz for servo motor control
PWM signal waveform for servo control at 50 Hz

PWM pulse width vs servo position table

The table below shows how different pulse widths map to specific servo positions. Keep in mind that exact values can vary a bit depending on the servo model:

Pulse Width (ms)Servo PositionDuty Cycle at 50 Hz
0.5 ms0° (minimum, some servos)2.5%
1.0 ms0° (standard minimum)5%
1.25 ms45°6.25%
1.5 ms90° (center position)7.5%
1.75 ms135°8.75%
2.0 ms180° (standard maximum)10%
2.5 ms180° (maximum, some servos)12.5%

The Arduino Servo library handles most of this for you automatically. For example, when you call servo.write(90), the library generates a 1.5 ms pulse at 50 Hz to move the servo to center position. Still, understanding the underlying PWM signal helps a lot when troubleshooting jitter, calibrating custom ranges, or working with the PCA9685 driver.

SG90 vs MG996R: choosing the right servo for your project

The SG90 micro servo and the MG996R standard servo are the two most popular hobby servos in Arduino projects. Which one you pick depends on your project’s torque, size, and durability needs. Below is a side-by-side comparison:

SG90 micro servo motor with mounting horn for Arduino projects
SG90 micro servo motor with horn attached
SpecificationSG90 Micro ServoMG996R Servo
Stall Torque1.2 kg·cm at 4.8 V, 1.6 kg·cm at 6 V11 kg·cm at 4.8 V, 13 kg·cm at 6 V
Operating Voltage3.5 to 6 V4.8 to 7.2 V
No-Load Current100 mA220 mA at 4.8 V, 250 mA at 6 V
Stall Current650 mA900 mA at 4.8 V, 1400 mA at 6 V
Operating Speed0.12 s per 60° at 4.8 V0.15 s per 60° at 4.8 V
Weight9 g55 g
Gear TypePlastic gearsMetal gears
Rotation Range180°180°
Ideal Use CaseLightweight projects, pan-tilt, small robotsRobotic arms, high-load joints, RC vehicles
ProsCheap, lightweight, low power drawHigh torque, durable metal gears, precise
ConsLow torque, plastic gears can stripHeavier, higher current draw, more expensive

So in short, go with the SG90 when weight and space are limited and loads are light. Pick the MG996R when you need real torque, for example in a 6-DOF robotic arm project where the shoulder and elbow joints carry heavy loads. A lot of robotic arm builds, including ours, use a mix of both: MG996R for the heavy base joints and SG90 for the lighter wrist and gripper.

Servo testers: a useful calibration tool

Before you wire a servo to your Arduino, a servo tester is a handy standalone tool for quickly checking that a servo works. In particular, these cheap modules (usually just a few dollars) let you sweep through the full range or dial in specific positions with a knob, without any code or microcontroller. They are especially useful when calibrating multiple servos for a robotics build.

Servo tester module for testing and calibrating SG90 and MG996R servo motors
Servo tester module for calibration

Powering servos safely: current draw and external supplies

One of the most common mistakes beginners make is powering servos directly from the Arduino’s 5 V pin. Although this might work for a single SG90 under light load, it is not a good idea for most projects. Here is why, and what to do instead.

Why not power servos from the Arduino 5 V pin?

The Arduino Uno’s 5 V regulator can only supply about 500 mA total. However, a single SG90 servo can pull up to 650 mA at stall, and an MG996R can hit 1400 mA. Therefore, if the servo demands more current than the regulator can deliver, you will see voltage drops that cause the Arduino to reset or act unpredictably.

External power supply recommendations

To power servos reliably, use a dedicated external power supply rated at 5 V to 6 V with enough current. For instance, a 5 V 2 A adapter works well for one or two SG90 servos, while a 5 V 10 A power supply is better for projects with six or more servos, like a robotic arm.

Always follow these wiring rules for Arduino servo motor control:

  • Common Ground: First, connect the ground of the external power supply to the Arduino GND. Without a common ground, the PWM signal has no reference and the servo will not respond.
  • Separate Power Rails: Next, connect the servo’s red (power) wire to the external supply’s positive terminal, not to the Arduino 5 V pin.
  • Signal Wire Only: Also, the only connection between the Arduino and the servo should be the signal (PWM) wire.
  • Decoupling Capacitors: Finally, place a 100 uF electrolytic capacitor across the power rails near the servos to reduce voltage spikes and jitter.

Current draw quick reference

Servo ModelNo-Load CurrentStall CurrentRecommended Supply per Servo
SG90100 mA650 mA5 V, at least 1 A
MG996R220 mA1400 mA6 V, at least 2 A

For example, a project with six servos running at once can easily exceed 3 A peak current. That is why a solid external power supply matters so much for any serious Arduino servo project.

Servo motor wiring: understanding the three wires

Before connecting your servo to an Arduino, you first need to understand the three-wire connector that all standard hobby servos use:

PinSignalWire Color
1Ground (GND)Black or Brown
2Power (VCC, 4.8 to 6 V)Red
3Control Signal (PWM)Orange or Yellow

Most servos follow this same pinout regardless of manufacturer. Specifically, the ground wire is always on one edge, power in the middle, and signal on the other edge. If you are ever unsure, just double-check the datasheet for your servo model.

Project 1: Arduino servo sweep – your first servo project

The Sweep sketch is the classic beginner project for Arduino servo control. Basically, it makes the servo rotate back and forth between 0 and 180 degrees continuously. As a result, this project teaches you the basics of the Arduino Servo library and also confirms that your wiring is correct.

Parts needed for the sweep project

  • Arduino Uno (or any Arduino board)
  • SG90 micro servo (or similar)
  • External 5 V power supply for the servo (recommended)
  • Breadboard and jumper wires

Sweep wiring connections

Servo WireConnects To
Brown or Black (GND)Arduino GND and external supply GND
Red (VCC)External 5 V supply positive
Orange or Yellow (Signal)Arduino digital pin 9
Arduino servo sweep project wiring diagram showing SG90 connected to pin 9
Wiring diagram for Project 1 Sweep Arduino servo control

Sweep Arduino code

To get started, open the Arduino IDE and go to File, Examples, Servo, Sweep, or just copy the code below. The Sweep example comes built into the standard Arduino Servo library.

/* Sweep
 by BARRAGAN http://barraganstudio.com
 This example code is in the public domain.
 modified 8 Nov 2013
 by Scott Fitzgerald
 https://www.arduino.cc/en/Tutorial/LibraryExamples/Sweep
 */
#include <Servo.h>
Servo myservo;  // create servo object to control a servo
// twelve servo objects can be created on most boards
int pos = 0;    // variable to store the servo position
void setup() {
    myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}
void loop() {
    for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
        // in steps of 1 degree
        myservo.write(pos);              // tell servo to go to position in variable 'pos'
        delay(15);                       // waits 15 ms for the servo to reach the position
    }
    for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
        myservo.write(pos);              // tell servo to go to position in variable 'pos'
        delay(15);                       // waits 15 ms for the servo to reach the position
    }
}

How the sweep code works line by line

Although the Sweep sketch is short, it covers all the core concepts for servo motor PWM Arduino control:

  • #include <Servo.h> – First, this line imports the Arduino Servo library, which handles all the PWM timing for you.
  • Servo myservo; – This creates a Servo object. You can create up to 12 servo objects on an Arduino Uno.
  • myservo.attach(9); – Then, this tells the library to send PWM signals on digital pin 9.
  • myservo.write(pos); – This command moves the servo to the angle stored in the variable pos. The value ranges from 0 to 180.
  • Finally, the two for loops sweep the angle from 0 to 180 and then back from 180 to 0, with a 15 ms delay between each step for smooth motion.

If the servo moves in the wrong direction or does not reach the full range, try adjusting the delay or check your power supply. Similarly, if you notice jitter at the endpoints, that usually means the servo cannot physically reach those extreme angles.

Project 2: Controlling a servo with a potentiometer (Knob)

The Knob project takes Arduino servo motor control a step further by adding a potentiometer as an input device. Basically, turning the knob directly controls the servo angle in real time. In this project you will learn how to read analog inputs and map them to servo positions, which is a pattern you will use in all sorts of robotics projects.

Parts needed for the Knob project

  • Arduino Uno (or any Arduino board)
  • SG90 micro servo (or similar)
  • 10 kOhm potentiometer
  • External 5 V power supply for the servo (recommended)
  • Breadboard and jumper wires

Knob wiring connections

ComponentPin or WireConnects To
Servo GND (Brown/Black)GNDArduino GND and external supply GND
Servo VCC (Red)VCCExternal 5 V supply positive
Servo Signal (Orange/Yellow)SignalArduino digital pin 9
Potentiometer pin 1Outer pinArduino 5 V
Potentiometer wiperMiddle pinArduino analog pin A0
Potentiometer pin 3Outer pinArduino GND
Arduino servo knob project wiring diagram with potentiometer and SG90 servo
Wiring diagram for Project 2 Knob Arduino servo control with potentiometer

Knob Arduino code

Next, open the Arduino IDE and go to File, Examples, Servo, Knob, or use the code below:

/*
 Controlling a servo position using a potentiometer (variable resistor)
 by Michal Rinott http://people.interaction-ivrea.it/m.rinott
 modified on 8 Nov 2013
 by Scott Fitzgerald
 http://www.arduino.cc/en/Tutorial/Knob
 */
#include <Servo.h>
Servo myservo;  // create servo object to control a servo
int potpin = A0;  // analog pin used to connect the potentiometer
int val;    // variable to read the value from the analog pin
void setup() {
    myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}
void loop() {
    val = analogRead(potpin);            // reads the value of the potentiometer (value between 0 and 1023)
    val = map(val, 0, 1023, 0, 180);     // scale it for use with the servo (value between 0 and 180)
    myservo.write(val);                  // sets the servo position according to the scaled value
    delay(15);                           // waits for the servo to get there
}

How the Knob code works: analogRead and map

Essentially, the two key functions in this sketch are analogRead() and map():

  • analogRead(A0) – First, this reads the potentiometer voltage and returns a value between 0 and 1023 (10-bit ADC resolution).
  • map(val, 0, 1023, 0, 180) – Then, this converts the 0 to 1023 range to a 0 to 180 degree range for the servo.
  • myservo.write(val) – Finally, this sends the mapped angle to the servo.

If you want finer control over the servo position, you can use myservo.writeMicroseconds() instead of myservo.write(). This lets you set the pulse width directly in microseconds (for example, 1000 to 2000), which gives you much higher resolution than the 0 to 180 degree range. Also, if the servo does not reach the full range when you turn the potentiometer all the way, try adjusting the map values slightly, for example map(val, 0, 1023, 10, 170).

PCA9685 servo driver: control up to 16 servos with Arduino

Controlling one or two servos directly from Arduino pins works fine, but what happens when you need more? The Arduino Uno only has six PWM-capable pins, and each servo uses one. On top of that, running more than a couple of servos can cause timing conflicts and jitter. This is where the PCA9685 servo driver comes in.

PCA9685 16-channel servo driver board for controlling multiple servos with Arduino via I2C
PCA9685 servo driver board for Arduino

What is the PCA9685 and why use it?

The PCA9685 is a 16-channel, 12-bit PWM driver chip that communicates with Arduino over the I2C bus. Instead of using Arduino PWM pins, the PCA9685 generates all 16 PWM signals on its own. As a result, you free up Arduino pins for sensors and other tasks, and the dedicated hardware PWM gives you rock-steady signals with zero jitter.

Furthermore, you can daisy-chain up to 62 PCA9685 boards on a single I2C bus, which in theory gives you 992 servo channels. In practice though, a single board with 16 channels is more than enough for most projects.

PCA9685 board connections explained

The PCA9685 breakout board has these main connections:

  • VCC: Logic power (3.3 V or 5 V from Arduino). This powers only the PCA9685 chip, not the servos.
  • GND: Ground, must be shared with Arduino and the servo power supply.
  • SDA: I2C data line. Connect to Arduino A4 (Uno) or the SDA pin.
  • SCL: I2C clock line. Connect to Arduino A5 (Uno) or the SCL pin.
  • V+: Servo power input. Connect an external 5 V to 6 V supply here. This powers all 16 servo channels.
  • OE: Output enable. Leave disconnected (pulled LOW internally) to keep all outputs active.

When to use PCA9685 instead of direct Arduino control

In short, use the PCA9685 when you need to control multiple servos with Arduino (more than two or three), when you want jitter-free motion, or when your project needs those Arduino PWM pins for other things like LED dimming or motor speed control. For simple one-servo projects, direct Arduino control works just fine.

Project 3: Dual servo control with PCA9685 and potentiometers

This project shows how to control two servos independently using a PCA9685 driver board and two potentiometers. In other words, it brings together everything you have learned about analog input, servo PWM, and I2C communication into one practical build.

Parts needed for the PCA9685 dual servo project

ComponentQuantity
Arduino Uno1
PCA9685 servo driver board1
SG90 micro servo (or similar)2
10 kOhm potentiometer2
External 5 V or 6 V power supply (at least 2 A)1
Breadboard and jumper wiresAs needed

PCA9685 dual servo wiring

ConnectionFromTo
I2C DataArduino A4 (SDA)PCA9685 SDA
I2C ClockArduino A5 (SCL)PCA9685 SCL
Logic PowerArduino 5 VPCA9685 VCC
Common GroundArduino GNDPCA9685 GND and external supply GND
Servo PowerExternal 5 V supply positivePCA9685 V+ terminal
Servo 13-pin connectorPCA9685 channel 0
Servo 23-pin connectorPCA9685 channel 15
Potentiometer 1 wiperMiddle pinArduino A0
Potentiometer 2 wiperMiddle pinArduino A1
Potentiometer outer pinsOuter pinsArduino 5 V and GND
Wiring diagram for dual servo control project using PCA9685 board and two potentiometers with Arduino
Wiring diagram for dual servo control with PCA9685 and potentiometers

PCA9685 dual servo Arduino code

For this sketch, you need the Adafruit PWM Servo Driver library. You can install it from the Arduino Library Manager by searching for “Adafruit PWM Servo Driver”. The code reads both potentiometers and maps their values to servo pulse widths:

/**
 * Author: Omar Draidrya
 * Date: 2024/05/07
 * This code controls servos using the PCA9685 PWM driver and potentiometers.
 */
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// Initialize the PCA9685 using the default address (0x40).
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

void setup() {
    Serial.begin(9600);   // Start serial communication for debugging purposes.
    pwm.begin();          // Initialize the PCA9685 module.

    // Set the frequency to 60 Hz, good for servo control.
    pwm.setPWMFreq(60);

    // Setup the analog pins as input.
    pinMode(A0, INPUT);
    pinMode(A1, INPUT);
}

void loop() {
    // Read the potentiometers.
    int potVal0 = analogRead(A0);  // Read the first potentiometer.
    int potVal1 = analogRead(A1);  // Read the second potentiometer.

    // Map the potentiometer values to servo pulse lengths.
    int servoPos0 = map(potVal0, 0, 1023, 150, 600); // Change values 150 to 600 depending on your servo specifications.
    int servoPos1 = map(potVal1, 0, 1023, 150, 600);

    // Set the servo positions.
    pwm.setPWM(0, 0, servoPos0);   // Set servo on channel 0.
    pwm.setPWM(15, 0, servoPos1);  // Set servo on channel 15.

    delay(20);  // Small delay to reduce jitter.
}

Understanding the PCA9685 pulse range values

In the code above, you will notice values like 150 and 600 passed to setPWM(). These are not degrees or milliseconds. Instead, they are 12-bit PWM counts out of 4096 total ticks per cycle. For instance, a value of 150 is roughly 0.73 ms pulse width, and 600 is roughly 2.93 ms. For most standard servos, a range of 150 to 600 covers the full 0 to 180 degree rotation. That said, you may need to tweak these values for your specific servo to avoid end-stop buzzing.

To test the setup, upload the code and then slowly turn each potentiometer. Make sure each servo responds independently. If a servo does not move, double-check the I2C connections and confirm that the external power supply is connected to V+ on the PCA9685 board.

Troubleshooting common Arduino servo problems

Even experienced makers run into servo issues now and then. Below are the most common problems and their solutions.

Servo jitter or twitching

If your servo vibrates or twitches instead of holding a steady position, the most likely cause is an unstable power supply. When the servo draws current spikes, the supply voltage drops momentarily, and as a result the control circuit loses its reference. To fix this, use a dedicated external power supply with adequate current and add a 100 uF to 470 uF electrolytic capacitor across the servo power rails. In addition, noisy PWM signals from software-based servo control can also cause jitter, which is another good reason to consider the PCA9685 hardware PWM driver.

Servo not moving at all

If the servo does not respond to commands, first check these things: verify that the signal wire is connected to the correct PWM-capable Arduino pin, confirm that the ground is shared between the Arduino and the servo power supply, and make sure the power supply is actually on and delivering the correct voltage. Also, check that the Servo library is included and the servo is properly attached in your code with servo.attach(pin).

Servo overheating

Servo overheating usually happens when the motor is stalled, meaning it is trying to hold a position against a load that exceeds its torque rating. In this state, the motor continuously draws stall current, which consequently generates a lot of heat. To prevent this, either reduce the mechanical load, use a higher-torque servo, or add rest periods in your code by detaching the servo when it does not need to hold position. For example, calling servo.detach() stops the PWM signal and lets the motor relax.

Noisy power supply and voltage brownouts

When multiple servos move at the same time, the combined current draw can cause voltage sags on a weak power supply. As a result, the Arduino may reset, LEDs may flicker, or servos may move erratically. Therefore, use a power supply with a higher current rating and add bulk capacitance (at least 470 uF) near the servos. Besides that, keep the power supply wires as short and thick as possible to minimize voltage drop.

Wrong pulse range or limited rotation

If your servo does not reach the full 0 to 180 degree range, the problem is likely the pulse width limits in your code. Because different servo models can have slightly different pulse ranges, you may need to adjust them. For direct Arduino control, you can specify custom limits with servo.attach(pin, minPulse, maxPulse), for example servo.attach(9, 544, 2400). Similarly, for the PCA9685, adjust the minimum and maximum PWM count values (for instance, 125 to 625 instead of 150 to 600).

Wrong ground wiring

Without a common ground between the Arduino and the servo power supply, the PWM signal has no valid reference voltage. Consequently, the servo may ignore commands entirely or behave unpredictably. So always connect all ground wires together, whether you are using a single power source or separate supplies for logic and servos.

Frequently asked questions (FAQ)

How many servos can I control with one Arduino Uno?

With the standard Servo library, you can control up to 12 servos on an Arduino Uno. However, each servo uses a timer, so running many servos may interfere with PWM on certain pins. If you need more, a PCA9685 board adds 16 channels with no timer conflicts.

Can I use a servo motor without a library?

Yes, you can technically do it with digitalWrite() and delayMicroseconds(). But this approach is less precise and blocks the CPU. Because the Servo library handles timing automatically, there is no good reason to do it manually.

What is the difference between servo.write() and servo.writeMicroseconds()?

The servo.write() function takes a degree angle (0 to 180) and converts it to a pulse width. In contrast, servo.writeMicroseconds() lets you set the exact pulse width (typically 1000 to 2000 us), so you get finer control over position.

Why does my servo buzz or vibrate at certain positions?

This usually means the servo is near its mechanical limit or the signal is slightly unstable. As a result, the control loop keeps trying to correct a tiny position error. To fix it, adjust your pulse range to avoid extremes and use a stable power supply.

Can I power a servo from a 9 V battery?

A 9 V battery is not a good choice for servos. Most hobby servos need 4.8 to 6 V, and besides that, 9 V batteries have very limited current capacity (around 500 mAh). Use a proper 5 V supply instead.

What does PCA9685 stand for?

PCA9685 is an NXP Semiconductors part number for a 16-channel, 12-bit PWM controller IC with I2C interface. In the maker community, it is mainly used as a servo driver, although it can also control LEDs and other PWM devices.

Can I use MG996R and SG90 servos together in the same project?

Yes, and in fact many robotic arm builds do exactly that. They use MG996R servos for the high-torque base and shoulder joints, and SG90 servos for the lighter wrist and gripper. Just make sure your power supply handles the combined current.

How do I control a continuous rotation servo with Arduino?

Continuous rotation servos work differently because the pulse width controls speed and direction instead of angle. A 1.5 ms pulse stops the motor, shorter pulses spin it one way, and longer pulses spin it the other way. You can still use the Servo library: servo.write(90) stops, servo.write(0) is full speed one way, servo.write(180) is full speed the other.

Why does my Arduino reset when the servo moves?

This happens because the servo draws too much current, causing a voltage drop that resets the Arduino. The fix is simple: power the servo from a separate external supply and share only the ground with the Arduino.

Can I control a servo over Bluetooth with Arduino?

Yes. By adding an HC-05 or HC-06 Bluetooth module, you can send servo angle commands wirelessly from a phone or computer. The Arduino receives them over serial and moves the servo. For a full walkthrough, check our HC-05 Bluetooth module guide.

Resources, libraries, and downloads

Below is a quick reference of all the tools, libraries, and files you need for the projects in this tutorial:

Arduino libraries used

  • Servo.h – Built into the Arduino IDE. No installation needed. Used for Project 1 (Sweep) and Project 2 (Knob).
  • Adafruit PWM Servo Driver – Install from the Arduino Library Manager. Search for “Adafruit PWM Servo Driver”. Used for Project 3 (PCA9685 Dual Servo).
  • Wire.h – Built into the Arduino IDE. Required for I2C communication with the PCA9685.

Code download and wiring diagrams

All three project codes are included directly in this article above. You can also find the source files and wiring diagrams on our GitHub repository. In addition, every wiring diagram in this tutorial is available as a high-resolution image, so just right-click and save.

Recommended next tutorials on OmArTronics

Once you feel comfortable with basic Arduino servo control, here are some more advanced projects worth exploring next:

Conclusion

In this tutorial, you learned how servo motors work on the inside: the DC motor, gear train, potentiometer feedback, and closed-loop control system. You also now understand how PWM signals at 50 Hz translate pulse width into precise angular position. And finally, you have completed three practical projects: the Sweep sketch for basic motion, the Knob project for analog input control, and the PCA9685 dual servo project for scaling up to multiple servos.

Along the way, you also learned how to choose between the SG90 and MG996R servos, how to power servos safely with external supplies, and how to troubleshoot common issues like jitter, brownouts, and overheating. In summary, these skills come up in just about every robotics project.

So what is your next step? Put what you have learned into a real project. For instance, building a DIY robotic arm is one of the best ways to solidify your servo control skills, and our step-by-step guide will walk you through the entire build.

3 thoughts on “Arduino Servo Control Guide: SG90, PWM, and PCA9685”

Leave a Comment