Servo speed using adafruit pca9685 16-channel servo driver

I am trying to run a model railway. My computer control is JRMI/CMRI.
I have a Nano controlling 4 Adafruit pca9685 16-channel servo drivers controlling servos with no problems.
Everything works fine but I cannot find a way to control the speed of the servos.
I have looked at various solution but cant get my head around how to use it in my sketch.
Any help for a confused pensioner much appreciated please.

Eddy

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <CMRI.h>
#include <Auto485.h>
Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40); //setup the board address 3
Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41);
Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(0x42);
Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(0x43);
Auto485 bus(2); // Arduino pin 2 -> MAX485 DE and RE pins
CMRI cmri(5, 24, 48, bus); //CMRI node & Nano number

int Tbit[3];


void setup() {
  Serial.begin(9600);
  bus.begin(9600);
  pwm1.begin();
  pwm1.setPWMFreq(60); // This is the maximum PWM frequency
  pwm2.begin();
  pwm2.setPWMFreq(60);
  pwm3.begin();
  pwm3.setPWMFreq(60);
  pwm4.begin();
  pwm4.setPWMFreq(60);
}

void loop(){
   cmri.process();
   Tbit[0] = (cmri.get_bit(0)); 
   Tbit[1] = (cmri.get_bit(1));
   Tbit[2] = (cmri.get_bit(2));
   
   if (Tbit[2] == 1){
     pwm2.setPWM(2, 0, 135);
   }
   if (Tbit[2] == 0){
     pwm2.setPWM(2, 0, 580);
   }
   
   if (Tbit[0] == 1){
     pwm4.setPWM(0, 0, 135);
   }
   if (Tbit[0] == 0){
     pwm4.setPWM(0, 0, 580);
   }
   if (Tbit[1] == 1){
     pwm4.setPWM(1, 0, 135);
   }
   if (Tbit[1] == 0){
     pwm4.setPWM(1, 0, 580);
   }
}

I am not familiar with the library

    pwm2.setPWM(2, 0, 580);

What are the 3 parameters ?

There is no very simple way to control servo speed using PCA9685. The last time I discussed this here we came up with one solution (and the OP seemed to be using exactly the same basic code that you are).

Have a look at Decrease servo motor speed with PCA9586 - Motors, Mechanics, Power and CNC - Arduino Forum and see if that will work for you.

Steve

1 Like

Perfect, THANK YOU.
I have a Nano and a PCA9685 running great. I have tried adding another PCA9685 without much success.
Is it worth trying to add two PCA9685 to one Nano??
Or should I just use just one Nano one PCA9685.

Thanks Eddy

Provided you have connected them up as described in this Adafruit tutorial and set the I2C addresses (0x40, 0x41, 0x42 and 0x43) then 3 or 4 PCA9685 boards on one Nano should be no problem.

Steve

Thanks for the quick reply.The problem I have is trying to address the second PCA9685.
I know the PCA9685's are connected properly as they were addressable in my original sketch.
In the original sketch each servo was addressed by

if (Tbit[0] == 0){
** pwm4.setPWM(0, 0, 580);**

if (Tbit[0] == 0){
** pwm1.setPWM(0, 0, 200);**

In this modified sketch each servo is addressed by

if (Tbit[0] == 1){ **
** slowMove(0, 1, 90);

In the sketch below when bit[0] is received both servos(0) are activated on boards pwn4 and pwm1.

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <CMRI.h>
#include <Auto485.h>
Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40); //setup the board address 
Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41);
Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(0x42);
Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(0x44);
Auto485 bus(2); // Arduino pin 2 -> MAX485 DE and RE pins
CMRI cmri(5, 24, 48, bus); //CMRI node & Nano number

int Tbit[20];
int currentPos[20];   // current positions for 5 servos numbered 0 - 4

void setup() {
  Serial.begin(9600);
  bus.begin(9600);
  pwm1.begin();
  pwm1.setPWMFreq(60); // This is the maximum PWM frequency
  pwm2.begin();
  pwm2.setPWMFreq(60);
  pwm3.begin();
  pwm3.setPWMFreq(60);
  pwm4.begin();
  pwm4.setPWMFreq(60);
  
  for (int i = 0; i < 5; i++) // set and remember initial positions using array
  {
    pwm4.setPWM(i, 0, 380);  // set initial position
    pwm1.setPWM(i, 0, 380);  // set initial position
    currentPos[i] = 380;    // and remember it as current
  }
}

void loop(){
   cmri.process();
   Tbit[0] = (cmri.get_bit(0)); 
   Tbit[1] = (cmri.get_bit(1));
   Tbit[2] = (cmri.get_bit(2)); 
   Tbit[3] = (cmri.get_bit(3)); 
   Tbit[4] = (cmri.get_bit(4)); 
   Tbit[5] = (cmri.get_bit(5));
   Tbit[6] = (cmri.get_bit(6));
   


  int angle;
  int i;
   
   if (Tbit[0] == 1){     
     slowMove(0, 1, 90);
   }  
   if (Tbit[0] == 0){
    slowMove(0, 1, 610);
   }
   if (Tbit[1] == 1){
    slowMove(1, 15, 152);
   }
   if (Tbit[1] == 0){
    slowMove(1, 15, 250);
   }
   if (Tbit[2] == 1){
     slowMove(2, 15, 152);
   }
   if (Tbit[2] == 0){
     slowMove(2, 15, 250);
   } 
   if (Tbit[3] == 1){
     slowMove(3, 1, 90);
   }
   if (Tbit[3] == 0){
     slowMove(3, 1, 610);
   }
   if (Tbit[4] == 1){
     slowMove(4, 15, 152);
   }
   if (Tbit[4] == 0){
     slowMove(4, 15, 250);
   } 
   if (Tbit[5] == 1){
     slowMove(17, 1, 90);
   }
   if (Tbit[5] == 0){
     slowMove(17, 1, 610);
   }
   }

// Checks to see if a move is needed using currentPos[]
// if so it uses for loop to move to position given in 'to' argument
// speed of the move is controlled by delayTime smaller delay = faster move

void slowMove(int servoNum, int delayTime, int to)
{
  if (currentPos[servoNum] == to) return;  // Nothing to do if it's already there

  else if (to > currentPos[servoNum])
  {
    for (int i = currentPos[servoNum]; i < to; i++)
    {
      pwm4.setPWM(servoNum, 0, i);
        pwm1.setPWM(servoNum, 0, i);
      delay(delayTime);
    }
  }
  else if (currentPos[servoNum] > to)
  {
    for (int i = currentPos[servoNum]; i > to; i--)
    {
      pwm4.setPWM(servoNum, 0, i);
       pwm1.setPWM(servoNum, 0, i);
      delay(delayTime);
    }
  }
  currentPos[servoNum] = to;    // save the current servo position
}

I guess you didn't get as far as page 2 of that thread I linked for you. As we worked out you need some way to tell the function doing the slow moving which board your servo is on.

There are several ways to do that but probably the easiest to understand is the way used in post #19 which is to have separate functions for each board, so slowmove0(), slowmove1() etc.

Give it a try - Steve

Thanks very much for your help Steve so much appreciated.
Yes i have now found page 2 :-[
The sketch works fine on my demo layout will test later on main layout.
I put Tbit in an array at the top but not quiet sure if i should put one here and how.

void loop(){
cmri.process();
Tbit[0] = cmri.get_bit(0); // Get the bit from CMRI 0 - 47 = 48
Tbit[1] = cmri.get_bit(1);
Tbit[2] = cmri.get_bit(2);
Tbit[3] = cmri.get_bit(3);

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <CMRI.h>
#include <Auto485.h>
Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40); //setup the board address 
Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41);
Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(0x42);
Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(0x44);
Auto485 bus(2); // Arduino pin 2 -> MAX485 DE and RE pins
CMRI cmri(5, 24, 48, bus); //CMRI node & Nano number

int Tbit[47];          //Max bits allowed from CMRI 0-47 = 48 bits
int currentPos1[16];   // current positions for 16 servos per board
int currentPos2[16]; 
int currentPos3[16]; 
int currentPos4[16]; 

void setup() {
  Serial.begin(9600);
  bus.begin(9600);     //Starts the RS485 bus
  pwm1.begin();
  pwm1.setPWMFreq(60); // This is the maximum PWM frequency
  pwm2.begin();
  pwm2.setPWMFreq(60);
  pwm3.begin();
  pwm3.setPWMFreq(60);
  pwm4.begin();
  pwm4.setPWMFreq(60);
  
  for (int i = 0; i < 16; i++) // set and remember initial positions using array
  {
    pwm1.setPWM(i, 0, 380);  // set initial position
    currentPos1[i] = 380;    // and remember it as current
  }
  for (int i = 0; i < 16; i++)
  {
    pwm2.setPWM(i, 0, 221);  // set initial position
    currentPos2[i] = 221;    // and remember it as current
  }

  for (int i = 0; i < 16; i++)
  {
    pwm3.setPWM(i, 0, 221);  // set initial position
    currentPos3[i] = 221;    // and remember it as current
  }

  for (int i = 0; i < 16; i++)
  {
    pwm4.setPWM(i, 0, 221);  // set initial position
    currentPos4[i] = 221;    // and remember it as current
  }
}
void loop(){
   cmri.process();
  Tbit[0] = cmri.get_bit(0);  // Get the bit from CMRI 0 - 47 = 48
  Tbit[1] = cmri.get_bit(1);
  Tbit[2] = cmri.get_bit(2);
  Tbit[3] = cmri.get_bit(3);
//  Tbit[5] = cmri.get_bit(4);
//  Tbit[6] = cmri.get_bit(5);
//  Tbit[7] = cmri.get_bit(6);
//  Tbit[8] = cmri.get_bit(7);
//  Tbit[9] = cmri.get_bit(8);
//  Tbit[10] = cmri.get_bit(9);

  int angle;
  int i;
  
  if (Tbit[0] == 1){
    slowMove1(0, 1, 145);//CT5001 offset by 1 from Tbit[0]
  }
  if (Tbit[0] == 0) {
    slowMove1(0, 1, 400);//CT5001
  }
  if (Tbit[1] == 1){
    slowMove4(0, 1, 145);//CT5002
  }
  if (Tbit[1] == 0) {
    slowMove4(0, 1, 400);//CT5002
  }
  if (Tbit[2] == 1){
    slowMove4(1, 1, 145);//CT5003
  }
  if (Tbit[2] == 0) {
    slowMove4(1, 1, 400);//CT5003
  }
   if (Tbit[3] == 1){
    slowMove4(2, 1, 145);//CT5004
  }
  if (Tbit[3] == 0) {
    slowMove4(2, 1, 400);//CT5004
  }


}
// Checks to see if a move is needed using currentPos[]
// if so it uses for loop to move to position given in 'to' argument
// speed of the move is controlled by delayTime smaller delay = faster move

void slowMove1(int servoNum, int delayTime, int to)
{
  if (currentPos1[servoNum] == to) return;  // Nothing to do if it's already there

  else if (to > currentPos1[servoNum])
  {
    for (int i = currentPos1[servoNum]; i < to; i++)
    {
      pwm1.setPWM(servoNum, 0, i);
      delay(delayTime);
    }
  }
  else if (currentPos1[servoNum] > to)
  {
    for (int i = currentPos1[servoNum]; i > to; i--)
    {
      pwm1.setPWM(servoNum, 0, i);
      delay(delayTime);
    }
  }
  currentPos1[servoNum] = to;    // save the current servo position
}
void slowMove2(int servoNum, int delayTime, int to)
{
  if (currentPos2[servoNum] == to) return;  // Nothing to do if it's already there

  else if (to > currentPos2[servoNum])
  {
    for (int i = currentPos2[servoNum]; i < to; i++)
    {
      pwm2.setPWM(servoNum, 0, i);
      delay(delayTime);
    }
  }
  else if (currentPos2[servoNum] > to)
  {
    for (int i = currentPos2[servoNum]; i > to; i--)
    {
      pwm2.setPWM(servoNum, 0, i);
      delay(delayTime);
    }
  }
  currentPos2[servoNum] = to;    // save the current servo position
}
void slowMove3(int servoNum, int delayTime, int to)
{
  if (currentPos3[servoNum] == to) return;  // Nothing to do if it's already there

  else if (to > currentPos3[servoNum])
  {
    for (int i = currentPos3[servoNum]; i < to; i++)
    {
      pwm3.setPWM(servoNum, 0, i);
      delay(delayTime);
    }
  }
  else if (currentPos3[servoNum] > to)
  {
    for (int i = currentPos3[servoNum]; i > to; i--)
    {
      pwm3.setPWM(servoNum, 0, i);
      delay(delayTime);
    }
  }
  currentPos3[servoNum] = to;    // save the current servo position
}
void slowMove4(int servoNum, int delayTime, int to)
{
  if (currentPos4[servoNum] == to) return;  // Nothing to do if it's already there

  else if (to > currentPos4[servoNum])
  {
    for (int i = currentPos4[servoNum]; i < to; i++)
    {
      pwm4.setPWM(servoNum, 0, i);
      delay(delayTime);
    }
  }
  else if (currentPos4[servoNum] > to)
  {
    for (int i = currentPos4[servoNum]; i > to; i--)
    {
      pwm4.setPWM(servoNum, 0, i);
      delay(delayTime);
    }
  }
  currentPos4[servoNum] = to;    // save the current servo position
}

Your declared array size needs to be the number of entries not the last index. So if you want 48 Tbits then it's Tbit[48].

And yes you should be able to do the reading in a simple for loop something like:

for (i=0; i<48; i++){
  Tbit[i] = cmri.get_bit(i);
}

Good luck - Steve