I hacked away at some example code to control 2 servos realtime via my pc with an old Uno over USB.
First off, is an Uno apropos for this ? Im getting stuttered jumpy movements. My host app sends serial 30x per sec. tried different baud rates, buffer size. I'm wondering if I need to switch to an Arduino with a hardware serial port, or if my code is just awful, or if it's something else:
// Example 5 - Receive with start- and end-markers combined with parsing
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use when parsing
// variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
int Int2FromPC = 0;
boolean newData = false;
//============
void setup() {
Serial.begin(57600);
// Serial.println("This demo expects 3 pieces of data - text, an integer and an integer");
// Serial.println("Enter data in this style <HelloWorld, 12000, 24000> ");
// Serial.println();
pwm.begin();
pwm.setPWMFreq(60); // Analog servos run at ~60 Hz updates
delay(10);
}
//============
void loop() {
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
parseData();
// showParsedData();
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
strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
integerFromPC = atoi(strtokIndx); // convert this part to an integer
// dxlRegWrite2(1, AX_GOAL_POSITION_L, integerFromPC);
pwm.setPWM(0, 0, integerFromPC);
strtokIndx = strtok(NULL, ",");
Int2FromPC = atoi(strtokIndx); // convert this part to an int
pwm.setPWM(1, 0, Int2FromPC);
}
//============
void showParsedData() {
Serial.print("Message ");
Serial.println(messageFromPC);
Serial.print("Integer ");
Serial.println(integerFromPC);
Serial.print("INt2 ");
Serial.println(Int2FromPC);
}
Why are you using Adafruit_PWMServoDriver.h for driving the servos rather than the simpler Servo.h? Are you actually using an Adafruit I2C servo driver board? Which one?
Second question - what servos and how are they powered? A circuit diagram would be good.
yes, thanks. Im using adafruit 16x12bit servo shield and 2 giant Torxis 12 volt servos with their own separate discrete power supply lines. As to why Im using it instead of servo lib ..... hmmm. though it might be better for realtime remote serial control since the shield would be doing the PWM I guess.
I need to switch to an Arduino with a hardware serial port
Your Uno HAS a hardware serial port.
strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
But, you never use the original data, so it is NOT necessary to copy it. Save time; stop copying.
thks Paul,
I commented out the offending string copy.
I upped baud, reduced size of buffer to 12 chars.
No noticeable difference in stutter yet. It happens mostly when I send a rush of numbers from mouse or kinect gesture, as if the whole communication chain just isn't keeping up. I may be just overwhelming the response time that the servos are capable of ? I haven't opened them up yet, but i think there's a little board in there with a few adjustments. I wonder now if I'm just sending the servo too much too fast before it can react and settle in it's destination.
// Example 5 - Receive with start- and end-markers combined with parsing
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
const byte numChars = 12;
char receivedChars[numChars];
//char tempChars[numChars]; // temporary array for use when parsing
// variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
int Int2FromPC = 0;
boolean newData = false;
//============
void setup() {
Serial.begin(115200);
// Serial.println("This demo expects 3 pieces of data - text, an integer and an integer");
// Serial.println("Enter data in this style <HelloWorld, 12000, 24000> ");
// Serial.println();
pwm.begin();
pwm.setPWMFreq(60); // Analog servos run at ~60 Hz updates
delay(10);
}
//============
void loop() {
recvWithStartEndMarkers();
if (newData == true) {
// strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
parseData();
// showParsedData();
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(receivedChars,","); // get the first part - the string
strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
integerFromPC = atoi(strtokIndx); // convert this part to an integer
pwm.setPWM(0, 0, integerFromPC);
strtokIndx = strtok(NULL, ",");
Int2FromPC = atoi(strtokIndx); // convert this part to an int
pwm.setPWM(1, 0, Int2FromPC);
}
//============
void showParsedData() {
Serial.print("Message ");
Serial.println(messageFromPC);
Serial.print("Integer ");
Serial.println(integerFromPC);
Serial.print("INt2 ");
Serial.println(Int2FromPC);
}
wanted to say thanks for the tips, Paul. I was able to narrow down some things about my motors stuttering , inspired by you 'asking the right questions'
basically I tuned the PID controls on the onboard microcontrollers and got acceptable performance/smoothness. The issue didnt seem to be the serial throughput after all. Your suggestion inspired me in this direction. Cheers.
As for the 'P' in my data stream that was a placeholder for a future feature. I wanted to 'stress test ' the amount of serial I could send. My current thought is that serial is probably not a bottleneck as much as I thought.
I will be going back to my dynamixels soon enough anyhow, as they have the ability to send high res position info. Will definitely be more serial 'fun' ahead.