Charleston, SC
Offline
Full Member
Karma: 0
Posts: 105
|
 |
« on: December 31, 2012, 06:41:26 pm » |
Hello, Hope everyone has a great New Year  Finally have my webcam hooked up to my bot using RoboRealm tracking 'a red object' setup. The data 'distanceX' is sent via Serial to my Arduino. DistanceX is basically the center of gravity of the object, X value of a 320 x 240 image on cam. In my Arduino sketch I have a function 'trackCalc()' to receive the data in a buffer and depending on the value of distanceX, drives my motors, then resets the buffer. The problem I'm having is that when an object is within the center threshold (forward), every several seconds it stutters just for a second (almost like it wants to turn). Left and right differential turns are fine though... Here is the function: void trackCalc() {
if (Serial.available()) { // Get the data coming through the serial port and store it in the buffer while (z < 4) { incomingData[z] = Serial.read(); // Assign the input value to the incomingData buffer z++; // Increment the counter }
distanceX = atoi(incomingData); // Convert ASCII to Int
if((distanceX > 100) && (distanceX < 220)) // forward threshold of image (X value or 320) { // go forward myservoSteering.write(neutralSteering); myservoThrottle.write(medFowThrottle); }
else {
if(distanceX <= 99) { myservoThrottle.write(neutralThrottle); myservoSteering.write(slowLeftSteering); }
else if(distanceX >= 221) { myservoThrottle.write(neutralThrottle); myservoSteering.write(slowRightSteering); }
} } z = 0; // Reset the counter delay(20);//delay(20);// Delay 20ms to allow the servo to rotate to the position
}
And the code in the loop: void loop() { //... // PING code here... //... if(range [2] >= pingThreshCenter) // if no obstructions detected on center PING sensor run function 'trackCalc()' { trackCalc(); } else if(range [2] < pingThreshCenter) // if there is an obstruction detected, stop motors (neutral). { myservoThrottle.write(neutralThrottle); myservoSteering.write(neutralSteering); //delay(10); } }
Thomas
|
|
|
|
« Last Edit: January 08, 2013, 10:00:11 pm by thomas3120 »
|
Logged
|
|
|
|
|
Seattle, WA USA
Online
Brattain Member
Karma: 316
Posts: 35533
Seattle, WA USA
|
 |
« Reply #1 on: December 31, 2012, 07:50:48 pm » |
if (Serial.available()) { // Get the data coming through the serial port and store it in the buffer while (z < 4) { incomingData[z] = Serial.read(); // Assign the input value to the incomingData buffer z++; // Increment the counter }
If there is at least one character available to read, read all 4 of them. distanceX = atoi(incomingData); // Convert ASCII to Int Then, without NULL terminating the array, pass it to a function that expects a NULL terminated array of chars. Try again.
|
|
|
|
|
Logged
|
|
|
|
|
Charleston, SC
Offline
Full Member
Karma: 0
Posts: 105
|
 |
« Reply #2 on: December 31, 2012, 08:24:18 pm » |
Thanks for the reply Paul,
Not exactly sure what you mean. Are you saying that when there is one character being sent and available to read, it's trying to read 4 characters?
Can you explain a bit more about 'NULL terminating the array'?
Instead of 4 elements/buffers in the array, would I need just one?
I may need my hand held just a bit on this one... :/
t
|
|
|
|
|
Logged
|
|
|
|
|
Charleston, SC
Offline
Full Member
Karma: 0
Posts: 105
|
 |
« Reply #3 on: January 01, 2013, 12:37:10 am » |
Looking around at a few references, would using this be better: int incomingByte = 0; // for incoming serial data
void setup() { Serial.begin(9600); // opens serial port, sets data rate to 9600 bps }
void loop() {
// send data only when you receive data: if (Serial.available() > 0) { // read the incoming byte: incomingByte = Serial.read();
// say what you got: Serial.print("I received: "); Serial.println(incomingByte, DEC); } }
t
|
|
|
|
|
Logged
|
|
|
|
|
Charleston, SC
Offline
Full Member
Karma: 0
Posts: 105
|
 |
« Reply #4 on: January 01, 2013, 06:31:41 am » |
I found this code in a previous post here, wondering if I'm getting closer to what Paul suggested? int serReadInt() { int i, serAva; // i is a counter, serAva hold number of serial available char inputBytes [7]; // Array hold input bytes char * inputBytesPtr = &inputBytes[0]; // Pointer to the first element of the array if (Serial.available()>0) // Check to see if there are any serial input { delay(5); // Delay for terminal to finish transmitted // 5mS work great for 9600 baud (increase this number for slower baud) serAva = Serial.available(); // Read number of input bytes for (i=0; i<serAva; i++) // Load input bytes into array inputBytes = Serial.read(); inputBytes = '\0'; // Put NULL character at the end return atoi(inputBytesPtr); // Call atoi function and return result } else return -1; // Return -1 if there is no input }
t
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
UK
Offline
Brattain Member
Karma: 138
Posts: 19067
I don't think you connected the grounds, Dave.
|
 |
« Reply #5 on: January 01, 2013, 06:36:29 am » |
inputBytes[i] = Serial.read(); would help keep the compiler happy
|
|
|
|
« Last Edit: January 01, 2013, 06:57:06 am by AWOL »
|
Logged
|
Pete, it's a fool looks for logic in the chambers of the human heart.
|
|
|
|
Seattle, WA USA
Online
Brattain Member
Karma: 316
Posts: 35533
Seattle, WA USA
|
 |
« Reply #6 on: January 01, 2013, 06:56:03 am » |
char * inputBytesPtr = &inputBytes[0]; // Pointer to the first element of the array Why do you think you need another pointer to the start of the array? delay(5); // Delay for terminal to finish transmitted // 5mS work great for 9600 baud (increase this number for slower baud)
No, this is a crappy workaround. Whatever is sending the value needs to append an end of packet marker, so that the Arduino can read and store the data as fast as it arrives, until the end of packet marker arrives. Only then should the data be used.
|
|
|
|
|
Logged
|
|
|
|
|
Charleston, SC
Offline
Full Member
Karma: 0
Posts: 105
|
 |
« Reply #7 on: January 01, 2013, 07:19:55 am » |
char * inputBytesPtr = &inputBytes[0]; // Pointer to the first element of the array Why do you think you need another pointer to the start of the array? delay(5); // Delay for terminal to finish transmitted // 5mS work great for 9600 baud (increase this number for slower baud)
No, this is a crappy workaround. Whatever is sending the value needs to append an end of packet marker, so that the Arduino can read and store the data as fast as it arrives, until the end of packet marker arrives. Only then should the data be used. Thanks for the replies AWOL & Paul, @ Paul That wasn't my code, was trying to figure out what you said about 'a function that expects a NULL terminated array of characters' and was just seeing if I was getting somewhat closer to what you were saying with that code I came across...
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Online
Brattain Member
Karma: 316
Posts: 35533
Seattle, WA USA
|
 |
« Reply #8 on: January 01, 2013, 07:33:50 am » |
and was just seeing if I was getting somewhat closer to what you were saying with that code I came across... Here is code I use to receive properly delimited data on the Arduino: #define SOP '<' #define EOP '>'
bool started = false; bool ended = false;
char inData[80]; byte index;
void setup() { Serial.begin(57600); // Other stuff... }
void loop() { // Read all serial data available, as fast as possible while(Serial.available() > 0) { char inChar = Serial.read(); if(inChar == SOP) { index = 0; inData[index] = '\0'; started = true; ended = false; } else if(inChar == EOP) { ended = true; break; } else { if(index < 79) { inData[index] = inChar; index++; inData[index] = '\0'; } } }
// We are here either because all pending serial // data has been read OR because an end of // packet marker arrived. Which is it? if(started && ended) { // The end of packet marker arrived. Process the packet
// Reset for the next packet started = false; ended = false; index = 0; inData[index] = '\0'; } } This expects you to send data starting with a '<' and ending with a '>'. Data is ignored until a '<' arrives. It is then stored until a '>' arrives. When the '>' arrives, the "Process the packet" area is where you would call atoi(inData) to get the numeric value, and use it. Then, the flags, array, and index are reset to read the next packet.
|
|
|
|
|
Logged
|
|
|
|
|
Charleston, SC
Offline
Full Member
Karma: 0
Posts: 105
|
 |
« Reply #9 on: January 05, 2013, 08:07:17 am » |
and was just seeing if I was getting somewhat closer to what you were saying with that code I came across... Here is code I use to receive properly delimited data on the Arduino: #define SOP '<' #define EOP '>'
bool started = false; bool ended = false;
char inData[80]; byte index;
void setup() { Serial.begin(57600); // Other stuff... }
void loop() { // Read all serial data available, as fast as possible while(Serial.available() > 0) { char inChar = Serial.read(); if(inChar == SOP) { index = 0; inData[index] = '\0'; started = true; ended = false; } else if(inChar == EOP) { ended = true; break; } else { if(index < 79) { inData[index] = inChar; index++; inData[index] = '\0'; } } }
// We are here either because all pending serial // data has been read OR because an end of // packet marker arrived. Which is it? if(started && ended) { // The end of packet marker arrived. Process the packet
// Reset for the next packet started = false; ended = false; index = 0; inData[index] = '\0'; } } This expects you to send data starting with a '<' and ending with a '>'. Data is ignored until a '<' arrives. It is then stored until a '>' arrives. When the '>' arrives, the "Process the packet" area is where you would call atoi(inData) to get the numeric value, and use it. Then, the flags, array, and index are reset to read the next packet. I appreciate the help and code Paul, Got it to work and understanding more about packets and 'SOP' start of packet and 'EOP' end of packet. Right now I have the X-axis or motor forward, left and right working perfect using the packet <distanceX> sent over serial to the Arduino. I'd like to incorporate the head tilt servo (which is 'distanceY') and having a bit of trouble getting the 2 sets of data to work(together). In the VBScript in roborealm I have and/or tried under serial and 'Send Sequence': <distanceX,distanceY>
And in the Arduino code: distanceX,distanceY = atoi(inData);
If tried a few variations but with no luck. Should I send the 2 different X and Y data in 2 different packets? In the Arduino code is there anything else I would need to modify or add? I read in my book something about adding a comma or 'delimiting character'? Any help appreciated, t
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Online
Brattain Member
Karma: 316
Posts: 35533
Seattle, WA USA
|
 |
« Reply #10 on: January 05, 2013, 08:49:00 am » |
In the VBScript in roborealm I have and/or tried under serial and 'Send Sequence': That will work. And in the Arduino code: That won't. The packet looks something like <27,43>, so inData ends up containing 27,43. The atoi() method scans the input and converts as much as possible to an int, so it returns 27, because the comma is not a numeric character. The comma operator on the left side of the equal sign does something, but it is almost certainly not what you are expecting. You really should read up on what functions you want to use are doing, like atoi(). When the packet contains multiple values, you need to use strtok() to extract each one, as a string, and call atoi() on each token.
|
|
|
|
|
Logged
|
|
|
|
|
Charleston, SC
Offline
Full Member
Karma: 0
Posts: 105
|
 |
« Reply #11 on: January 05, 2013, 05:01:21 pm » |
In the VBScript in roborealm I have and/or tried under serial and 'Send Sequence': That will work. And in the Arduino code: That won't. The packet looks something like <27,43>, so inData ends up containing 27,43. The atoi() method scans the input and converts as much as possible to an int, so it returns 27, because the comma is not a numeric character. The comma operator on the left side of the equal sign does something, but it is almost certainly not what you are expecting. You really should read up on what functions you want to use are doing, like atoi(). When the packet contains multiple values, you need to use strtok() to extract each one, as a string, and call atoi() on each token. Thanks again Paul  I know that atoi() is used to convert an ASCII to Integer, another is atol(), ASCII to Long. As well as itoa(), convert Integer to ASCII. Looking up strtok(), function is to split the string into tokens. An example in C++ would be: /* strtok example */ #include <stdio.h> #include <string.h>
int main () { char str[] ="- This, a sample string."; char * pch; printf ("Splitting string \"%s\" into tokens:\n",str); pch = strtok (str," ,.-"); while (pch != NULL) { printf ("%s\n",pch); pch = strtok (NULL, " ,.-"); } return 0; }
I read an example in one of my Arduino books about using 'fields' // const int NUMBER_OF_FIELDS = 3; int fieldIndex = 0; int values[NUMBER_OF_FIELDS]; //... //...
Would this be also another approach I could look at? t
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Online
Brattain Member
Karma: 316
Posts: 35533
Seattle, WA USA
|
 |
« Reply #12 on: January 05, 2013, 05:53:43 pm » |
Would this be also another approach I could look at? Possibly, although it seems to me that you know how many fields there will be (2) and exactly what the fields represent.
|
|
|
|
|
Logged
|
|
|
|
|
Charleston, SC
Offline
Full Member
Karma: 0
Posts: 105
|
 |
« Reply #13 on: January 06, 2013, 07:08:55 am » |
Would this be also another approach I could look at? Possibly, although it seems to me that you know how many fields there will be (2) and exactly what the fields represent. Hey Paul, I came up with this here, let me know if I'm getting close (in getting the 2 data tokens I need from Serial) in this code: void setup() {
char inString[]= "distanceX, distanceY"; char delimiters[]= "!:,"; char* valPosition;
valPosition = strtok(inString,delimiters); int distance[] = {0,0};
Serial.begin(9600);
for(int i=0; i<2; i++) { distance[i] = atoi(valPosition); Serial.println(distance[i]); valPosition = strtok(NULL,delimiters); } }
void loop() { //... //... }
|
|
|
|
« Last Edit: January 06, 2013, 07:15:00 am by thomas3120 »
|
Logged
|
|
|
|
|
Seattle, WA USA
Online
Brattain Member
Karma: 316
Posts: 35533
Seattle, WA USA
|
 |
« Reply #14 on: January 06, 2013, 07:27:34 am » |
I came up with this here, let me know if I'm getting close (in getting the 2 data tokens I need from Serial) in this code: You are close. The only issue is that "distanceX" will be rather useless data to pass to atoi(), as will "distanceY".
|
|
|
|
|
Logged
|
|
|
|
|
|