PCA9685 - Multiple Servo Control Using Arduino

Published  February 12, 2024   1
PCA9685 Multiple Servo Control

The 16-channel 12-bit PWM/Servo Driver, PCA9685, is an ideal module for precise control of multiple PWM outputs that are commonly used to control servos, LEDs, and other devices in applications such as robotics, automation, lighting, and servo motor control. In this project, we will use most of this module’s channels to control the Servo motors. At the same time, we will interact and learn all processes of controlling, interfacing, and coding this module so that you are well aware of the Unique use of this module. 

Components Required:

  • Arduino Uno R3
  • 8 * SG90 Servo Motor
  • PCA9685 16-Channel Module
  • External 5v-Adaptor
  • Jumper Wires

Servo Interfacing with Arduino

As you are well aware that Servo motors require PWM pulses to run and control them. The changing Duty Cycle PWM pulse can control the Angle of rotation. The Servo motors have three Wires named: VCC, GND, and PWM, and also a small internal circuitry present inside it.

Servo Motor Pinout

VCC: This wire is used to supply power to the servo motor—the positive voltage source, usually between 4.8V to 6V.

GND: This wire is the ground reference for the servo motor.

Signal (S or PWM): This wire sends control signals to the servo motor which connects to the PWM (Pulse Width Modulation) output pin on the microcontroller.

The Connection of the servo with Arduino is pretty simple and only requires three connections:

  • Connect VCC and GND of Servo to 5v and Ground to the Arduino
  • Connect any Digital PWM pin to the Servo’s Yellow (PWM) wire.

Then upload Sweep Sketch from the Servo.h library inside Arduino IDE to make it rotate from 0 to 180 and vice-versa.

Servo.h Library

Moreover, all deep details for understanding servo motors will be provided in the article on How to Control Servo Motor using Arduino

Multi-Servos Interfacing using Arduino

Most of you might ever use Arduino for any kind of project, it’s a commonly used microcontroller. The Arduino has only Six PWM output pins, meaning it can handle only Six servos at a time. Let’s say your robot project requires 8 servos to control synchronously the arm’s position. In this case, you need to change the microcontroller which will cost you even more, and the Controller won’t be fully utilized. And then there are only a few options left other than purchasing any different microcontroller like:

  • Using Multiplexing: The technique used to control multiple servos with limited PWM pins. This involves rapidly switching between servos, sending PWM signals to each one in turn. This can be done manually using code, or you can use dedicated ICs like the 74HC595 shift register to expand your PWM outputs. The technique cannot be easily implemented.
  • Software PWM: Connecting the servos to digital pins on the Arduino and writing code to generate PWM signals on the digital pins using the Arduino IDE will control the servos. 

Note: Both of the above methods are quite complex and cannot provide the accurate controlling of servos using PWM. In such a typical condition the PCA9685 module comes into the Picture.  

PCA9685 module with Arduino

PCA9685 module Compatible with Arduino and Raspberry Pi-like platforms, this module resolves the issue of limited PWM outputs on microcontrollers. By utilizing only 2 pins, it enables control of 16 free-running PWM outputs. 

Operated through I2C, this PWM driver includes a built-in clock, eliminating the need for continuous signal transmission from the microcontroller. It operates at 5V but remains 3.3V microcontroller-compatible, facilitating safe driving of outputs up to 6V. This feature is beneficial for controlling LEDs with 3.4+ forward voltages, such as white or blue ones. 

With 6 address select pins, you can connect up to 62 modules on a single I2C bus, providing 992 outputs.

The module comes with Six control pins or Arduino interfacing pins named: VCC, GND, SDA, SCL, OE, and V+.

Servo Driver Pinout

  • GND: Ground connection.
  • VCC: Power supply voltage (usually 3.3V to 5V) to power the PCA9685 module internal circuitry.
  • SDA (Serial Data Line): Connects to the I2C data line on your microcontroller.
  • SCL (Serial Clock Line): Connects to the I2C clock line on your microcontroller.
  • OE (Output Enable): Output Enable pin. Pulling this pin low will disable all outputs.
  • V+ (External Power): The pin on the PCA9685 module typically refers to providing the external power supply voltage for the servos or devices connected to the output channels.
  • Signal / PWM: The pin used to control the servos by changing the PWM duty cycle.

The Module combines a power of several different features and components which strengthen its effectiveness and beginner-friendly environment.

PCA9685 IC

Power Led indicator: Indicator Led often displays the power status of the module which is either ON or OFF.

16 Servo's Connection: There are 16 output ports, each equipped with three pins: V+, GND, and a PWM output. While the PWM outputs operate independently, they all need to share the same PWM frequency. For example, LEDs typically require a frequency of 1.0 KHz, while servos need 60 Hz. It's important to note that you cannot allocate half of the ports for LEDs at 1.0 KHz and the other half for servos at 60 Hz. The module is configured for servos, but it can also be utilized for LEDs. The frequency can be set/changed inside the Sketch code allowing to control of 16 particular identical devices at once.
Each PWM pin has a 220-ohm resistor in series, and the output logic aligns with VCC. It's crucial to consider these specifications, especially when dealing with LEDs, given their sensitivity to voltage and current. Additionally, the maximum current per pin is limited to 25mA.

Cascading another PCA9685 Module: The PCA9685 supports I2C communication, and you can cascade multiple PCA9685 modules together on the same I2C bus. All six header pins of the secondary module can be directly attached to the Vacant side pins of the previous module as you can see in the below image:

PCA9685 I2C Module

Each PCA9685 module needs to have a unique I2C address to avoid conflicts. The PCA9685 has a few hardware address jumpers (A0, A1, to A5) that you can use to set a different address for each module.

  • The I2C address is 7 bits, and it's made up of the combination of address bits (A0 to A5) and the RW bit.
  • The RW bit is the least significant bit (LSB) of the 7-bit address.
  • If the RW bit is 0, it indicates a write operation. If it's 1, it indicates a read operation.

The different combinations of address bits from A0 to A5 can set a different address for each module. Thus, the PCA9685 supports up to 62 unique addresses (including the default address), so in theory, you can connect up to 62 PCA9685 modules to the same I2C bus, each with a different address. The default I2C address for the PCA9685 is 0x40.

I2C Address Selection Jumpers: You can establish a unique address by configuring the address jumpers located on the upper right edge of the board. The binary address set through these jumpers is then combined with the base I2C address. To set the address offset, use a small amount of solder to connect the appropriate address jumper for each '1' in the binary address.

I2C Selection Jumper

Board 0: Address = 0x40 Offset = binary 00000 (no jumpers required)
Board 1: Address = 0x41 Offset = binary 00001 (bridge A0 as in the photo above)
Board 2: Address = 0x42 Offset = binary 00010 (bridge A1)
Board 3: Address = 0x43 Offset = binary 00011 (bridge A0 & A1)
Board 4: Address = 0x44 Offset = binary 00100 (bridge A2) 
etc.
Later you can declare each object for each board inside your code sketch. Initiate the "begin" function for each object, and manage each servo by interacting with the respective object it is connected to.

PCA9685 with Arduino Circuit Diagram

We are going to connect eight servo motors to the PCA9685 controller which is eventually more than the Arduino board PWM digital pins. Also, currently, we are using only a single controller board which is enough for eight servos. We use a 5v external adaptor to supply our servos using a V+ pin.

PCA9685 Arduino Circuit Diagram

  • Connect VCC to 5v and GND to the Ground pin of the Arduino.
  • Connect the external Source positive to the V+ terminal of PCA9685 which is directly connected to Servo VCC. And the ground is common to Arduino Ground.
  • Connect the SDA and SCL to A4 & A5 respectively for I2C communication with the module.
  • Each servo is connected to each channel on the module. Each channel module has also the same three pins: Signal/PWM, V+, and GND which are identical to the servo.

PCA9685 Connection

Programming the PCA9685 with Arduino

The code sets the I2C communication with the Controller module and initiates the module to control the servos channel described inside the code. Our code rotates the servos synchronously from 0 to 180 and 180 to 0.
Firstly, we are going to use a library Adafruit_PWMServoDriver.h which has most of the inbuilt features or functions to make controlling easy. The library can be easily installed using the simple process:

  • Open Arduino IDE > Tools > Manage Libraries > Search for “Adafruit PWM Servo Driver”, then install it.

Adafruit PWM Servo Driver

The single object created by the library is defined for accessing the single controller board. If you cascade the multiple boards, then you can simply define each object for each board. The I2C address should be passed as a parameter while creating the object and as we are well aware the different I2C addresses should be generated for multiple boards. 
The values of minimum and Maximum pulses are defined inside two Different variables which are used to set the rotation angle of the Servos. Minimum and Maximum value defines the 0 and 180 servo angle positions respectively.    

#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);       // called this way, it uses the default address 0x40   

#define SERVOMIN  125                                                 // this is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX  625                                                 // this is the 'maximum' pulse length count (out of 4096)

Inside setup, we began with the Serial monitor then we called the controller board to begin function. If you are using multiple boards then initialize each one with begin(). Additionally, by using the function setPWMFreq(); we can set the desired frequency to control the different output devices on the 16 channels. Since Servos are very comfortable at 60Hz.

void setup() {
  Serial.begin(9600);
  Serial.println("16 channel Servo test!");
  board1.begin();
  board1.setPWMFreq(60);                  // Analog servos run at ~60 Hz updates
}

Inside the loop, The function setPWM() is used which takes three parameters as input and also it is used to set the pulse width for a specific channel on the PCA9685 PWM driver. Here's the function signature:

 void Adafruit_PWMServoDriver::setPWM(uint8_t num, uint16_t on, uint16_t off);
  • num: The channel number (0 to 15) on the PCA9685 for which you want to set the PWM values.
  • On: The "on" time, which is the tick (counter value) when the signal should transition from low to high.
  • Off: The "off" time, which is the tick (counter value) when the signal should transition from high to low.

The two For loops are used in which the first one Sets the Zeroth position for all the servos. The secondary For loop synchronously sweeps all 8 channels from 0 to 180 angle. 

void loop() 
  { for(int i=0; i<8; i++)
      { board1.setPWM(i, 0, angleToPulse(0) );}
    delay(1000);
    
    for( int angle =0; angle<181; angle +=10)
      { for(int i=0; i<8; i++)
          { board1.setPWM(i, 0, angleToPulse(angle) );}
      }
    delay(100);
  }

Finally, a simple function is defined which takes the angle value and returns the pulse value by mapping both the Min and Max value.

int angleToPulse(int ang)                             //gets the angle in degree and returns the pulse width
  {  int pulse = map(ang,0, 180, SERVOMIN,SERVOMAX);  // map angle of 0 to 180 to Servo min and Servo max 
     Serial.print("Angle: ");Serial.print(ang);
     Serial.print(" pulse: ");Serial.println(pulse);
     return pulse;
  }

That’s how our code sweeps the servos back & and forth from 0 to 180 angle.

Note: If you are using more channels on the PCA controller, then update the “I” value limit in both for loops up to the updated channel number.

Demonstration of PCA9685 with Arduino

Check for all the connections once before you power it. Attach only eight servos from channels 0 to 7 as mentioned on the controller board. Use an external adapter since Arduino isn’t capable of delivering that much power. All data about angle and pulse are monitored on serial display.

Code

#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);       // called this way, it uses the default address 0x40   

#define SERVOMIN  125                                                 // this is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX  625                                                 // this is the 'maximum' pulse length count (out of 4096)

void setup() {
  Serial.begin(9600);
  Serial.println("16 channel Servo test!");
  board1.begin();
  board1.setPWMFreq(60);                  // Analog servos run at ~60 Hz updates
}

void loop() 
  { for(int i=0; i<8; i++)
      { board1.setPWM(i, 0, angleToPulse(0) );}
    delay(1000);
    
    for( int angle =0; angle<181; angle +=10)
      { for(int i=0; i<8; i++)
          { board1.setPWM(i, 0, angleToPulse(angle) );}
      }
    delay(100);
  }

int angleToPulse(int ang)                             //gets angle in degree and returns the pulse width
  {  int pulse = map(ang,0, 180, SERVOMIN,SERVOMAX);  // map angle of 0 to 180 to Servo min and Servo max 
     Serial.print("Angle: ");Serial.print(ang);
     Serial.print(" pulse: ");Serial.println(pulse);
     return pulse;
  }

Have any question realated to this Article?

Ask Our Community Members

Comments