I have a project that needs to control 5 servos simultaneously with as little delay as possible (the servos are triggering musical notes). I am using savox digital 12-bit servos that run from 250-333hz. (I am using Due, and I have set the variant.h file to 333Hz). I am using analogWrite to control the servos (yes, I know everybody says don't use analogWrite to control servos, but its fine, as far as I know, if the settings are correct).
I am sending, via serial, a list of numbers, such as 2239 1863 1898 2200 2224, each number corresponding to the desired position of the servo. The is code I am using is below (I am not much of a programmer, and I got the basic concept from some other code I found online).
When I send the list, in my project usually only 2 numbers will change from the previous list. But when all of the numbers change, then somehow arduino gets overloaded or something and sends out signals which are confusing the servos. Sometimes the signals correct themselves after a second or two, and sometimes I have to send a new list to correct them.
As you can see in the code, I have added a delay between each individual analogWrite function. With longer delay times, this signal confusion doesn't happen. But the shorter the delay times, the more often the signals go crazy. Is this a problem with my code? Or a limitation of arduino? I need as close to instantaneous response of the servos as possible.
As you can see in the code, I have added a delay between each individual analogWrite function. With longer delay times, this signal confusion doesn't happen. But the shorter the delay times, the more often the signals go crazy. Is this a problem with my code? Or a limitation of arduino? I need as close to instantaneous response of the servos as possible.
It's likely a problem with his code.
The serial read code is awful, as written, and is probably the problem.
I would completely rewrite that sectino. Take a look at the serial input basics thread. Your life will be made much easier if you put a marker at the start of a set of values you're sending over serial, or better still, before each value, chosen to specify which servo it is), like a1234 b2333, etc.. Since you've got spaces between each value, the space signifies the end of a value, and the letter the start of one. So you can use that to know when you've got the whole message.
Thanks, PaulS for your "grammatical" corrections. A dumb newbie such as myself appreciates all the help he can get! I put the delays in there because for some reason it kept the servos from freaking out when they were all moving at once.
DrAzzy, thanks for the reference to the serial input basics thread. I modified some code from that thread to fit my needs. The code is below. My inputs are now formatted like this: <2239/1863/1898/2200/1842>. I wasn't sure how to implement a separate marker for each number as you suggested. However, I am still getting the same behavior as before. When 1-2 servos are in action, everything is fine, but when 3-5 servos are in motion, I get unpredictable behavior. I'm thinking it might be a hardware issue.
Unfortunately I don't have an oscilloscope (nor can I afford one at the moment) to check the PWM signals coming out of the Arduino. Is it possible that it is a timer issue on the arduino? My power supply to the servos is quite good, so I'm thinking that is not the problem.
Any suggestions how to troubleshoot from here? Could it still be a code problem?
// Receive with start- and end-markers combined with parsing
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use by strtok() function
// variables to hold the parsed data
int servo1 = 0;
int servo2 = 0;
int servo3 = 0;
int servo4 = 0;
int servo5 = 0;
boolean newData = false;
//============
void setup() {
Serial.begin(9600);
Serial.println("Ready");
}
//============
void loop() {
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() replaces the commas with \0
parseData();
servos();
newData = false;
}
}
//============
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
//============
void parseData() {
// split the data into its parts
char * strtokIndx; // this is used by strtok() as an index
strtokIndx = strtok(tempChars,"/"); // get the first part - the string
servo1 = atoi(strtokIndx); // copy it to servo1
strtokIndx = strtok(NULL, "/"); // this continues where the previous call left off
servo2 = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, "/");
servo3 = atoi(strtokIndx);
strtokIndx = strtok(NULL, "/");
servo4 = atoi(strtokIndx);
strtokIndx = strtok(NULL, "/");
servo5 = atoi(strtokIndx);
}
//============
void servos() {
Serial.print("servo1 ");
Serial.println(servo1);
Serial.print("servo2 ");
Serial.println(servo2);
Serial.print("servo3 ");
Serial.println(servo3);
Serial.print("servo4 ");
Serial.println(servo4);
Serial.print("servo5 ");
Serial.println(servo5);
analogWriteResolution(12);
analogWrite(2,servo1);
analogWrite(3,servo2);
analogWrite(4,servo3);
analogWrite(5,servo4);
analogWrite(6,servo5);
}
Ahhhhhhh nevermind!!! It is a power supply issue. I just increased the amps, and now it works perfect. I thought I had enough, but didn't catch there was a quick peak when the servos all moved at once.
Well, the benefit of all this is now I have better code, and I learned more about serial communication!!