How Servo Motors Work & How To Control Servos using Arduino

In this tutorial we will learn how servo motors work and how to control servos using Arduino and PCA9685 PWM driver. You can watch the following video or read the written tutorial below.

Overview

There are many types of servo motors and their main feature is the ability to precisely control the position of their shaft. A servo motor is a closed-loop system that uses position feedback to control its motion and final position.

 

Closed-loop System

In industrial type servo motors the position feedback sensor is usually a high precision encoder, while in the smaller RC or hobby servos the position sensor is usually a simple potentiometer. The actual position captured by these devices is fed back to the error detector where it is compared to the target position. Then according to the error the controller corrects the actual position of the motor to match with the target position.

Servo Motor Closed Loop System

In this tutorial we will take a detailed look at the hobby servo motors. We will explain how these servos work and how to control them using Arduino.

Controlling multiple servo motors with Arduino and PCA9685 PWM Driver

Hobby servos are small in size actuators used for controlling RC toys cars, boats, airplanes etc. They are also used by engineering students for prototyping in robotics, creating robotic arms, biologically inspired robots, humanoid robots and so on.

Servo Motor Applications Robotic Arm Hexapod Humanoid Robot

How RC/ Hobby Servos Work

Inside a hobby servo there are four main components, a DC motor, a gearbox, a potentiometer and a control circuit. The DC motor is high speed and low torque but the gearbox reduces the speed to around 60 RPM and at the same time increases the torque.

How Hobby Servo Works Inside Components DC Motor Potentiometer Control Circuit

 

The potentiometer is attached on the final gear or the output shaft, so as the motor rotates the potentiometer rotates as well, thus producing a voltage that is related to the absolute angle of the output shaft. In the control circuit, this potentiometer voltage is compared to the voltage coming from the signal line. If needed, the controller activates an integrated H-Bridge which enables the motor to rotate in either direction until the two signals reach a difference of zero.

A servo motor is controlled by sending a series of pulses through the signal line. The frequency of the control signal should be 50Hz or a pulse should occur every 20ms. The width of pulse determines angular position of the servo and these type of servos can usually rotate 180 degrees (they have a physical limits of travel).

 

RC Servo Motor Control Signal

Generally pulses with 1ms duration correspond to 0 degrees position, 1.5ms duration to 90 degrees and 2ms to 180 degrees. Though the minimum and maximum duration of the pulses can sometimes vary with different brands and they can be 0.5ms for 0 degrees and 2.5ms for 180 degrees position.

Arduino Servo Motors Control

Let’s put the above said to test and make a practical example of controlling a hobby servo using Arduino. I will use the MG996R which is a high-torque servo featuring metal gearing with stall torque of 10 kg-cm. The high torque comes at a price and that’s the stall current of the servo which is 2.5A. The running current is from 500mA to 900mA and the operating voltage is from 4.8 to 7.2V.

Arduino Servo Motor Control MG996R

The current ratings indicate that we cannot directly connect this servo to the Arduino, but we must use a separate power supply for it. Here’s the circuit schematic for this example.

Arduino Servo Control Circuit Schematic

We simply need to connect the control pin of the servo to any digital pin of the Arduino board, connect the Ground and the positive wires to the external 5V power supply, and also connect the Arduino ground to the servo ground.

Arduino Servo Motor Control Code

Now let’s take a look at the Arduino code for controlling the servo motor. The code is very simple. We just need to define the pin to which the servo is connect, define that pin as an output, and in the loop section generate pulses with the specific duration and frequency as we explained earlier.

/*
     Servo Motor Control - 50Hz Pulse Train Generator
           by Dejan, https://howtomechatronics.com
*/

#define servoPin 9

void setup() {
  pinMode(servoPin, OUTPUT);
}

void loop() {
   // A pulse each 20ms
    digitalWrite(servoPin, HIGH);
    delayMicroseconds(1450); // Duration of the pusle in microseconds
    digitalWrite(servoPin, LOW);
    delayMicroseconds(18550); // 20ms - duration of the pusle
    // Pulses duration: 600 - 0deg; 1450 - 90deg; 2300 - 180deg
}

After some testing I came up with the following values for the duration of the pulses that work with my servo. Pulses with 0.6ms duration corresponded to 0 degrees position, 1.45ms to 90 degrees and 2.3ms to 180 degrees.

I connected a multimeter in series with the servo to check the current draw. The maximum current draw that I noticed was up to 0.63A at stall. Well that’s because this isn’t the original TowerPro MG996R servo, but a cheaper replica, which obviously has worse performance.

MG996R Servo Motor Current Draw

Nevertheless, let’s take a look at a more convenient way of controlling servos using Arduino. That’s using the Arduino servo library.

/*
     Servo Motor Control using the Arduino Servo Library
           by Dejan, https://howtomechatronics.com
*/

#include <Servo.h>

Servo myservo;  // create servo object to control a servo

void setup() {
  myservo.attach(9,600,2300);  // (pin, min, max)
}

void loop() {
  myservo.write(0);  // tell servo to go to a particular angle
  delay(1000);
  
  myservo.write(90);              
  delay(500); 
  
  myservo.write(135);              
  delay(500);
  
  myservo.write(180);              
  delay(1500);                     
}

Here we just need to include the library, define the servo object, and using the attach() function define the pin to which the servo is connected as well as define the minimum and maximum values of the pulses durations. Then using the write() function we simply set the position of the servo from 0 to 180 degrees. With this library we can drive up to 12 servos at the same time or 48 servos using Arduino Mega board.

Arduino and PCA9685 PWM/ Servo Driver

There’s also another way of controlling servos with Arduino, and that’s using the PCA9685 servo driver. This is a 16-Channel 12-bit PWM and servo driver which communicates with Arduino using the I2C bus. It has a built in clock so it can drive 16 servos free running, or independently of Arduino.

Arduino and PCA9685 PWM Servo Driver

What’s even cooler we can daisy-chain up to 62 of these drivers on a single I2C bus. So theoretically we can control up to 992 servos using only the two I2C pins from the Arduino board. The 6 address select pins are used for setting different I2C addressed for each additional driver. We just need to connect the solder pads according to this table.

PCA9685 Address Select Pins Table I2CAddress

Here’s the circuit schematic and we can once again notice that we need a separate power supply for the servos.

Arduino and PCA9685 Circuit Schematic

You can get the components needed for this example from the links below:

  • MG996R Servo Motor ……………………………….. Amazon / Aliexpress
  • PCA9685 PWM Servo Driver  ……………….……. Amazon /  Aliexpress
  • Arduino Board ………………………………………….. Amazon / Aliexpress
  • 5V 6A DC Power Supply …………………………….. Amazon / Aliexpress

*Please note: These are affiliate links. I may make a commission if you buy the components through these links.
I would appreciate your support in this way!

Now let’s take a look at the Arduino code. For controlling this servo driver we will use the PCA9685 library which can be downloaded from GitHub.

PCA9685 Arduino Code

/*
     Servo Motor Control using Arduino and PCA9685 Driver
           by Dejan, https://howtomechatronics.com
           
     Library: https://github.com/NachtRaveVL/PCA9685-Arduino
*/

#include <Wire.h>
#include "PCA9685.h"

PCA9685 driver;

// PCA9685 outputs = 12-bit = 4096 steps
// 2.5% of 20ms = 0.5ms ; 12.5% of 20ms = 2.5ms
// 2.5% of 4096 = 102 steps; 12.5% of 4096 = 512 steps
PCA9685_ServoEvaluator pwmServo(102, 470); // (-90deg, +90deg)

// Second Servo
// PCA9685_ServoEvaluator pwmServo2(102, 310, 505); // (0deg, 90deg, 180deg)


void setup() {
  Wire.begin();                 // Wire must be started first
  Wire.setClock(400000);        // Supported baud rates are 100kHz, 400kHz, and 1000kHz
  driver.resetDevices();        // Software resets all PCA9685 devices on Wire line

  driver.init(B000000);         // Address pins A5-A0 set to B000000
  driver.setPWMFrequency(50);   // Set frequency to 50Hz
}
void loop() {
  driver.setChannelPWM(0, pwmServo.pwmForAngle(-90));
  delay(1000);
  driver.setChannelPWM(0, pwmServo.pwmForAngle(0));
  delay(1000);
  driver.setChannelPWM(0, pwmServo.pwmForAngle(90));
  delay(1000);
}

So first we need to include the libraries and define the PCA9685 object. Then using the Servo_Evaluator instance define the pulses duration or the PWM output of the driver. Note that the outputs are 12-bit, or that’s a resolution of 4096 steps. So the minimum pulse duration of 0.5ms or 0 degrees position would correspond to 102 steps, and the maximum pulse duration of 2.5ms or 180 degrees position to 512 steps. But as explained earlier these values should be adjusted according your servo motor. In my case values from 102 to 470 corresponded to 0 to 180 degrees position.

In the setup section we need to define the I2C clock rate, set the driver address and set the frequency to 50Hz.

In the loop section, using the setChannelPWM() and pwmForAngle() functions we simply set the servo to the desired angle.

I connected a second servo to the driver, and as I expected, it wasn’t positioning the same as the first one, and that’s because the servos that I’m using are cheap copies and they are not so reliable. However, this isn’t a big problem because using the Servo_Evaluator instance we can set different output settings for each servo. We can also adjust the 90 degrees position in case it’s not precisely in the middle. In that way all servos will work the same and position at the exact angle.

Controlling a lot of servos with daisy chained PCA9685 drivers

We will take a look at one more example and that’s controlling a lot of servos with multiple chained PCA9685 drivers.

Controlling 22 Servo Motors with Arduino and PCA9685

For that purpose we need to connect the drivers to each other and connect the appropriate address select solder pads. Here’s the circuit schematic:

Multiple PCA9685 Drivers Daisy Chained Circuit

Let’s take a look at the Arduino code now.

/*
     Servo Motor Control using Arduino and PCA9685 Driver
           by Dejan, https://howtomechatronics.com
           
     Library: https://github.com/NachtRaveVL/PCA9685-Arduino
*/

#include <Wire.h>
#include "PCA9685.h"

PCA9685 driver;

// PCA9685 outputs = 12-bit = 4096 steps
// 2.5% of 20ms = 0.5ms ; 12.5% of 20ms = 2.5ms
// 2.5% of 4096 = 102 steps; 12.5% of 4096 = 512 steps
PCA9685_ServoEvaluator pwmServo(102, 470); // (-90deg, +90deg)

// Second Servo
PCA9685_ServoEvaluator pwmServo2(102, 310, 505); // (0deg, 90deg, 180deg)


void setup() {
  Wire.begin();                 // Wire must be started first
  Wire.setClock(400000);        // Supported baud rates are 100kHz, 400kHz, and 1000kHz
  driver.resetDevices();        // Software resets all PCA9685 devices on Wire line

  driver.init(B000000);         // Address pins A5-A0 set to B000000
  driver.setPWMFrequency(50);   // Set frequency to 50Hz
}
void loop() {
  driver.setChannelPWM(0, pwmServo.pwmForAngle(-90));
  delay(1000);
  driver.setChannelPWM(0, pwmServo.pwmForAngle(0));
  delay(1000);
  driver.setChannelPWM(0, pwmServo.pwmForAngle(90));
  delay(1000);
}

So we should create separate PCA9685 object for each driver, define the addresses for each driver as well as set the frequency to 50Hz. Now simply using the setChannelPWM() and pwmForAngle() functions we can set any servo at any driver to position any angle we want.

So that would be all for this tutorial, I hope you enjoyed it and learned something new. Feel free to ask any question in the comments section below and make sure you can my Arduino Projects Collection.

2 Responses

  1. Bart

    Dear Mechatronics,

    I am writing with very quick question (to you might also seem like silly one) regarding your “How Servo Motors Work & How To Control Servos using Arduino” video on YouTube.
    I am have just started working with Arduino and servos. Servos I went for ar exactly the same ones like yours in the video, which means that I also had to use external power source. Here is the challenge, I think I have used wrong powers source – because wires became hot. Please, would you mind sharing more information regarding power source you were using – and also how exacactly you have wired it up through the breadboard?
    Power source I have used 220V to 5V at 15A

    Bart

    Reply
    • Dejan Nedelkovski

      5V is the right power source, the amps rating doesn’t matter that are big in your case, it just means it can handle more servos working at the same time (depends on the servo current draw). The middle wire of the servo is 5V, the brown GND and the yellow is signal line.

      Reply

Leave a Reply

Your email address will not be published.