Last Updated: 26/12/23
PCA685 and Servo Control
Components >> PCA685 and Servo Control
PCA9685 and Servo Control
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
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.
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
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.