How to use PCA9685 16-Channel 12-Bit PWM Servo Driver with Arduino.

PCA9685 16-Channel 12-bit servo motor driver with Arduino

The PCA9685 16-channel 12-bit PWM servo motor driver is used to control up to 16 servo motors using I2C communication with microcontrollers like Arduino and Raspberry Pi. Normally these microcontrollers have a limited number of PWM pins for controlling servo motors.

With the PCA9685 servo motor driver module, more motors can be used with just two pins of the microcontroller and the motors are plugged directly onto the board therefore no additional components are required. This is very useful in projects involving use of many servo motors like in hexapods, quadcopter drones, robotic arms, spider robots and RC vehicles.

In this tutorial I will be demonstrating how to use the PCA9685 16-Channel 12-bit PWM servo motor driver module with Arduino

PCA9685 16-Channel 12-bit Hardware Overview.

The PCA9685 16-channel servo motor driver board has control pins on either side where you can use one set of control inputs to connect the board to an Arduino board and the pins on the opposite end are used to chain one driver to another.

PCA9685 16 channel servo motor driver pinout

The pinout for PCA9685 16-channel servo motor driver is as follows;

  • GND – this is the ground pin
  • VCC – is the logic power pin and should be 3V to 5V
  • V+ – this is an external power supply for the servo motors which should be 5 to 6V. However, it is always better to use the power supply terminal block at the top of the board to supply power to the servo motors.
  • SCL – I2C clock pin
  • SDA – I2C data pin
  • OE – This is the Output Enable pin which is pulled LOW by default hence making all pins enabled. If this pin is HIGH, all the output pins will be disabled.
  • Output ports: There are 16 output ports each with V+, GND and PWM pins where the servo motors are connected to the PCA9685 board. These outputs are 12-bit, that’s a resolution of 4096 programmable steps.

Should you include the on-board capacitor?

The PCA9685 module board comes with a through-hole capacitor slot where an electrolytic capacitor can be soldered depending on how you intend to use this module.

If your project involves many servo motors and the power supply is not very stable or generates noise, it is necessary to include a capacitor on the module board. The value of the capacitor is normally estimated as n*100µF where n is the number of servos. For example, in my case I used a 1000µF capacitor.

Connecting the PCA9685 16-Channel Servo driver module to Arduino.

The connection is done as illustrated below. The SCL and SDA pins of the driver board are attached to analog pins A5 and A0 since these are the I2C pins for Arduino UNO. Make sure you use the correct pins if you are using a different Arduino board.

PCA9685 16-Channel servo motor driver with Arduino schematic diagram

5V power for running the motors is supplied using the two terminal block pins. The Arduino board should be powered from a different power source but its 5V pin is connected to VCC of the motor driver.

The PWM output pins on the PCA9685 12-Channel servo motor driver module are capable of sinking a maximum of 25mA or sourcing a maximum of 10mA. Do not attempt to drive high current motors, bulbs or other devices directly from these pins as you will risk damaging the module.

Code for controlling servo motors using PCA9685 16-Channel servo driver module using Arduino.

Before writing code for using this driver module you need to download and include the Adafruit-PWM-Servo-Driver library in your Arduino IDE.

The example code below is for performing a sweeping movement of servo motors from 0 to 180 ° and backwards from 180 ° to 0 °. This sweep is done for all servos motors attached to the PCA9685 driver module one after the other.

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver myServo = Adafruit_PWMServoDriver();

#define SERVOMIN 150
#define SERVOMAX 600

uint8_t servonum = 0;
uint8_t numberOfServos = 6;

void setup() {

void loop() {
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++){
    myServo.setPWM(servonum, 0, pulselen);
  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--){
    myServo.setPWM(servonum, 0, pulselen);
  servonum ++;
  if (servonum > numberOfServos-1) 
     servonum = 0;

Code Explanation

First include the Wire.h library for I2C communication and Adafruit_PWMServoDriver.h library for controlling the PCA9685 servo motor driver.

Then Adafruit_PWMServoDriver object is created and given any suitable name for example ‘myServos’. The Adafruit_PWMServoDriver() method takes the I2C address of the PCA9685 module board. If no address is specified then the default value is used which is 0x40. I have explained how to modify the I2C address in a later part of the tutorial.

Next you have to define the minimum (SERVOMIN) and maximum (SERVOMAX) pulse width value for the given servo motors and these correspond with 0 ° and 180 ° positions of the servo motor. The Adafruit library code comes with vales of 150 and 600 for the minimum and maximum pulse width respectively.

These values vary depending on the model and manufacturer of the motor therefore you need to adjust the pulse width range to match the servos you are using to avoid hitting the hard stop which can destroy the motors. In my case I used values of 125 and 575 for SERVOMIN and SERVOMAX respectively.

In the setup section of the code, the myServos object is initialized using the begin() method and also the PWM frequency for the 16 output pins is configured using the setPWMFreq(60) method.

The frequency is set to 60Hz which is equivalent to a PWM signal with 16.6ms period that is within the duty cycle range of most servos.

The loop section contains the code for controlling the movement of the servo motors depending on the pulse width values set.

The function setPWM(n_servo, on, off) is used to set the pulse width of a PWM output.

  • n_servo is the number of servo or output to be configured (0 to 15)
  • on is the value the PWM signal rising edge, that is, when the signal should transition from low to high. Since it is 12-bit signal, the value should be between 0 to 4095.
  • off is the value of PWM signal falling edge, that is, when the signal should transition from high to low and the value ​​​​must also be between 0 and 4096.

Two ‘for’ loops have been used to determine the movement of servo motors from 0 to 180 ° and backwards from 180 ° to 0 ° using the set pulse width.

Using multiple PCA9685 12-channel Servo motor drivers.

One of the major advantages of PCA9685 servo motor driver is that you can daisy-chain up to 62 of

 these driver modules on a single I2C bus. This means that you can control up to 992 servos using only two I2C pins from the Arduino board.

To be able to use the different modules, you need to set a different I2C address for each board and this is done using the six-address selection solderable pads (A0 – A5) where you have to connect these pads depending on the address you like to give a specific board.

The table below shows some examples of how the solderable pads can be connected to assign specific I2C addresses. Normally the solderable pads come without any connection giving the default I2C address as 0x40.

PCA9685 I2C Address select pins

The diagram below shows how you can daisy-chain three PCA9685 16-Channel servo motor driver modules. Each module has to be given a different I2C address by soldering the I2C address selection pads.

connecting three pca9685 16-channel servo motor driver modules to Arduino.

When writing code for controlling servo motors attached to these daisy-chained driver modules, you have to create a separate object for each board and also initialize each of these objects separately in the setup section of the code like demonstrated below.

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver myServo1 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver myServo2 = Adafruit_PWMServoDriver(0x41);
Adafruit_PWMServoDriver myServo3 = Adafruit_PWMServoDriver(0x4A);

void setup() {




You may also like...