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.


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:
- First, the Arduino sends a PWM signal that encodes the desired angle.
- Then, the servo’s control circuit reads the pulse width and calculates the target position.
- Next, the control circuit reads the potentiometer to determine the shaft’s current position.
- After that, if there is a difference between the target and the current position, the circuit drives the DC motor in the correct direction.
- 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.

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 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 Position | Duty Cycle at 50 Hz |
|---|---|---|
| 0.5 ms | 0° (minimum, some servos) | 2.5% |
| 1.0 ms | 0° (standard minimum) | 5% |
| 1.25 ms | 45° | 6.25% |
| 1.5 ms | 90° (center position) | 7.5% |
| 1.75 ms | 135° | 8.75% |
| 2.0 ms | 180° (standard maximum) | 10% |
| 2.5 ms | 180° (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:

| Specification | SG90 Micro Servo | MG996R Servo |
|---|---|---|
| Stall Torque | 1.2 kg·cm at 4.8 V, 1.6 kg·cm at 6 V | 11 kg·cm at 4.8 V, 13 kg·cm at 6 V |
| Operating Voltage | 3.5 to 6 V | 4.8 to 7.2 V |
| No-Load Current | 100 mA | 220 mA at 4.8 V, 250 mA at 6 V |
| Stall Current | 650 mA | 900 mA at 4.8 V, 1400 mA at 6 V |
| Operating Speed | 0.12 s per 60° at 4.8 V | 0.15 s per 60° at 4.8 V |
| Weight | 9 g | 55 g |
| Gear Type | Plastic gears | Metal gears |
| Rotation Range | 180° | 180° |
| Ideal Use Case | Lightweight projects, pan-tilt, small robots | Robotic arms, high-load joints, RC vehicles |
| Pros | Cheap, lightweight, low power draw | High torque, durable metal gears, precise |
| Cons | Low torque, plastic gears can strip | Heavier, 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.

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 Model | No-Load Current | Stall Current | Recommended Supply per Servo |
|---|---|---|---|
| SG90 | 100 mA | 650 mA | 5 V, at least 1 A |
| MG996R | 220 mA | 1400 mA | 6 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:
| Pin | Signal | Wire Color |
|---|---|---|
| 1 | Ground (GND) | Black or Brown |
| 2 | Power (VCC, 4.8 to 6 V) | Red |
| 3 | Control 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 Wire | Connects 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 |

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 variablepos. The value ranges from 0 to 180.- Finally, the two
forloops 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
| Component | Pin or Wire | Connects To |
|---|---|---|
| Servo GND (Brown/Black) | GND | Arduino GND and external supply GND |
| Servo VCC (Red) | VCC | External 5 V supply positive |
| Servo Signal (Orange/Yellow) | Signal | Arduino digital pin 9 |
| Potentiometer pin 1 | Outer pin | Arduino 5 V |
| Potentiometer wiper | Middle pin | Arduino analog pin A0 |
| Potentiometer pin 3 | Outer pin | Arduino GND |

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.

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
| Component | Quantity |
|---|---|
| Arduino Uno | 1 |
| PCA9685 servo driver board | 1 |
| SG90 micro servo (or similar) | 2 |
| 10 kOhm potentiometer | 2 |
| External 5 V or 6 V power supply (at least 2 A) | 1 |
| Breadboard and jumper wires | As needed |
PCA9685 dual servo wiring
| Connection | From | To |
|---|---|---|
| I2C Data | Arduino A4 (SDA) | PCA9685 SDA |
| I2C Clock | Arduino A5 (SCL) | PCA9685 SCL |
| Logic Power | Arduino 5 V | PCA9685 VCC |
| Common Ground | Arduino GND | PCA9685 GND and external supply GND |
| Servo Power | External 5 V supply positive | PCA9685 V+ terminal |
| Servo 1 | 3-pin connector | PCA9685 channel 0 |
| Servo 2 | 3-pin connector | PCA9685 channel 15 |
| Potentiometer 1 wiper | Middle pin | Arduino A0 |
| Potentiometer 2 wiper | Middle pin | Arduino A1 |
| Potentiometer outer pins | Outer pins | Arduino 5 V and GND |

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:
- DIY 6-DOF Robotic Arm Build – Build a complete robotic arm using six servos controlled by potentiometers.
- Bluetooth-Controlled Robotic Arm – Add wireless Bluetooth control to your robotic arm using an HC-05 module and a custom Android app.
- Servo Control with Joystick and OLED Display – Control servos using a joystick with real-time position feedback on an OLED screen.
- Mobile Robot Arm (OmObiArm) – Combine a robotic arm with a mobile rover base for a fully mobile manipulation platform.
- HC-05 Bluetooth Module Complete Tutorial – Learn everything about Bluetooth serial communication with Arduino.
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”