Last Updated: 28/12/2023
PCA685 Servo Setter
Projects >> PCA685 Servo Setter
PCA9685 Servo setter. Servos controlled by buttons
In this project we will build a small control system to allow the setting of servos.
I used MG90 servos, SG90's are the same but with plastic gears and the system should work with other servos as well.
The goal is to be able to adjust the servo swing using adjuster buttons as well as being able to set the servo to it's centre position (90 degrees).
I built this to set servos for a model railway and to allow me to adjust the throw of turnouts/points in real time for quick setting.
Pinout and wiring diagrams
Pins
8 - 11 are connected to push button (momentary switches) with a 4.7k OHM pull down resistor
A5 to PCA9685 SCL
A4 to PCA9685 SDA
Make sure you have 5v going to the PCA9685 board as well as the VCC and GND. One powers the circuitry, the other supply powers the 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
Pinout and wiring diagrams
The breadboard version looks like this.
The breadboard version worked very well.
I was using the servo setter to test a new servo mount for my layout.
The system is quite simple, I drill a 8mm hole directly under the centre of the point switch blade hole. The piece of aluminium strip has a 1mm hole in it. The fixing screws are far enough apart so that the screws don't come up under the track.
It's hard to see in the photo but the servo is push fitted into a 3D printed bracket. The fit was really good but it needs work as I forgot the servo is not symmetrical so the one screw hole in the bracket would be under the track. I just need to widen the bracket to clear the tracks.
As you will see in the video the movement is really nice.
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: PCA9685ServoSetterV1.ino
Click to Download code: PCA9685ServoSetterV1.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.
/* PCA9685ServoSetterV1
Goals
To be able to set a servo to 90 dgrees ready for baseboard install.
Be able to move the servo side to side adjusting the distance to get the correct figures for the points.
Code to be written as a state machine without delay()
PCA9685 board
button to set to 90 degrees....or into side to side mode
button to select left/right direction adjustment
2 buttons for adjusters (Lower and Higher values)
4 x 4.5k OHM resistors for Pull Down on buttons
Pin Connections
SDA/SCL connections for PCA9685
Arduino UNO: A4 (SDA), A5 (SCL)
Arduino Mega 2560: 20 (SDA), 21 (SCL)
ESP32: 21(SDA), 22 (SCL)
pin 8 mode swap ... button with 4.7K pull down resistor
pin 9 left/right adjustment swap... button with 4.7K pull down resistor
pin 10 low adjust... button with 4.7K pull down resistor
pin 11 high adjustment... button with 4.7K pull down resistor
*/
#include "Wire.h"
#include "Adafruit_PWMServoDriver.h"
//PCA 9685 settings
// 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
//end pca9685 settings
//buttons...will all need pull down resistors
const int modePin = 8; // sets if servo is moved to 90 degrees or sweeping left to right.
const int adjustPin = 9;//sets if we are adjusting the anticlockwise (nearer to zero) or clockwise (nearer to 180)
const int lowAdjPin = 10;
const int highAdjPin = 11;
// our servo
int myServo = 0;//we will always be testing on pins 0 on PCA9685
//System variables
byte currentMode = 0; //0 sets to 90 degrees 1: sweep mode
byte adjustSide = 0; //0 = Low...nearest to Zero 1 = High side nearest to 180
unsigned long currentMillis; //will stire the current time in from millis()
unsigned long buttonTimer;//stores the next value in millis for debounce
int buttonDebounce = 200;//smaller vale makes buttons more sensitive, easier to get double press
unsigned long servoSweepTimer; //stores when next sweep can start
int sweepTimer = 2500;//set to 2.5 secs between sweeps
byte sweepDirection;//0 == anticlockwise 1 clockwise
int sweepLowLimit = 90;//starts in central position
int sweepHighLimit = 90;//starts in central position
//Servo movement function
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);
}
}
//deals with button presses
void buttonState() {
if (currentMillis - buttonTimer >= buttonDebounce) { //are buttons debounced
buttonTimer = currentMillis;//reset debounce timer
//the system will only read one button at a time so checks through the buttons in order and if any are pressed
//that button will be dealt with and any other presses at the same time ignored
//This means only 1 debounce timer is needed.
if (digitalRead(modePin) > 0) {
//Serial.println("modePin");
if (currentMode > 0) {
currentMode = 0;//move to 90 degrees
Serial.println("Move to 90");
} else {
currentMode = 1;//sweep mode
Serial.println("Sweep Mode");
}
} else {
if (digitalRead(adjustPin) > 0) {
//Serial.println("adjustPin");
if (adjustSide > 0) {
adjustSide = 0;
Serial.println("adjust Low");
} else {
adjustSide = 1;
Serial.println("adjust High");
}
} else {
if (digitalRead(lowAdjPin) > 0) {
//Serial.println("lowAdjPin");
if (adjustSide > 0) {
sweepHighLimit--;
Serial.print("High Set: ");
Serial.println(sweepHighLimit);
} else {
sweepLowLimit--;
Serial.print("Low set: ");
Serial.println(sweepLowLimit);
}
} else {
if (digitalRead(highAdjPin) > 0) {
//Serial.println("highAdjPin");
if (adjustSide > 0) {
sweepHighLimit++;
Serial.print("High Set: ");
Serial.println(sweepHighLimit);
} else {
sweepLowLimit++;
Serial.print("Low set: ");
Serial.println(sweepLowLimit);
}
}
}
}
}
}
}
//Checks if the servo needs to move
void servoMode() {
if (currentMode < 1) {
setServoPos(myServo, 90);//set servo to centre postion
} else { //normal sweep mode
if (currentMillis - servoSweepTimer >= sweepTimer) {
servoSweepTimer = currentMillis;//reset for next sweep
if (sweepDirection < 1) { //move to anticlockwise limit
setServoPos(myServo, sweepLowLimit);
Serial.print("M Low: ");
Serial.println(sweepLowLimit);
sweepDirection = 1;
} else { //move to clockwise limit
setServoPos(myServo, sweepHighLimit);
Serial.print("M High: ");
Serial.println(sweepHighLimit);
sweepDirection = 0;
}
}
}
}
void setup() {
Serial.begin(9600);
Serial.println("PCA9685ServoSetterV1");
pwm.begin();
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(SERVO_FREQ); // Analog servos run at ~50 Hz updates
delay(10);//let the above values take effect
pinMode(modePin, INPUT);
pinMode(adjustPin, INPUT);
pinMode(lowAdjPin, INPUT);
pinMode(highAdjPin, INPUT);
}
void loop() {
currentMillis = millis();//get the current millis since board started for timing
buttonState();//deals with button presses
servoMode();
}
Additional Resource Links
PCA9685 PWM Servo Board 27/12/2023
How To 1 : Debouncing buttons 15/09/2021Servos 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 Servo Setter as a reference.