Download PDF Parts List View on YouTube Download Code

I’ve used Servo Motors in several projects and experiments in the DroneBot Workshop but have yet to publish a complete servo motor guide. That changes with this article (and its associated video).

This guide is designed to be a comprehensive resource for experimenters, makers, and electronics hobbyists working with small DC servo motors and microcontrollers.  We’ll cover everything from basic operating principles to advanced control techniques and troubleshooting tips.

Introduction

Let me start by saying that working with servo motors can be fun! Having your creation come to life and move into the prescribed position can give you a sense of accomplishment, much more than getting a text formatting function to work in your latest program (at least it is for me).

Servo motors are known for their precise control of angular position. They enable you to animate projects, control mechanisms, and build robotic arms, remote-controlled vehicles, and almost anything your imagination can imagine. 

This guide focuses on small DC servo motors (commonly used in RC models and microcontroller-based projects). It provides the theory, practical knowledge, and code you need to integrate them with popular microcontrollers like the Arduino Uno and the ESP32. We’ll also use the PCA9685 16-channel PWM driver for scenarios requiring multiple servo outputs.

Whether a beginner or an experienced maker, this article (and its accompanying video) will be your reference for working with small DC servo motors and microcontrollers.

DC Servo Motors

This guide focuses on small DC servo motors used by hobbyists, experimenters, and RC enthusiasts. These motors range from tiny Micro Servos to larger units capable of lifting over 50 kg.

We will cover both analog and digital servo motors, which look alike but operate differently internally. 

Basic Servo Operating Principles

A servo motor is a self-contained electromechanical system that rotates an output shaft to a specific angular position in response to an electrical signal. Unlike regular DC motors that spin continuously, most servo motors are designed for precise angular positioning (Continuous Rotation Servos are the exception to this rule).

A servo motor is a closed-loop control system.  It uses feedback to accurately achieve a desired position.  Internally, a servo motor consists of a DC motor, a potentiometer (for position feedback), a control circuit, and gears.  The control circuit compares the desired position (from the PWM control signal) with the current position (from the potentiometer).  If there’s a difference, the motor is driven to correct the error until the desired position is reached.

The servo motor’s internal control circuit can be analog or digital. Both respond to the same PWM (Pulse Width Modulation) control signal, the pulse width of which determines the servo position.

Servo Motor Construction

A typical hobby servo motor contains:

  • DC motor
  • Potentiometer (for position feedback)
  • Control circuit PCB
  • Gear train (for torque multiplication)
  • Output shaft
  • Case (housing)
  • 3-wire connection (ground, power, signal)

The potentiometer is mechanically linked to the output shaft, providing position feedback to the control circuit. The circuit continuously compares the desired position (from the input signal) with the actual position (from the potentiometer) and adjusts the motor accordingly.

Some digital servos use an encoder instead of a potentiometer.

The servo gears can be either plastic or metal. Metal gears are preferable for most applications as they are more durable than their plastic counterparts. Many “plastic” gears are nylon compounds that can be durable and waterproof.

Most servo motors are permanently sealed in a plastic or aluminum housing. Some are waterproof, making them ideal for RC watercraft.  

Hobby servo motors come in three size categories:

Micro  (or Nano) Servo

  • Typical Dimensions: ~23 × 12 × 24 mm (height × width × length)
  • Approximate Weight: 8–12 g

Popular in small-scale RC planes, lightweight robotics, and tight spaces where minimal weight and size are critical.

Lower torque capacity compared to larger classes but sufficient for small or low-load projects.

A popular Micro Servo is the SG-90 (Amazon Link)

Another popular Micro Servo is the MG-90 (Amazon Link)

Standard Servo

  • Typical Dimensions: ~40 × 20 × 36 mm (height × width × length)
  • Approximate Weight: 35–50 g

The most common form factor for hobbyists and RC enthusiasts, used in cars, boats, planes, and general robotics.

Provides a balance between moderate torque, decent speed, and convenient size.

A popular and inexpensive digital standard servo is the MG995 (Amazon Link)

If you require more torque, a good choice is the DS3235 (180-degrees, Amazon Link)   (270-degrees, Amazon Link)

Giant (or Large-Scale) Servo

  • Typical Dimensions: ~60 × 30 × 57 mm (height × width × length)
  • Approximate Weight: 90 g and up (can exceed 150 g for very large servos)

Designed for large-scale RC models (e.g., quarter-scale aircraft, big gas-powered vehicles) and heavy-duty robotics applications.

These motors offer significantly higher torque and may operate at higher voltages (e.g., 6–7.4 V or more).

A typical Giant servo motor is the DS51150 (Amazon Link)

Giant serves often feature metal gears and robust cases to handle large mechanical loads.

Controlling Servo Motors

Hobby servo motors are controlled using Pulse Width Modulation, or PWM.  As its name implies, PWM is a method of conveying information by modulating the width of a pulse.

Servo motors typically require a repeating pulse signal operating at approximately 50 Hz (a pulse every 20 milliseconds). The width of the high portion of the pulse within each 20 ms period determines the servo’s target angle. 

In most servo motors, a pulse width between 0.5 and 1 millisecond corresponds to one extreme of the servo’s range, while a pulse width between 2 and 2.5 milliseconds corresponds to the other extreme. Any pulse width falling between these values will move the servo to an intermediate position. 

For most servos, a 1.5 millisecond pulse width will center the shaft.

270-degree servo motors use the same PWM signals as their 180-degree counterparts. 

A Continuous Rotation Servo motor uses the same range of PWM pulses to control both speed and direction.

A 1.5 millisecond pulse width will stop the motor.  Increasing the pulse width will rotate the motor clockwise.  The wider the pulse, the faster it will spin until it reaches maximum speed with a 2 to 2.5-millisecond pulse.

A pulse width of less than 1.5 milliseconds turns the motor counterclockwise. As the pulse width decreases, it spins faster until it reaches maximum speed at the motor’s minimum pulse width.

Analog vs. Digital Servo Motors

Analog and digital hobby servo motors appear and function similarly. Both are controlled by identical PWM pulses, and they share the same motor and potentiometer layout, although some digital servo motors use encoders in place of potentiometers. The main difference between the two types is found in their control PCBs.

Analog Servo Motors

Analog servos are the traditional type of servo motors and remain widely used due to their affordability and simplicity. They are typically less expensive than digital servo motors but offer lower precision and accuracy.

The following diagram illustrates the operation of a typical analog servo motor:

The incoming PWM signal is processed with a pulse width to voltage converter to obtain a control voltage. This is fed to a comparator, which compares it with a voltage derived from the potentiometer position.

The comparator’s output will be either zero, positive, or negative. It will only be zero if its two input voltages are equal, meaning that the motor shaft is in the correct position. Otherwise, it will be positive or negative, the voltage level reflecting the difference between the pot position and the desired position.

This output is fed to an error amplifier and then to an H-Bridge motor driver, which powers the motor. As the input is PWM pulses, the output will also be pulsed at the 50Hz rate of the input PWM signal.

Digital Servo Motors

Digital servos use a microcontroller to process the PWM signal, allowing faster updates (up to 500 Hz or more). This results in smoother operation, higher torque, and better precision. However, they consume more power and are generally more expensive than analog servos.

One method of constructing a digital servo motor is illustrated here:

The key difference in a digital servo motor control circuit is that it is based on a microcontroller instead of an analog comparator.  The PWM signal is fed directly to the microcontroller, where it is precisely measured. It is compared to the potentiometer position, an analog voltage that is sent to an analog-to-digital converter (ADC).

The microcontroller’s output is a PWM signal fed to an H-Bridge motor driver to power the motor. It is a higher-frequency PWM signal, so it gives the motor more effective power. However, it can also cause a “whining” noise in the motor’s coils, which some users may find objectionable.

Here is a second method of building a digital servo motor. This design replaces the potentiometer with a digital encoder, either optical or hall-effect.

Servo Motor Specifications

One of the first things you should do when designing a project based on servo motors is to obtain the specification sheet(s) for the motor(s) you are considering using. They are a wealth of information.

Here are some specifications you should be looking at:

Torque

Servo motor torque measures the rotational force the motor can apply to its output shaft.

Torque is typically represented in kg·cm or oz·in; torque describes the servo’s ability to move or hold a load. For example, a servo rated at “2 kg·cm” can exert a force of 2 kilograms on a 1 cm lever arm.

When selecting a servo, it’s crucial to consider the required torque for your application, taking into account the weight of the load, the leverage involved in the mechanism, and any potential external forces the servo might need to overcome. 

Insufficient torque will result in the servo being unable to reach the desired position or maintain it under load, potentially leading to damage or unreliable operation. Overloading a servo motor beyond its torque capacity can lead to overheating, reduced efficiency, or mechanical failure.

Voltage Range

The voltage range specification shows the acceptable input voltage for the servo’s internal electronics and motor.  

This range is typically provided as a minimum and maximum voltage value (e.g., 4.8V – 6.0V). Supplying a voltage outside this range can lead to unpredictable behavior, reduced performance, or even permanent damage to the servo’s internal electronics and motor.

Standard hobby servo motors operate between 4.8 V and 6.0 V, but many modern designs can safely accept up to 7.4 V or more. Higher-voltage-capable servos often provide increased torque and speed but also require a robust power supply capable of delivering sufficient current at that voltage.

Rated Current

Rated current, sometimes specified as “no-load” current and “stall” current, describes how much current the servo motor draws during operation. This value is usually expressed in amperes (A) or milliamperes (mA).

No-load current indicates the draw when the servo runs without a load, while stall current is significantly higher and represents the current draw when the servo’s shaft is prevented from moving. 

When designing power systems for multiple servos, you must ensure the power supply can handle the combined rated current of all servos.

Deadband

The dead zone, also known as deadband, is a small range of input signal change where the servo doesn’t respond. The servo’s motor stays inactive if the control pulse width falls within this narrow band around its last position, as the position is assumed to be “close enough.” 

It’s essentially a zone of insensitivity designed to prevent the servo from constantly oscillating or “hunting” for the exact commanded position due to minor variations or noise in the control signal. 

A smaller deadband width allows for finer control and higher accuracy, but it may also make the servo more sensitive to signal noise. Conversely, a larger deadband width reduces sensitivity to noise but may compromise positional accuracy. Some digital servos allow users to adjust the deadband width programmatically, enabling customization for specific applications.

The deadband is typically expressed in microseconds for PWM signals or as a percentage of the total control range.

Speed

Servo speed describes how quickly the output shaft can rotate, commonly specified as the time (in seconds) it takes to rotate 60° at a given voltage (e.g., 0.12 s/60° at 6.0 V). A lower speed value indicates a faster servo.

A servo’s speed is influenced by factors such as the motor’s internal gearing and the applied voltage. Higher voltages generally result in faster operation, up to the servo’s maximum rated voltage.

Operating Travel

Operating travel indicates the usable angular range of motion for the servo, typically around 0° to 180° for many hobby servos. Some specialized units can rotate 270°, and continuous-rotation servos turn indefinitely without a fixed stop.

Programmable digital servos allow you to adjust or limit their operating travel through software configuration.

Using Servo Motors with Microcontrollers

Servo motors can be driven using an RC receiver or a dedicated controller. However, as experimenters, we will use microcontrollers.

Microcontrollers are excellent for this application as they excel at precision timing and can produce beautiful, clean PWM waveforms. Many servo motor libraries are available for microcontrollers to simplify coding tasks.

Let’s see how we can use servo motors with some popular microcontrollers. We will start with a classic microcontroller, the Arduino Uno.

Servos with Arduino Uno

The Arduino Uno is an 8-bit microcontroller board based on the ATmega328P. It has 14 digital input/output pins, of which six can be used as PWM outputs.  These outputs can all be used to drive a servo motor.

The I/O pins on the Uno are 5-volt logic, which is great for driving servo motors.

The Uno also has a 5-volt output for powering external sensors and displays. You should NEVER use this to power a servo motor; servo motors should ALWAYS be powered by their own power supply.

Arduino Servo Library

The Arduino Servo Library is a built-in library that makes it easy to control hobby servos from most Arduino boards. The library uses the Arduino’s timers to produce a stable 50 Hz output signal.

By default, the library works with servos that expect PWM pulse widths of 1ms to 2ms. However, you can also directly specify the PWM pulse width for servo motors with an extended range.

The primary functions of the servo library are as follows:

attach(pin)

  • Associates a servo object with a specific Arduino pin for output.
  • Example: myServo.attach(9);

attach(pin, minPulse, maxPulse)

  • Same as attach(pin) but also allows specifying custom pulse width bounds (in microseconds).
  • Example: myServo.attach(9, 600, 2400);

detach()

  • Stops sending control signals to the servo, freeing the pin and allowing the servo to move freely or reduce power draw.
  • Example: myServo.detach();

write(angle)

  • Writes an angle to the servo (0–180 degrees); the library converts this to an appropriate pulse width (usually ~1 ms to ~2 ms).
  • Example: myServo.write(90); // Move servo to 90°

writeMicroseconds(us)

  • Directly specify the pulse width in microseconds, allowing fine-grained or non-standard positioning.
  • Example: myServo.writeMicroseconds(1500); // ~center position

read()

  • Returns the last written angle (0–180) sent by write().
  • Example: int currentAngle = myServo.read();

readMicroseconds()

  • Returns the last written pulse width in microseconds (from writeMicroseconds()).
  • Example: int currentPulse = myServo.readMicroseconds();

attached()

  • Returns true if a servo is currently attached to a pin, or false otherwise.
  • Example: if (myServo.attached()) { … }

Arduino Servo Motor Hookup

The Arduino Uno has 6 PWM-capable I/O pins. Most Uno boards identify them with a “waveform” or “tilde” (~) mark. Any of these pins can be used to drive a servo motor; we will be using pin 9.

Just about any hobby servo motor can be used in the experiment. Make sure you know the operating voltage and provide a suitable power supply. Six AA cells make a convenient servo motor power supply.

Ensure you don’t use the Arduino’s 5-volt output for the servo!

Remember to tie the servo power supply ground and Arduino ground together.

Arduino Test Code

The library is very easy to use, as the following code sample will illustrate.

We begin by including the Arduino Servo Library.

Next, we create an object called myServo and define a variable to hold its position (in degrees).  We also define the servo I/O pin, pin 9 (If you decide to move the servo to a different pin, you’ll need to change the entry here).

In setup, we attach the servo object to the servo pin.  If we were using multiple servo motors, we would have a different object for each one. Remember, the servo requires a PWM-capable pin on the Arduino Uno.

The Loop illustrates how we use a servo write to set the servo’s position. The servo moves immediately to the position specified in degrees, from 0 to 180. In this sketch, we sweep it back and forth, with a slight delay between moves to allow the servo motor to catch up.

Run the sketch and observe the servo motor. You’ll want to put a horn on to make it easier to see it move.

Remember, the library expects a 180-degree servo motor with 1 to 2 milliseconds pulse widths. If you need to use a motor with different specifications, you can do it as follows:

  • Use the long form of the attach function to specify pulse width parameters.
  • Specify position by pulse width instead of degrees

Doing this lets you drive virtually any servo motor using the Arduino Servo library.

Servos with ESP32

The ESP32 makes an excellent servo motor controller. Almost all of its 3.3-volt logic level outputs support PWM, and its high-precision timers generate very accurate waveforms.

You cannot, however, use the Arduino Servo Motor Library with an ESP32. The library relies upon Arduino-specific timers (actually ATMega328-specific) to operate, and it won’t work with the ESP32’s internal timers.

Fortunately, there are several servo motor libraries to choose from, including one that is almost a clone of the Arduino Servo Motor Library.

ESP32Servo Library

The ESP32Servo library is a popular and widely used library for controlling servo motors. It is a port of the Arduino Servo library optimized for the ESP32’s architecture. The library leverages the ESP32’s PWM timers to control up to 16 servos on individual channels, making it a versatile choice for servo control.

Here are the functions and commands included in this library:

Servo Class

  • attach(pin) – Attaches servo to a pin
  • attach(pin, min, max) – Attaches servo with custom min/max pulse widths
  • write(angle) – Writes angle to the servo (0-180)
  • writeMicroseconds(us) – Writes pulse width in microseconds
  • read() – Returns current angle
  • attached() – Returns true if the servo is attached
  • detach() – Detaches servo from pin

ESP32PWM Class (For advanced control)

  • allocateTimer(timer) – Allocates a timer for PWM generation
  • attachPin(pin, freq, resolution) – Attaches a pin for PWM control
  • writeScaled(duty, min, max) – Writes a scaled duty cycle

You’ll note that the basic ESPServo commands mimic commands for the Arduino Servo Library. This is intentional; this library is meant to allow the use of older Arduino code with an ESP32.

ESP32 Servo Motor Hookup

You can use almost any ESP32 module for this experiment.  I used a Seeeduino XIAO ESP32-S3 module and hooked it to a servo motor, as illustrated in the following diagram:

Once again, please note that a suitable power supply will be required; I used four AA batteries to provide 6 volts.

Another thing worth mentioning is that pin D9 on the XIAO ESP32-S3 is actually ESP32 GPIO 8. You’ll need to note that when you write the code for the servo motor.

ESP32 Test Code

Here is some code that accomplishes the same objective as the Arduino code—move the motor shaft back and forth.

The code has many similarities to the Arduino code.  

We start by including the ESP32Servo Library and creating an object, “myservo,” to represent the servo motor.

We also allocate the servo pin; as noted above, this is GPIO 8 on the ESP32.

In setup, we allocate all four of the ESP32’s PWM timers.  We then set the PWM period (frequency) to 50 Hz and the minimum and maximum PWM pulse width. You can determine these values from your servo motor spec sheet.

The loop is quite similar to the Arduino sketch. We use two counters, incrementing and decrementing, to step the motor through a 0 to 180-degree range.

Load it up and watch it work! It will work identically to the Arduino Uno example.

Other ESP32 Servo Motor Libraries

The ESP32Servo Library can perform every task you will need for basic servo motor movements. But there are also other libraries that you can use, some with additional features and some that work in a different fashion.

ServoEasing Library

The ServoEasing library adds smooth ease-in and ease-out movement to servo control, creating smoother and more natural motion. It supports multiple easing functions such as linear, quadratic, cubic, sine, circular, and more.

ServoEasing Class

  • attach(pin) – Attaches servo to a pin
  • attach(pin, minPulseWidth, maxPulseWidth) – Attaches with custom pulse widths
  • setEasingType(easingType) – Sets the easing type (LINEAR, QUADRATIC, CUBIC, etc.)
  • setSpeed(degreesPerSecond) – Sets speed in degrees per second
  • setSpeedForAllServos(degreesPerSecond) – Sets speed for all servos
  • startEaseTo(endDegree) – Starts non-blocking movement with easing
  • startEaseTo(endDegree, speed) – Starts movement with custom speed
  • startEaseToD(endDegree, milliseconds) – Starts movement with fixed duration
  • easeTo(endDegree) – Blocking movement with easing
  • easeTo(endDegree, speed) – Blocking movement with custom speed
  • easeToD(endDegree, milliseconds) – Blocking movement with a fixed duration
  • setEasingTypeForAllServos(easingType) – Sets easing type for all servos
  • update() – Updates non-blocking servo movements
  • wait() – Waits for all servos to complete movement
  • isMoving() – Returns true if the servo is moving

Easing Types

  • EASE_LINEAR – Constant speed
  • EASE_QUADRATIC_IN – Accelerating from zero velocity
  • EASE_QUADRATIC_OUT – Decelerating to zero velocity
  • EASE_QUADRATIC_IN_OUT – Acceleration until halfway, then deceleration
  • EASE_CUBIC_IN, EASE_CUBIC_OUT, EASE_CUBIC_IN_OUT – Cubic easing
  • EASE_QUARTIC_IN, EASE_QUARTIC_OUT, EASE_QUARTIC_IN_OUT – Quartic easing
  • EASE_SINE_IN, EASE_SINE_OUT, EASE_SINE_IN_OUT – Sinusoidal easing
  • EASE_CIRCULAR_IN, EASE_CIRCULAR_OUT, EASE_CIRCULAR_IN_OUT – Circular easing
  • EASE_BOUNCE_IN, EASE_BOUNCE_OUT, EASE_BOUNCE_IN_OUT – Bouncing effect

The library can be installed directly from the Arduino IDE and comes with many example files. Most of the examples are for multiple servos, including this one:

ESP32_ISR_Servo Library

The ESP32_ISR_Servo library is different in that it uses interrupt timers to control servos, providing more reliable and precise control. This approach minimizes conflicts with other operations, making it suitable for complex projects with critical timing.

ESP32_ISR_Servo Library Functions

  • ESP32_ISR_Servos.setupServo(pin, min, max) – Sets up a servo with pulse range
  • ESP32_ISR_Servos.setPulseWidth(index, pulseWidth) – Sets pulse width
  • ESP32_ISR_Servos.setPosition(index, position) – Sets position (0-180)
  • ESP32_ISR_Servos.getPosition(index) – Gets current position
  • ESP32_ISR_Servos.deleteServo(index) – Removes a servo
  • ESP32_ISR_Servos.enableAll() – Enables all servos
  • ESP32_ISR_Servos.disableAll() – Disables all servos
  • ESP32_ISR_Servos.toggle(index) – Toggles a servo between enabled/disabled
  • ESP32_ISR_Servos.enable(index) – Enables a specific servo
  • ESP32_ISR_Servos.disable(index) – Disables a specific servo

The library can be installed directly from the Arduino IDE and comes with many examples, including this one:

ESP32ServoController Library

The ESP32ServoController library is an excellent solution for controlling multiple servos with high precision. It’s designed to overcome timing issues and performs better for applications requiring exact timing control. This high performance is achieved using the ESP32’s MCPWM peripheral for more accurate PWM signal generation.

Servo and ServoChannel Classes

  • ServoChannel::attach(pin) – Attaches a channel to a pin
  • ServoChannel::setAngle(angle) – Sets angle (0-180)
  • ServoChannel::setPulseWidth(us) – Sets pulse width in microseconds
  • ServoChannel::getPulseWidth() – Gets current pulse width
  • ServoChannel::detach() – Detaches channel from pin
  • Servo::attach(pin) – Attaches servo to pin
  • Servo::detach() – Detaches servo
  • Servo::attach(pin, minUs, maxUs) – Attaches with custom pulse range
  • Servo::write(angle) – Sets angle (0-180)
  • Servo::writeMicroseconds(us) – Writes pulse width in microseconds
  • Servo::read() – Reads current angle

As with the previous libraries, several example files are included with this library. Thus one controls two servo motors:

PCA9685 16-Channel PWM Driver

The PCA9685 is a 16-channel, 12-bit pulse-width modulation (PWM) driver commonly used for controlling multiple servos or LEDs. It communicates via the I2C protocol and can operate on 3.3 V or 5 V logic. 

Each of its 16 output channels provides 4096 steps of PWM resolution, enabling fine control over servo positions or LED brightness. The PCA9685 is ideal for animating robotics projects or driving multiple RC servos from a single microcontroller.

PCA9685 Basics

It should be noted that the PCA9685 was not initially intended for use with servo motors. NXP developed the PCA9685 integrated circuit as a 16-channel LED controller, and some of its features are more suited to LEDs than servo motors.

Because of its obvious use for servo motors, Adafruit built a module around this board for use with servos. This essential means that the integrated circuit was mounted on a PCB with 16 servo connectors, connectors for I/O and power, and a connector for an independent servo motor power supply. As Adafruit made the module open source, several manufacturers have copied it. It has also been reworked as a Raspberry Pi HAT.

Using a PCA9685 has several advantages over using a microcontroller to drive a servo motor directly. As the PCA9685 has its own timing circuit, it doesn’t rely on the sometimes intermittent timing from a microcontroller, which can be affected if the microcontroller gets busy.

PCA9685 Module Pinout

Most PCA9685 modules follow the same basic design as the Adafruit module. This is illustrated in the diagram below:

The Logic & Power connections are duplicated on each edge of the board. This makes it easier to cascade these modules. You may connect up to 62 modules, each with its own I2C address.  The Logic & Power connections are as follows:

  • GND – Ground.
  • OE – Output enable. When held LOW, the servo motor outputs are enabled.  It is LOW by default, so it may be left unconnected.
  • SCL – I2C Clock.
  • SDA – I2C Data.
  • VCC – Logic Power supply. This can be 3.3 or 5 volts.
  • V+ – Motor power supply. Note that this connection does not have reverse polarity protection.

The bottom of the module has connectors for 16 servo motors (or LEDs). 

On the top of the board is a terminal strip for connecting motor power. Using this strip adds the benefit of reverse voltage protection.  Keep in mind that the power supply should be able to supply enough current to handle a situation where every servo motor has stalled.

To the right of the terminal strip are six solder pads. These can be used as jumpers to set the I2C address of the board. If left unused, the default I2C address is 0x40. 

The PCA9685 module also has a filter capacitor for the motor power supply. This is not installed on some boards, allowing the user to select an appropriate value. The general rule of thumb for determining the capacitor value is 100 microfarads per motor.

I2C Addresses & Interfacing

The PCA9685 communicates via I2C and can work with 3.3 or 5-volt logic. No pullup resistors are included in the module; you can add them for long data runs.

The PCA9685 has a default I2C address of 0x40. You can change the address using the address selection pins (A0-A5). Each pin adds a binary value to the base address when connected to VCC (by shorting the jumper with solder). This lets you connect up to 62 devices to one I2C bus.

0x70 Address

There is also the “all-call address” of 0x70. This address will allow you to send commands to every PCA9685 on the I2C bus. This is used to reset all the devices simultaneously, although it is more applicable to LEDs than servo motors.

The “all-call address” must be enabled using the PCA9685 Mode Registers. The Adafruit library we will use with the PCA9685 can read and write these registers.

PCA9685 Timing

The PCA9685 uses a 12-bit resolution for PWM, meaning each time slice (or PWM cycle) is divided into 4096 steps. Each step corresponds to a specific point in time within the cycle, and the output can be either HIGH or LOW during each step. Here’s how it works:

Time Slice (PWM Period)

  • The total duration of the PWM cycle is determined by the frequency set for the PCA9685. For example, at a frequency of 50 Hz (commonly used for servo motors), the period of one PWM cycle is 20 ms.
  • This 20 ms period is divided into 4096 equal steps, each lasting approximately 4.88 microseconds (µs).  These steps are referred to as Ticks.

ON and OFF Counters

  • Each channel on the PCA9685 has two 12-bit registers: an ON counter and an OFF counter.
  • The ON counter specifies the Tick number at which the signal changes from LOW to HIGH.
  • The OFF counter specifies the Tick number at which the signal changes from HIGH to LOW.

PCA9685 Hookup

I’m using the Seeeduino XIAO ESP32-S3 board again, but you can use another ESP32 module if you wish. Actually, just about any microcontroller that supports I2C can be used.

I connected four small servo motors to the first four servo connectors (0 – 3). I also used a 6-volt power supply of four AA batteries to power the servo motors. Your computer powers the XIAO via the USB-C connector.

In addition to the I2C clock and data lines, I have wired the Output Enable (OE) pin to a GPIO on the ESP32. This is optional, but it can be used to disable all servo motors if required. Leave it disconnected if you prefer.

PCA9685 ESP32 Code

The Adafruit PWMServoDriver Library simplifies writing C++ code for the PCA9685 module. This library provides all the functions for configuring and using the PCA9685 for servo motors and LEDs. It includes some example sketches to get you started.

Here is a sketch that uses the Adafruit library to drive our four servo motors. You can easily expand it to drive up to 16 servo motors; just be sure your power supply can accommodate that many.

We start by including the Wire library (for I2C) and the Adafruit PWMServoDriver libraries. After that, we initialize the PWMServo library using the default I2C address. If you change the address using the jumpers, you can specify it here.

We then set the minimum and maximum pulse widths in Ticks (not microseconds). 

In the Setup, we start the PWM object we defined and set its frequency to 50 Hz.

Next, we define a function setServoAngle that accepts an angle and calculates the number of Ticks required to generate the correct pulse width. It is then passed to the set PWM command, with the starting Tick value of zero.  We also pass through the channel numbers, from 0 to 15, to specify which motor we are controlling.

In the Loop, we simply step through the four servos. For each one, we do the same thing we have done in previous demonstrations: cycle the motors back and forth 180 degrees.

Load it up and test it out. You should see the four motors move in sequence.

You can adjust the number of motors by changing the “4” in the integer “i” in the first for loop.

Servo Motor Troubleshooting

Servo motors are fantastic devices that can resolve many design challenges but don’t always perform as you want. In this section, I’ll outline some common servo motor problems and provide advice for rectifying them.

Power Supply Issues

Servo motors can draw significant currents, especially when under load or multiple servos are used. Insufficient power can lead to jittering, erratic movement, or complete failure.

Common Problems

  • Undersized or Unstable Power Supply – Servos often draw significant current, especially under load—much more than a typical onboard regulator can provide. If the power supply voltage sags or browns out, servos will jitter or reset.
  • Voltage Mismatch –  Many hobby servos are rated for ~4.8–6.0 V, while some high-torque digital servos can run at 7.4–8.4 V. Feeding the wrong voltage range can cause poor performance or permanent damage.

Solutions

  • Use a Dedicated Servo Power Supply—Provide a separate 5–6 V (or higher if the servo supports it) regulated supply capable of supplying enough current for all servos. Never use a microcontroller’s 5 V pin to power a servo motor, even a small one.
  • Ensure Common Ground – Always tie the servo power and microcontroller ground together. A missing or intermittent ground link is a frequent cause of random servo jitter.
  • Check Capacity and Wiring – Use sufficiently thick wires for servo power lines, and consider distributing power along multiple points or using a bus bar if powering many servos.
  • Add Filter Capacitors – Use capacitors across the servo power lines to stabilize voltage and reduce noise.

PWM Driver Issues

As servo motors are controlled using Pulse Width Modulation (PWM), any imperfection in the PWM signal can affect performance.

Common Problems

  • Incorrect PWM Frequency – Most hobby servos expect about a 50 Hz signal. If the frequency is too high or too low, the servo may behave erratically or fail to hold position.
  • Improper Pulse Width Range – Standard servos typically respond to pulse widths from ~1 ms (minimum) to ~2 ms (maximum). Sending pulses outside this range can cause mechanical stress or unpredictability.
  • Software Timing or Library Conflicts – If other tasks or interrupts interfere with the PWM generation, you can get jitter or missed pulses.

Solutions

  • Verify Frequency Settings – Double-check library or timer settings to confirm the PWM frequency is set to ~50 Hz for standard servos. Some digital servos can tolerate higher frequencies (e.g., up to 300 Hz), but verify specifications first.
  • Use Established Libraries – Rather than manually writing PWM code, use reliable libraries such as Arduino’s “Servo.h,” the ESP32Servo library, or the Adafruit PCA9685 library. These libraries handle low-level timing details and help ensure stable signals.
  • Scope or Frequency Counter – If possible, use an oscilloscope or frequency counter to confirm the pulse widths and frequency delivered to the servo.

Analog vs. Digital Servo Considerations

Sometimes, a digital servo motor makes sense instead of an analog one. In some situations, it’s the other way around.

Common Problems

  • Analog Servo Jitter – Analog servos often exhibit noticeable jitter or humming, especially under low loads or near their center positions, if the supply voltage is slightly unsteady or if there’s electrical noise.
  • Digital Servo Overload – Digital servos can draw significantly higher current (due to more aggressive internal PID loops), causing brownouts or resets in underpowered systems. They also produce more substantial holding torque, which is excellent for control but can strain a weak power setup.
  • Wrong Servo Choice – Some builders use analog servos in demanding applications needing high torque and rapid response, only to find the servo cannot handle the load or speed. Conversely, using digital servos in a simple, low-power scenario may be unnecessarily expensive or cause unexpected power demands.

Solutions

  • Switching Servo Types – If an analog servo is jittering due to small position changes, a digital servo’s tighter control loop may help at the cost of a higher current draw. Alternatively, an analog servo might suffice if you only need moderate torque and prefer minimal power draw at idle.
  • Match Specs to Application – Check each servo’s torque, speed, and voltage requirements. Digital servos often benefit high-performance robotic arms, heavy RC vehicles, or 3D-printed mechanical linkages. Simple flaps, basic rotating mechanisms, or small hobby applications do well with analog servos.
  • Tune or Program Digital Servos – Many digital servos are programmable, so you can adjust deadband, torque limiting, or response speed to combat issues like overcurrent or overshoot.

Interference

Common Problems

  • Signal Noise on Control Line – Long servo leads can pick up electromagnetic interference (EMI), causing random twitches, especially if the wires run near high-current motors or wireless transmitters.
  • Ground Loop Noise – Complex wiring or multiple return paths can introduce ground loops, adding noise to the servo signal.

Solutions

  • Use Shielded Wires – If the servo cables are long, use shielded servo cables to minimize EMI pickup.
  • Ferrite Beads or Chokes – Placing ferrite beads near the servo connector can filter high-frequency interference. These clip-on chokes are especially helpful in RC environments with high-power motors and speed controllers.
  • Proper Routing – Keep servo signal wiring away from high-current lines or switching regulators. Route servo lines in a separate bundle to avoid inductive coupling.
  • Other Motors – If using a brushed DC motor near the servo, add capacitors across the brushed motor terminals to reduce noise.

These four categories—power supply, PWM generation, servo type differences, and noise/interference—cover the most frequent root causes of DC servo motor malfunctions. By systematically addressing each area, you can significantly improve the reliability and performance of your servo-driven projects.

Conclusion

Servo motors are vital components in robotics and many hobby activities. They provide a method of adding controlled motion to your project. As we have seen, they are very easy to control using microcontrollers like Arduino and ESP32.

I hope you have enjoyed this rather long servo motor guide and that you find the code samples useful. The ZIP file you can download with the code also contains some MicroPython examples you can try with both a single servo motor and the PCA9685.

Parts List

Here are some components you might need to complete the experiments in this article. Please note that some of these links may be affiliate links, and the DroneBot Workshop may receive a commission on your purchases. This does not increase the cost to you and is a method of supporting this ad-free website.

Micro Servo SG90 – Amazon

Micro Servo MG90 – Amazon

Standard Servo MG995 – Amazon

Standard High Torque Servo (180-Degrees) DS3235 – Amazon

Standard Hi Torque Servo (270-Degrees) DS3235 – Amazon

Giant Servo DS51150 – Amazon

Adafruit PCA9685 – Adafruit

 

Resources

Code Samples – All the code used in this article in one easy-to-use ZIP file!

Article PDF – A PDF version of this article in a ZIP file.

Servo Splines – ServoCity Glossary for Servo Motors

 

DC Servo Motor Guide – With ESP32 & Arduino
Summary
DC Servo Motor Guide - With ESP32 & Arduino
Article Name
DC Servo Motor Guide - With ESP32 & Arduino
Description
Everything you need to know about using servo motors with microcontrollers like the ESP32 and Arduino. This guide explains how servo motors work and how you can use them in your projects.
Author
Publisher Name
DroneBot Workshop
Publisher Logo
Tagged on:     
Subscribe
Notify of

0 Comments
Oldest
Newest
Inline Feedbacks
View all comments