I'm trying to control a couple of servos with a PCA9685 however I can't seem to get the code to control them correct. Based on the pwmtest example it just has this for loop to move them around:
for (uint16_t i=0; i<4096; i += 8) {
for (uint8_t pwmnum=0; pwmnum < 16; pwmnum++) {
pwm.setPWM(pwmnum, 0, (i + (4096/16)*pwmnum) % 4096 );
}
}
And when i put that into my code the involves a radio receiver to determine the position and servo number it works fine (note that I've just copied and pasted not actually integrated it into the code. It still just runs the loop without external data affecting it). However, when I try to integrate the code to turn the servos based on the radio data it is receiving it does not work.
String str;
int state = radio.readData(str);
joint = str.charAt(0);
jointi = joint.toInt();
str.remove(0, 2);
str2 = str.toInt();
for (uint16_t i=str2; i<str2+1; i += 1) {
for (uint8_t pwmnum=jointi; pwmnum < (jointi+1); pwmnum++) {
pwm.setPWM(pwmnum, 0, (i + (4096/16)*pwmnum) % 4096 );
}
}
That is one variation I've tried to keep the code as close to the example as possible. What I thought would definitely work doesn't as well:
String str;
int state = radio.readData(str);
joint = str.charAt(0);
str.remove(0, 2);
str2 = str.toInt();
pca9685.setPWM(joint.toInt(), 0, str2);
By doesn't work I'm referring to the servo's not moving. However, I can see the PWM pulses changing on an oscilloscope. Does anyone know why this is?
You definitely don’t need the two nested for loops to run only one pca9685.setPWM()
How are the servo powered, what’s your arduino? How is the radio connected and what are the requirements ?… a circuit and details on the components would help.
Of course I second what @groundFungus said, print the data
when you want to drive SERVOS you should start with the SERVO example.
The code to control them is identical, it just moves them in different ways, and the pwm code works fine for servos.
You definitely don’t need the two nested for loops to run only one pca9685.setPWM()
That was the pwmtest example, idk what the difference is between the loops and just using pca9685.setPWM(), as this is my main problem of why the example works and not mine.
a circuit and details on the components would help.
I'll try to make some but it might take some time
We usually like to see the whole program
Here is one that doesn't work:
#include <RadioLib.h>
#include <Adafruit_PWMServoDriver.h>
#include <Wire.h>
Si4432 radio = new Module(5, 16, 17);
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
String joint;
int str2;
int jointi;
void setup() {
Serial.begin(115200);
pwm.begin();
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(54);
Wire.setClock(400000);
// initialize Si4432 with default settings Upper: 24883200 Lower: 24883199
Serial.print(F("[Si4432] Initializing ... "));
// Write to pwm
int state = radio.begin();
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true);
}
// set the function that will be called
// when new packet is received
radio.setPacketReceivedAction(setFlag);
// start listening for packets
Serial.print(F("[Si4432] Starting to listen ... "));
state = radio.startReceive();
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true);
}
// if needed, 'listen' mode can be disabled by calling
// any of the following methods:
//
// radio.standby()
// radio.sleep()
// radio.transmit();
// radio.receive();
// radio.readData();
}
// flag to indicate that a packet was received
volatile bool receivedFlag = false;
// this function is called when a complete packet
// is received by the module
// IMPORTANT: this function MUST be 'void' type
// and MUST NOT have any arguments!
#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
void setFlag(void) {
// we got a packet, set the flag
receivedFlag = true;
}
void loop() {
// check if the flag is set
if(receivedFlag) {
// reset flag
receivedFlag = false;
// you can read received data as an Arduino String
String str;
int state = radio.readData(str);
if (state == RADIOLIB_ERR_NONE) {
// packet was successfully received
// Serial.println(F("[Si4432] Received packet!"));
joint = str.charAt(0);
jointi = joint.toInt();
str.remove(0, 2);
str2 = str.toInt();
// Drive each PWM in a 'wave'
for (uint16_t i=str2; i<str2+1; i += 1) {
for (uint8_t pwmnum=jointi; pwmnum < (jointi+1); pwmnum++) {
pwm.setPWM(pwmnum, 0, (i + (4096/16)*pwmnum) % 4096 );
}
}
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
// packet was received, but is malformed
Serial.println(F("CRC error!"));
} else {
// some other error occurred
Serial.print(F("failed, code "));
Serial.println(state);
}
// put module back to listen mode
radio.startReceive();
}
}
The only thing I'm changing is the section where it takes the radio data and assigns variables to it starting here: joint = str.charAt(0); and ending after the for loop. All I need to do is replace the for loop with an exact copy of the original example and everything works fine. The weirdest problem I'm having is why it shows the changes on the oscilloscope for the broken ones yet does not move the servos.
One possible reason why your servos are not moving is that you are using the wrong frequency for the PWM signal. Servos typically expect a frequency of 50 Hz, which means a pulse every 20 milliseconds. However, the default frequency of the PCA9685 module is 200 Hz, which means a pulse every 5 milliseconds. This can cause the servos to behave erratically or not at all. You can change the frequency of the PCA9685 module by using the setPWMFreq() function in your code. For example, you can add this line before your loop:
pwm.setPWMFreq(50); // Set the frequency to 50 Hz
This should set the frequency to 50 Hz, which is suitable for most servos.
It isn't likely related to your present problem, but if you're using your breadboard as a power distribution center for the current needed for servos, you'll be back, asking why it doesn't work, is unpredictable, or actually fried your breadboard. Breadboard contacts shouldn't be subjected to any more than 100 mA, if you want to use the breadboard indefinitely. Use a power distribution board, a set of wire nuts, or lever-nuts to fan out your power supply current.
See here is the problem with solely text based communication cause I actually ment thank you yet I can't tell if your sarcasm post was sarcastic about my from your pov sarcastic post, so was your sarcastic post a response to my non sarcastic post that may have come off as sarcastic or am I thinking to hard about this?
Talking about breadboards, some of the long ones like in your picture have the power rails split in the middle so the left side of the red and blue lines are not connected to the right side.
You can see if that’s the case for your breadboard as the blue and red line are interrupted towards the middle.
(100mA in the breadboard is being hyper conservative you can definitely pass way more under 5V but I second the opinion that you don’t want high current in breadboards in general as you might have undesirable consequences and there should be no breadboard in a final / permanent installation - those DuPont wires will likely create issues).
I finally figured it out. For some reason my servos weren't accepting instantaneous changes in pwm. I had to use a for loop to iterate through their current position to the one I want them to be in. No clue why they are like this but it is.
Here is the working code, sorry for the late reply I kinda forgot:
#include <RadioLib.h>
#include <Adafruit_PWMServoDriver.h>
#include <Wire.h>
Si4432 radio = new Module(5, 16, 17);
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
String joint;
int str2;
int jointi;
int servo0;
int servo1;
int servo2;
int servo3;
int servo4;
int servo5;
void setup() {
Serial.begin(115200);
pwm.begin();
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(54);
Wire.setClock(400000);
// initialize Si4432 with default settings Upper: 24883200 Lower: 24883199
Serial.print(F("[Si4432] Initializing ... "));
// Write to pwm
int state = radio.begin();
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true);
}
// set the function that will be called
// when new packet is received
radio.setPacketReceivedAction(setFlag);
// start listening for packets
Serial.print(F("[Si4432] Starting to listen ... "));
state = radio.startReceive();
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true);
}
}
// flag to indicate that a packet was received
volatile bool receivedFlag = false;
#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
void setFlag(void) {
// we got a packet, set the flag
receivedFlag = true;
}
void moveServo(int servo) {
if (servo>str2) {
for (uint16_t pulselen = servo; pulselen > str2; pulselen-=1) {
pwm.setPWM(jointi, 0, pulselen);
}
} else if (servo<str2) {
for (uint16_t pulselen = servo; pulselen < str2; pulselen+=1) {
pwm.setPWM(jointi, 0, pulselen);
}
} else if (servo==str2) {
Serial.println("Same Value");
} else {
Serial.println("Error values");
}
}
void loop() {
// check if the flag is set
if(receivedFlag) {
// reset flag
receivedFlag = false;
// you can read received data as an Arduino String
String str;
int state = radio.readData(str);
if (state == RADIOLIB_ERR_NONE) {
// packet was successfully received
Serial.println(F("[Si4432] Received packet!"));
Serial.print(str);
joint = str.charAt(0);
jointi = joint.toInt();
str.remove(0, 2);
str2 = str.toInt();
if (jointi==0) {moveServo(servo0); servo0=str2;}
if (jointi==1) {moveServo(servo1); servo1=str2;}
if (jointi==2) {moveServo(servo2); servo2=str2;}
if (jointi==3) {moveServo(servo3); servo3=str2;}
if (jointi==4) {moveServo(servo4); servo4=str2;}
if (jointi==5) {moveServo(servo5); servo5=str2;}
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
// packet was received, but is malformed
Serial.println(F("CRC error!"));
} else {
// some other error occurred
Serial.print(F("failed, code "));
Serial.println(state);
}
// put module back to listen mode
radio.startReceive();
}
}
I don't argue with success, so party on, but your solution seems implausible. That loop probably finishes before the servo is even aware it's being asked to move. The function call takes a bit of time, but relative to the frequency of the servo control signal, no time at all.
I'll try to measure that when I get to the lab, and see how servos work here.
I hope this
No clue why they are like this but it is.
is not the case, or applies for some odd reason to your servo, either all servos of that make and model or the ones you were lucky enough to get.