Controlling servos via rotary encoders

Good afternoon, I am building a 6 axis robotic arm, using 6 DM996 servos, 6 push buttons rotary encoders, PCA9685 PWM servo controller and Arduino Mega 256.
I am facing a very strange problem:
I can perfectly read the different values from the rotary encoders, but when i pass the requested value to the selected servo the system freezes. no way to go further.
Any clue?
Thank you in advance.
Here is wiring and code for 2 encoders and servos only.

/******************************************
 *Claudio Ungaro 23/09/2023
 * 
 *rotary and servo motor 
 *using microseconds
 *using AdaFruit PWM servo driver
 ******************************************/

//#include <Servo.h>
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

// for sensitivity reason all angles are multiplied by 2
// Min angle 45 deg, Max angle 135 deg
#define MAXANG 270
#define MINANG 90
#define HOME 180
#define MAXMICRO 2400
#define MINMICRO 600
#define HOMEMICRO 1500

#define SERVOMIN 150   // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX 600   // This is the 'maximum' pulse length count (out of 4096)
#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


//Define the pin connection
int CLK_0 = 2;  //CLK->D2
int DT_0 = 4;   //DT->D3
int SW_0 = 5;   //SW->D4
int CLK_1 = 3;  //CLK->D2
int DT_1 = 6;   //DT->D3
int SW_1 = 7;   //SW->D4
int dummy = 1500;
// Interrupt 0 on pin 2
const int interrupt0 = 0;
const int interrupt1 = 1;
//Define the count
int count_0 = HOME;
int count_1 = HOME;
//CLK initial value
int lastCLK_0 = HOME;
int lastCLK_1 = HOME;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  // setup the encoders
  Serial.println("Setting the encoders");
  pinMode(SW_0, INPUT);
  digitalWrite(SW_0, HIGH);
  pinMode(CLK_0, INPUT);
  pinMode(DT_0, INPUT);
  //Set the interrupt 0 handler, trigger level change
  attachInterrupt(interrupt0, ClockChanged_0, CHANGE);
  //
  pinMode(SW_1, INPUT);
  digitalWrite(SW_1, HIGH);
  pinMode(CLK_1, INPUT);
  pinMode(DT_1, INPUT);
  //Set the interrupt 0 handler, trigger level change
  attachInterrupt(interrupt1, ClockChanged_1, CHANGE);
  // setting the PWN module
  pwm.begin();
  delay(10);
  pwm.setOscillatorFrequency(27000000);
  pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates
  // setting servos to HOME position
  Serial.println("Setting HOME position of each Servo");
  pwm.writeMicroseconds(0, MINMICRO);
  delay(1500);
  pwm.writeMicroseconds(0, MAXMICRO);
  delay(1500);
  pwm.writeMicroseconds(0, HOMEMICRO);
  delay(1500);
  pwm.writeMicroseconds(1, MINMICRO);
  delay(1500);
  pwm.writeMicroseconds(1, MAXMICRO);
  delay(1500);
  pwm.writeMicroseconds(1, HOMEMICRO);
  delay(1500);

  Serial.println("End SETUP");
}

void loop() {
  // put your main code here, to run repeatedly:
  // if button is pushed set to zero encoder and Servo
  if (!digitalRead(SW_0) && count_0 != HOME)  //Read the button press and the count value to 0 when the counter reset
  {
    count_0 = HOME;
    Serial.print("Encoder 0 Real Angle :");
    int angleVal = count_0 / 2;
    Serial.print(angleVal);
    Serial.println("  Micro ");
    Serial.println(HOMEMICRO);
    pwm.writeMicroseconds(0, HOMEMICRO);
    lastCLK_0 = HOME;
  }

  if (!digitalRead(SW_1) && count_1 != HOME)  //Read the button press and the count value to 0 when the counter reset
  {
    count_1 = HOME;
    Serial.print("Encoder 1 Real Angle:");
    int angleVal = count_1 / 2;
    Serial.println(angleVal);
    Serial.println("  Micro ");
    Serial.println(HOMEMICRO);
    pwm.writeMicroseconds(1, HOMEMICRO);
    lastCLK_1 = HOME;
  }
  // Put in a slight delay to help debounce the reading
  delay(1);
}

void ClockChanged_0() {
  int clkValue = digitalRead(CLK_0);  //Read the CLK pin level
  int dtValue = digitalRead(DT_0);    //Read the DT pin level
  if (lastCLK_0 != clkValue) {
    lastCLK_0 = clkValue;
    count_0 += (clkValue != dtValue ? 1 : -1);  //CLK and inconsistent DT + 1, otherwise - 1
    if (count_0 < MINANG) count_0 = MINANG;
    if (count_0 >= MAXANG) count_0 = MAXANG;
    Serial.print("Encoder 0 Real Angle :");
    int angleVal = count_0 / 2;
    Serial.print(angleVal);
    Serial.print("  Microseconds ");
    int microAngle = count_0 * 5 + 600;
    Serial.println(microAngle);
    pwm.writeMicroseconds(0, microAngle);
  }
}

//The interrupt handlers
void ClockChanged_1() {
  int clkValue = digitalRead(CLK_1);  //Read the CLK pin level
  int dtValue = digitalRead(DT_1);    //Read the DT pin level
  if (lastCLK_1 != clkValue) {
    lastCLK_1 = clkValue;
    count_1 += (clkValue != dtValue ? 1 : -1);  //CLK and inconsistent DT + 1, otherwise - 1
    if (count_1 < MINANG) count_1 = MINANG;
    if (count_1 >= MAXANG) count_1 = MAXANG;
    Serial.print("Encoder 1 Real Angle :");
    int angleVal = count_1 / 2;
    Serial.print(angleVal);
    Serial.print("  Microseconds ");
    int microAngle = count_1 * 5 + 600;
    Serial.println(microAngle);
    pwm.writeMicroseconds(1, microAngle);
  }
}

In order to get some perspective on your problem, during the program development, when did the problem begin to occur?

As long as i controlled the servos with the Arduino using PWM pins all works fine.
Using the controller PCM9685 i can do the rotations in the setup, but as soon i move the rotary encoder (any of them) using writeMicroSeconds values from the encoders, the system freezes.
i tried to click only one step to see if it was a problem of interrupts overlapping, no avail.
I realizes that using servos to control a robotic arm is not the best way to do it, nevertheless i cant stand to not understand and solve this problem, because it could pop up in the future in other projects.
Thank you for you attention

Here is the printout :slightly_smiling_face:

15:17:50.106 -> Setting the encoders
15:18:05.784 -> Setting HOME position of each Servo
15:18:14.745 -> End SETUP
15:18:15.727 -> En

as soon the encoder moves......

Remove the Serial printing from the encoder isr's.

1 Like

PWM pins are not required for servos.

I did try it, imagining that the printing time would be the problem, but it doesn't sort the matter.
Thank you anyway

With PWM i can control the servo with Aurduino "loop". but i need to control them via I2C

Are you using pullup resistors on the SDA and SCL lines?

https://learn.adafruit.com/working-with-i2c-devices/pull-up-resistors

No, I don't.
I have read the information on your link, and I have to admit i am quite "light" on that.
Do i have to put them between the SDA, SCL lines and the 3.3 V or the 5v ?
Thank you for your suggestion.

Yes, to pull "up" both lines. This becomes more necessary when the lines are long

The Adafruit PCA9685 PWM servo controller has the pullups installed on the board. See the board schematic.

image

Though, as mentioned, lower value resistors (stronger pullups) may be required for longer wires. The I2C is not made for longer wires. It is made for interconnect of ICs on the same PC board. So running wires of longer distances will take special care (stronger pullup, slower communication rates, etc.).

Arduino MEGA Board has built-in pull-up resistors on the I2C Bus (Fig-1):
megaPullUpI2C
Figure-1:

Last night was very late when i read your comment, and promise myself to look at it this morning. Then just getting up i remembered that the pull ups resistors are already set on the Adafruit board as said by gF.
In any case the cable are short (avg 30 cm, 12 in), so the mystery remains intact. :rofl: :rofl: :rofl:

Finally I found the answer to my question.
see https://forums.adafruit.com/viewtopic.php?f=19&t=134673

Blockquote
The Mega has only one level of interrupt and interrupts are disabled on entry to an interrupt service routine. Since i2c communication requires interrupts, it is not possible to communicate with the PWM Servo driver when interrupts are disabled.

Blockquote
I have to use multiple PWM pins on the Mega.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.