PCA9685 driver issue

Hello, I have a kind of complicated issue with the PCA9685 driver but I will try my best to simplify it for you
I have a communication project between Arduino and Processing, the communication between them is as follows:

Sending data from Processing to Arduino to operate the servo motors in a certain way, but the strange thing that happens to me is that the motor vane angle does not exceed 10 to 20 angles at the time I send it, for example, an order to move by 150, of course, this problem only occurs when the motors are connected to the PCA9685 and when the motors are connected directly to the Arduino chip, the commands are very smooth and works with zero issues.

Any help would be greatly appreciated

Please show your code and connection diagram.
Without them I don't see a way to help you.

Sure

Arduino code:
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver board2 = Adafruit_PWMServoDriver(0x41);
Adafruit_PWMServoDriver board3 = Adafruit_PWMServoDriver(0x42);
Adafruit_PWMServoDriver board4 = Adafruit_PWMServoDriver(0x43);

const int num_servos = 9;
int pos [num_servos];
int rec = 0;
int x;

void setup() {
Serial.begin(9600);

board1.begin();
board2.begin();
board3.begin();
board4.begin();
board1.setPWMFreq(50); // Analog servos run at ~60 Hz updates
board2.setPWMFreq(50);
board3.setPWMFreq(50);
board4.setPWMFreq(50);

}

void loop() {

if (Serial.available()>0){
int message = Serial.read();
int x = (message);
pos[rec] = x;
rec++;
}
if(rec == num_servos){
        board1.setPWM(7, 0, pos[0]);
        board1.setPWM(10, 0, pos[1]);
        board2.setPWM(0, 0, pos[2]);
        board2.setPWM(6, 0, pos[3]);
        board3.setPWM(8, 0, pos[4]);
        board3.setPWM(12, 0, pos[5]);
        board4.setPWM(0, 0, pos[6]);
        board4.setPWM(1, 0, pos[7]);
        board4.setPWM(2, 0, pos[8]);



rec = 0;

}
}

Processing code:

import processing.serial.*;

float max_distance;
Serial myPort;
int x;
void setup() {
size(1000, 1000);
noStroke();
max_distance = dist(0, 0, width/8, height/8);
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
}

void draw() {
background(0);

for(int j = 799; j >= 0; j -= 400) {
for(int i = 400; i <= 800; i += 400) {
float size = dist(mouseX, mouseY, i, j);
size = size/max_distance * 70;
ellipse(i, j, size, size);
float y = map(size, 0, 350, 125, 450);

  if (y >= 275 ){
  y = 450;
  }
  x = int (y);
  myPort.write(x);
  println(x);
}

}
}

Why is it that virtually no one is willing to post a connection diagram these days?

hello mate, unfortunately, I didn't find somewhere to draw the diagram

Paper and pencil is good enough for diagram.

Maybe you should write a standalone program controlling the PCA9685 module to make sure that part of your code works as expected.

In order to be able to draw a schematic you have to learn how to read one. These links may help you learn.

Reading a schematic
http://blog.makezine.com/archive/2011/01/reading-circuit-diagrams.html
Colin's Lab video on reading a schematic

Then as @Etienne_74 says pencil and paper are as good as anything else.

@Etienne_74 @b707
Here is my simple daigram guys and as an extra test i connect an ultrasonic sensor to the Arduino board with the same code and it works perfectly going from 0° till 180° the problem happens only with processing and serial connection, thanks for your support

Well it is not a schematic, this sort of thing is called (at best) a block diagram. It is also incredibly hard to read with it being rotated through 90˚.

So I have rotated it and cropped it and here it is.

So now some questions.

  1. Have you set different addresses on each PCA98685 by setting the address links?
  2. Have you a PCA98685 board or a chip?
  3. Why have you no pull up resistors on the I2C lines?
  4. How have you wired up the servos to the PCA98685?
  5. You show no power supply so how are you powering them? There is not enough current capability to power even one servo from the 5V pin of an arduino, let alone 36 servos. As a rough guide you should allow 1A current per servo so this is quite a hefty load.

Now on to the code. You should have posted it like this:-
Processing code

import processing.serial.*;

float max_distance;
Serial myPort;
int x;
void setup() {
size(1000, 1000);
noStroke();
max_distance = dist(0, 0, width/8, height/8);
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
}

void draw() {
background(0);

for(int j = 799; j >= 0; j -= 400) {
for(int i = 400; i <= 800; i += 400) {
float size = dist(mouseX, mouseY, i, j);
size = size/max_distance * 70;
ellipse(i, j, size, size);
float y = map(size, 0, 350, 125, 450);

  if (y >= 275 ){
  y = 450;
  }
  x = int (y);
  myPort.write(x);
  println(x);
}
}
}

So when it starts up Processing will print what connection your myPort port is. What is this? If it is not showing anything the just add a print myPort after you have created it.

Now for the Arduino Code

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

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver board2 = Adafruit_PWMServoDriver(0x41);
Adafruit_PWMServoDriver board3 = Adafruit_PWMServoDriver(0x42);
Adafruit_PWMServoDriver board4 = Adafruit_PWMServoDriver(0x43);

const int num_servos = 9;
int pos [num_servos];
int rec = 0;
int x;

void setup() {
Serial.begin(9600);

board1.begin();
board2.begin();
board3.begin();
board4.begin();
board1.setPWMFreq(50); // Analog servos run at ~60 Hz updates
board2.setPWMFreq(50);
board3.setPWMFreq(50);
board4.setPWMFreq(50);

}

void loop() {
if (Serial.available()>0){
int message = Serial.read();
int x = (message);
pos[rec] = x;
rec++;
}
if(rec == num_servos){
        board1.setPWM(7, 0, pos[0]);
        board1.setPWM(10, 0, pos[1]);
        board2.setPWM(0, 0, pos[2]);
        board2.setPWM(6, 0, pos[3]);
        board3.setPWM(8, 0, pos[4]);
        board3.setPWM(12, 0, pos[5]);
        board4.setPWM(0, 0, pos[6]);
        board4.setPWM(1, 0, pos[7]);
        board4.setPWM(2, 0, pos[8]);

rec = 0;

}
}

When you programmed the Arduino did the serial port you used match the serial port of the Arduino?
If you have the serial monitor running close it down and open up another port that matches your Processing Port. By the way what version of Processing are you using?

You might want to look at this [How to get the best from this from this Forum (How to get the best out of this forum) before you proceed any further. It tells you how to post code and ask questions.

2 Likes
  1. Wiring question. Good sketch, but what gauge wire is between your 100A 5V supply and the three PCA boards? I'd use the largest wire that will fit into the puny screw terminals.
  2. You're using println() in your processing. I know nothing about Processing, but something like println() makes me suspicious you'll have line-end characters (0x0d, 0x0a, one or the other or both). I see no handling for that in your serial receive. That means after the first transmission, you'll be on or two characters out of sync, and that will go on and on. Best read Serial Basics before continuing.
    With kudos to @Grumpy_Mike for cleaning up and reposting your code for you.

Sorry for that dude

  1. yes
  2. board
  3. why should I use pull-up resistors on the I2C lines and how to use them?
  4. of course
  5. I powered the drivers by using a 5v/100w multiple USB charger so every PCA9685 board gets one USB cable connected to the GND and V+ ports

and Serial myPort is a variable as you see here myPort = new Serial(this, portName, 9600);

sure

4.0.1
and the last thing I wanna add, did you get what is my problem?

hello mate

a normal USB charging cable

bro the println() in the processing code print's the data that is being sent from processing to Arduino via Serial connection so it let me know what exactly Arduino gets, is it clear now?

Gotcha. Like I said, Processing isn't my world.

Still, focusing only on the Arduino side, you've got no way to remain in sync with what's being sent to you; you may not send extra chars, but you also won't recover from missing ones. Good luck with that, it's a bad dependency in the serial communications domain; you're also at the mercy of start-of-program spurious characters, which we see all the time.

Insufficient(and I doubt you have a 100 Ampere 5v USB charger; more likely, you've misread the ratings plate). You're running servos, sonny(I am quite sure I'm not your bro). Check out the specifications for one servo's stall current, multiply by the number of servos on any given board as a worst-case current flow.
Check the web for ampacity specifications for the wire gauges in your connection. Do some research, then some thinking about what happens when an insufficient wire(read small resistor) is inserted between a motor and a power source.
Yes, it will likely work, for testing one or two servos at a time. But that's it.

So 100W not 100A as @camsysca picked up on.
So some simple maths, if you have a power supply with a capability of 5V and a total power of 100W
Watts = Voltage * Current
So Current = Watts / voltage.
Plug in the numbers and you get
Current = 100 / 5 = 20A
So at 12 Servos per PLA9685 you should be OK.
However you said:-

That is not the sort of power supply that has a USB connector. Are you sure on the ratings of this power supply?
This is what a power supply of this capability normally looks like:-


Can you post a picture of your power supply please.

You use pull-up resistors on the I2C lines because these lines are what is known as open drain (sometimes open collector) outputs. They can only pull down. That means the only way the lines can be at a HIGH or logic one state is if they are pulled up. That means connected to 5V through a resistor. Now it is likely that your PCA9685 board (make unspecified) has a 10K pull up resistor on each board giving you an effective total resistance of 3K3 (or 3.3K). If this is true then you don't need to add any more.

Yes I know that, but what does it say it is in the Processing console when you start the processing code running?

Shame about that I am still on 3.8, still, I will have a look and see if I can get together a simple example that just does the serial communication between Processing and an Arduino.

I understand that you perceive your problem to be the inability to send data between Processing and an Arduino. However, if the Arduino side of your setup is not correct in the first place then sending it data is not going to help. A common mistake is trying to do too much at the same time instead of getting the fundamentals working first. Getting the fundamentals of communication would help you sort exactly where you are going wrong.

OK so the move from Processing 3 to 4 proved to be a bit more time consuming than I imagined. Still after three hours of work here are two sketches that will send a message from Processing to and Arduino and the Arduino will receive the message and echo it back to the Processing console. So you can check what message was sent.

What you had wrong in the original code is the allocation of the ports, assuming the port you wanted was in the first item in a list of ports meant you never picked the right one. You also did not choose the write command to talk the the Arduino. Even then the code only sent sent single bytes.

The messages that are sent in these example are two variables separated by a comer in the form of "servo number , servo angle", I assume that is what you might need. The servo number is the square you hover the cursor over and the servo angle is, in this code, is fixed. I am assuming in the final code you might want to set this value with a series of sliders, instead of these squares in the example.

You will also want to actually do something with the message on the Arduino side.

Processing Code

/**
 * Simple Write. 
 * 
 * Check if the mouse is over a rectangle and writes the status to the serial port. 
 */


import processing.serial.*;
Serial port; // Create object from Serial class
String connectTo = "/dev/cu.usbmodem14201";  // the name of the device driver to use
Serial[] myPorts = new Serial[2]; // list of ports

int pos; // the servo number
int servoPos = 45; // servo position in degrees
byte x, y;
boolean lastTime = false;

void setup() {
  size(130, 130);
  noStroke();
  frameRate(20);
  
int portNumber = 99;
    String [] ports;
    printArray(Serial.list()); // uncomment for full list of serial devices
    ports = Serial.list();
      for(int j = 0; j< ports.length; j++) { // go through all ports looking for ours
         if(connectTo.equals(Serial.list()[j])) portNumber = j;         
    } 
    if(portNumber == 99) portNumber = 0; // if we haven't found our port then connect to the first one
    String portName = Serial.list()[portNumber]; 
    println("Connected to "+portName);
    port = new Serial(this, portName, 57600);
    port.bufferUntil(13);  // call serialEvent every line feed
  
  
    fill(180);
  for(x=0; x<4 ; x++){
    for(y=0; y<4; y++){
      rect(10+ x*30, 10+ y*30, 20, 20);
    } }
}

void draw() {
 // background(255);
  if (mouseOverRect() == true && lastTime == false) {  // If mouse is over square,
    fill(255);                    // change color and
    //port.write(0x21); 
    pos = (x * 16) | (byte)(3 - (int)y);
    println("square position "+pos);
    port.write(str(pos));
    port.write(0x2C); // ASCASI for commer ","
    port.write(str(servoPos)); // servo position
    port.write(0);
    
     lastTime = true;
     rect(10+ x*30, 10+ y*30, 20, 20);         // Draw a square
  }
 if(mouseOverRect() == false && lastTime == true) {                        // If mouse is not over square,
    fill(0);                      // change color and
     lastTime = false;
     rect(10+ x*30, 10+ y*30, 20, 20);         // Draw a square
  }
}

boolean mouseOverRect() { // Test if mouse is over square
boolean over = false;
   for(int x1=0; x1<4 ; x1++){
    for(int y1=0; y1<4; y1++){
      if((mouseX >= (10 + x1*30)) && (mouseX <= (30 + x1*30)) && (mouseY >= (10 + y1*30)) && (mouseY <= (30 + y1*30))){
        over= true;
        x = (byte)x1;
        y = (byte)y1;
      }
    } }
  return (over);
}

// look for stuff from the Arduino se we can print it
void serialEvent(Serial port) {  // this gets called everytime a line feed is recieved
  String recieved = port.readString() ;  
    println(recieved + " from serial port");
}

Note here you need to change the line
String connectTo = "/dev/cu.usbmodem14201"; // the name of the device driver to use
To the name that the Arduino is presenting as the serial port. To help you do that I have left in the code that shows all the available serial ports, so you can see the name of the one you want.

** Arduino Code**

// simple sketch to get messages from Processing
String incoming;  // buffer to hold message

void setup() {
  Serial.begin(57600); // to match the processing sketch
  delay(100); // let the serial port get up and running
}

void loop() {
  if(Serial.available() != 0)  { // got something to read
    incoming = Serial.readStringUntil(0);
     // echo message to the processing consol
     Serial.print("got message --> ");
     Serial.println(incoming);
     // now do something with the message.
  }
}

actually, I just solved the problem, but I have a Q
how could I read or know what's happening on the Arduino serial monitor while there is the processing monitor sending data, when I run the serial monitor it gives me this message:

Error opening serial port 'COM3'. (Port busy)

and thank you mate

see the code i posted. It uses the serial event call back. But both your port and the Arduino need to be on the same device.

To be clear you should not open any serial monitor windows. It all gets echoed back to the console of the Processing window.

It would be interesting to see your solution.