Pages: [1] 2 3   Go Down
Author Topic: Object color tracking, serial data using strtok() or Serial.parseInt() Updated  (Read 2661 times)
0 Members and 1 Guest are viewing this topic.
Charleston, SC
Offline Offline
Full Member
***
Karma: 1
Posts: 144
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,
Hope everyone has a great New Year smiley
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:
Code:
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:
Code:
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
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
  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.

Code:
    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 Offline
Full Member
***
Karma: 1
Posts: 144
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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 Offline
Full Member
***
Karma: 1
Posts: 144
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Looking around at a few references, would using this be better:

Code:
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 Offline
Full Member
***
Karma: 1
Posts: 144
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I found this code in a previous post here,  wondering if I'm getting closer to what Paul suggested?

Code:
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 Offline
Brattain Member
*****
Karma: 289
Posts: 25697
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
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." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
  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?

Code:
    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 Offline
Full Member
***
Karma: 1
Posts: 144
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
  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?

Code:
    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
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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:
                                                                                                                               
Code:
#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 Offline
Full Member
***
Karma: 1
Posts: 144
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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:
                                                                                                                               
Code:
#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':
 
Code:
<distanceX,distanceY>

And in the Arduino code:
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
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
In the VBScript in roborealm I have and/or tried under serial and 'Send Sequence':
That will work.

Quote
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 Offline
Full Member
***
Karma: 1
Posts: 144
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
In the VBScript in roborealm I have and/or tried under serial and 'Send Sequence':
That will work.

Quote
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 smiley
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:
Code:
/* 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'

Code:
//
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
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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 Offline
Full Member
***
Karma: 1
Posts: 144
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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:

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
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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

Pages: [1] 2 3   Go Up
Jump to: