Last Updated: 26/12/23

PCA685 and Servo Control

Components >> PCA685 and Servo Control

PCA9685 and Servo Control

PCA9685 Board

I have done a couple of tutorials using the PCA9685 board for LED control.
This time we will be looking at servo control

Pinout and wiring diagrams

 

Pin Out

The pin out of these boards is quite simple.

Pins at either end of the board (enables boards to be daisy chained).

GND: Ground

Output Enable: making this pin high disables all outputs. I do not use this pin in my examples.

SCL: Connects to the SCL pin on your Arduino/ESP32

SDA: Connects to your SDA pin on your Arduino/ESP32

VCC: 3-5V maximum. This powers the various components on the board.

V+: This is the power input for the servos/leds connected to the outputs. This is needed because if you connected 16 servos to your Arduino it will not be able to supply enough power, especially if running of a USB connection.

NOTE: There is a V+ and GND with screw connectors in the centre of the board. This is usually the best way to feed external power into the board as it as polarity protection. The V+ on the end of the board does not. YOU HAVE BEEN WARNED.

Top of Board

I2C solder Pads Soldering across these pads enables you to change the I2C address of the board that by default is 0x40.

Yellows/Red/Black pins along the bottom.

Yellow LED Positive: Servo Signal pin (Orange on my current MG90 servos).

Black (GND): Servo ground pin (brown wire on my current batch of MG90 servos)

Red Power supply for Servo

Wiring:for a few common boards

SDA/SCL connections

Arduino UNO: A4 (SDA), A5 (SCL) :

Arduino Mega 2560: 20 (SDA), 21 (SCL) :

ESP32: 21(SDA), 22 (SCL)

For other Arduino boards see:https://www.arduino.cc/en/reference/wire

PCA9685 Connections

Connection Test 1 ...IMPORTANT

Bad wiring is one of the biggest problems with Arduino projects so run this test to make sure your board is being found before doing anythine else.

Once you have connected your board up as shown above check that it can be found.

If you are using an Arduino board go to File>Examples> Wire>I2c_scanner and upload the skecth to your board.

if all goes well your serial Monitor will display something like

Scanning...
I2C device found at address 0x40 !
done

Once your device has been found it's time to add the LED's

 

Connection Test 2 ...Board and Servo Test

First make sure you have the Adafruit PWM Servo Library installed.

Tools>Manage Libraries> Type Adafruit PWM Servo Library in the serach bar.

If it is not installed install it.

Adafruit PM Servo Library

Connect the board according to the circuit diagram.

Now download a test file from the Adafruit PWM servo Library examples.

File>Examples>Adafruit PWM servo Driver Library>servo.

Load the sketch and in the Serial Monitor you should see something like:

8 channel Servo test!
0
1
2
3
4
5
6
7

If you have a servo in any of the first 8 connections (nearest board connection end) you servo should move back and forth a couple of times.

At this point although we may not understand the sketch we know the board and servos work.

 

Example 1: PCA9685ServoV1.ino


Click to Download code: PCA9685ServoV1.ino

This is a stripped down version of the Adafruit example. Although it's been simplified it is not really working in a way that we would set servos.

 

 
/* PCA9685ServoV1
 *  26/12/23
 *  
 * PCA9685 Board with MG90 servo
 * 
 * This sketch only controls a single servo on pca9685 pin 0
 * Moving it from 0 - 180 degrees
 * 
 * 
SDA/SCL connections 

Arduino UNO: A4 (SDA), A5 (SCL) 
Arduino Mega 2560: 20 (SDA), 21 (SCL)  
ESP32: 21(SDA), 22 (SCL)

 */


#include "Wire.h"
#include "Adafruit_PWMServoDriver.h"

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);

#define USMIN  600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX  2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates

// our servo
uint8_t servonum = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("PCA9685ServoV1");

  pwm.begin();
  pwm.setOscillatorFrequency(27000000);
  pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates

  delay(10);
}



void loop() {
    
  for (uint16_t microsec = USMIN; microsec < USMAX; microsec++) {
    pwm.writeMicroseconds(servonum, microsec);
  }

  delay(500);
  for (uint16_t microsec = USMAX; microsec > USMIN; microsec--) {
    pwm.writeMicroseconds(servonum, microsec);
  }

  delay(500);

}

 

Example 2: PCA9685ServoV2.ino


Click to Download code: PCA9685ServoV2.ino

In this version the servo still continues to sweep back from 0-179 (180 degrees) but this time a value "q" is passed to a new funtion where "q" is the angle we want the servo to move to in degrees.
All the calculations take place in the function

void setServoPos(int servo, int pos)

All we now need to do is pass it the servo number and the position we want it to move to.
You will notice the servo moves faster as it's moving in degrees as opposed to the original version that moved it 0.1 degrees per step.
as such a small movement is unlikely to be of any use in a model railway scenario I have stuck to whole numbers.

 

 
/* PCA9685ServoV2
 *  26/12/23
 *  
 * This version of the sketch allows you to set the position in degrees from 0-179
 *  
 * PCA9685 Board with MG90 servo
 * 
 * This sketch only controls a single servo on pca9685 pin 0
 * Moving it from 0 - 180 degrees
 * 
 * 
SDA/SCL connections 

Arduino UNO: A4 (SDA), A5 (SCL) 
Arduino Mega 2560: 20 (SDA), 21 (SCL)  
ESP32: 21(SDA), 22 (SCL)

 */


#include "Wire.h"
#include "Adafruit_PWMServoDriver.h"

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);

#define USMIN  600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX  2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates

// our servo
uint8_t servonum = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("PCA9685ServoV1");

  pwm.begin();
  pwm.setOscillatorFrequency(27000000);
  pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates

  delay(10);
}

void setServoPos(int servo, int pos){
  //This first bit of code makes sure we are not trying to set the servo outside of limits
  int sendPos;
  if(pos > 179){
    pos = 179;
  }
  if(pos < 0){
    pos = 0;
  }
  sendPos = USMIN + ((USMAX - USMIN)/180 * pos);
  if(servo > -1 && servo < 16){//only try to move valid servo addresses
    pwm.writeMicroseconds(servo, sendPos);
  }
}

void loop() {
  int q;

  for(q=0;q<180;q++){
    setServoPos(servonum,q);
  }

  delay(500);

  for(q=179;q>-1;q--){
    setServoPos(servonum,q);
  }

  delay(500);
  
}

 

Example 3 PCA9685ServoV3.ino


Click to Download code: PCA9685ServoV3.ino

This final version moves in the same set of steps as the last version but is controlling 2 different servos..

 

 
/* PCA9685ServoV3
 *  26/12/23
 *  
 * This version of the sketch allows you to set the position in degrees from 0-179
 * This time the servo will move  from Zero to 90 to 179 and back again
 *  
 * PCA9685 Board with MG90 servo
 * 
 * 
 * 
SDA/SCL connections 

Arduino UNO: A4 (SDA), A5 (SCL) 
Arduino Mega 2560: 20 (SDA), 21 (SCL)  
ESP32: 21(SDA), 22 (SCL)

 */


#include "Wire.h"
#include "Adafruit_PWMServoDriver.h"

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);

#define USMIN  600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX  2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates

// our servo
uint8_t servonum = 0;

void setServoPos(int servo, int pos){
  //This first bit of code makes sure we are not trying to set the servo outside of limits
  int sendPos;
  if(pos > 179){
    pos = 179;
  }
  if(pos < 0){
    pos = 0;
  }
  sendPos = USMIN + ((USMAX - USMIN)/180 * pos);
  if(servo > -1 && servo < 16){//only try to move valid servo addresses
    pwm.writeMicroseconds(servo, sendPos);
  }
}

void setup() {
  Serial.begin(9600);
  Serial.println("PCA9685ServoV3");

  pwm.begin();
  pwm.setOscillatorFrequency(27000000);
  pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates
  delay(10);
}

void loop() {
    setServoPos(servonum,0);//0 Degrees
  delay(500);
  setServoPos(servonum,90);//90 degrees
  delay(500);
  setServoPos(servonum,179);//179 degrees
  delay(500);
  setServoPos(servonum,90);//90 degrees
  delay(500);
}

 

Example 4: PCA9685ServoV4.ino


Click to Download code: PCA9685ServoV4.ino

This is a stripped down version of the Adafruit example. Although it's been simplified it is not really working in a way that we would set servos.

 

 
/* PCA9685ServoV4
 *  26/12/23
 *  
 This sketch is controlling 2 servos, one on pin 0 the 2nd on pin 3
 *  
 * PCA9685 Board with MG90 servo
 * 
 * 
 * 
SDA/SCL connections 

Arduino UNO: A4 (SDA), A5 (SCL) 
Arduino Mega 2560: 20 (SDA), 21 (SCL)  
ESP32: 21(SDA), 22 (SCL)

 */


#include "Wire.h"
#include "Adafruit_PWMServoDriver.h"

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);

#define USMIN  600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX  2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates

// our servos
const int servo0 = 0;
const int servo3 = 3;

void setServoPos(int servo, int pos){
  //This first bit of code makes sure we are not trying to set the servo outside of limits
  int sendPos;
  if(pos > 179){
    pos = 179;
  }
  if(pos < 0){
    pos = 0;
  }
  sendPos = USMIN + ((USMAX - USMIN)/180 * pos);
  if(servo > -1 && servo < 16){//only try to move valid servo addresses
    pwm.writeMicroseconds(servo, sendPos);
  }
}

void setup() {
  Serial.begin(9600);
  Serial.println("PCA9685ServoV4");

  pwm.begin();
  pwm.setOscillatorFrequency(27000000);
  pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates
  delay(10);
}

void loop() {
    setServoPos(servo0,0);//0 Degrees
  delay(500);
  setServoPos(servo0,90);//90 degrees
  delay(500);
  setServoPos(servo0,179);//179 degrees
  delay(500);
  setServoPos(servo0,90);//90 degrees
  delay(500);

  setServoPos(servo3,0);//0 Degrees
  delay(500);
  setServoPos(servo3,90);//90 degrees
  delay(500);
  setServoPos(servo3,179);//179 degrees
  delay(500);
  setServoPos(servo3,90);//90 degrees
  delay(500);
}

Additional Resource Links

Multiple PCA9685 PWM Servo Boards with servos and LED's 05/01/2024

Servos 13/08/2021

Adafruit PWM servo library

Comments


This site has been designed to be child friendly, this means that comments cannot be added to videos or directly to the site.
To add a comment or ask a question please email the address in this image: and use PCA685 and Servo Control as a reference.