Go Down

Topic: Reading values from string and split in commands (Read 1 time) previous topic - next topic

asuryan

Hi dear Arduino-Community!

I would like to build a controll structure/functions that allow me to
controll the arduino with a set of commands from a string.
In the future, there should be the possibilty to either read lines from a
sd-card or recieve that controll string serially from processing.

Ive done a lot research but to be honest I dont really understand the concept
of "*"-pointers, atoi() etc. So its really hard to manipulate a string and use the
data as controll commands. This is what I got so far:

Code: [Select]
#include <Servo.h>

Servo servo1;
Servo servo2;

int newPos = 0;
char p, *i;
int u;
char sBuffer[20];
int ctrlData[20];
boolean gotCmd = false;
int dataLength = 0;

void setup()
{
  servo1.attach(7);
  servo2.attach(8);
  Serial.begin(9600);
  Serial.println("Test");
}


void loop()
{
  getData();
  if(gotCmd == true){
    *i = ctrlData[1];
    int newPos = atoi(i);
    Serial.println(newPos);
    servo1.write(newPos);
    gotCmd = false;
    Serial.println("Command got!");
  }
}

void getData()
{
  if(Serial.available() > 0){
    delay(10);
    int z=0;
    char *str;
    while(z<19){
      sBuffer[z] = Serial.read();
      ++z;
    }
    Serial.flush();
    Serial.println("Ok");
    char *p = sBuffer;
    while ((str = strtok_r(p, ";", &p)) != NULL)
    {
      int pos = atoi(str);
      ctrlData[dataLength] = pos;
      dataLength++;
    }
    gotCmd = true;
  }
}


My problem is that Im not able to convert the ASCII inside the ctrlData[] to integer
so that the servo understands it. Im confused because I initialised the array as integer
so whats the problem here?

Thank you very much for your input!!
Regards!

AWOL

Code: [Select]
  if(Serial.available() > 0){
    delay(10);
    int z=0;
    char *str;
    while(z<19){
      sBuffer[z] = Serial.read();
      ++z;
    }

I think your understanding of serial comms needs some realignment too.
Look what happens here: At 9600, one character takes just over a millisecond to transmit, so the chances that there are 19 characters in the buffer after a delay of only 10 milliseconds are quite low.
"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.

PaulS

Code: [Select]
    while ((str = strtok_r(p, ";", &p)) != NULL)
Why are you using the thread-safe version of the strtok function on a single threaded machine?

Code: [Select]
      dataLength++;
Probably be a good idea to reset this variable at some point.

Code: [Select]
    Serial.flush();
Unless you've got a really good understanding of serial communications, which you don't, you should NEVER use this.

Code: [Select]
    char *p = sBuffer;
    while ((str = strtok_r(p, ";", &p)) != NULL)

sBuffer is NOT a string. You can't use it like one.

You need to figure out the difference between a string and an array of characters. Consult ANY C book.

You haven't told us what you are sending, and why you think every token should be convertible to an int.

asuryan

First of all: thanks for fast input!!
Sorry Im learning so there are so many fields being aware of.

@AWOL:
Ok thanks, not thought of that but it is so obvious... I will change this to a much higher baud rate like 115200.

@PaulS:
- Im not sure what you mean by "thread-safe" version of strtok? Are there any differences? :\
- I will reset this one at the beginning of serial.available() > 0. Good point! :)
- Ok... I never use flush again. Just thought it would be an good idea to clear the serial buffer after the uC got the data.
- Ok I have to do more reasearch about strings and an array of characters. Is there some good reference in the web?

Im sending something like: "2;90"
Where the first token is the number of servo and the second token is the degrees it should move. So I have to
solve 2 problems: 1. Get the data 2.Split this string by the ";" delimiter and use the 2 numbers for servo.write(). Of
course the selection of servo will get a switch-function.


PaulS

Quote
Im not sure what you mean by "thread-safe" version of strtok? Are there any differences?

Sure. The strtok function should not be used, without extreme caution, in a multi-threaded application, while the strtok_r function is safe to use in a multi-threaded application.

If you are not creating a multi-threaded application, you don't need to use the thread-safe version, although, on a PC, it doesn't really hurt. It results in more memory usage, and larger code, though. On the Arduino, it does hurt, since there is little memory available, and code space is limited, too.

Quote
Just thought it would be an good idea to clear the serial buffer after the uC got the data.

It's a little like reading to the end of a paragraph and throwing the rest of the book away. How will you ever find out whodunit?

Quote
Ok I have to do more reasearch about strings and an array of characters. Is there some good reference in the web?

There's about 42 bazillion references. Some of them are even good.

The difference between an array of characters and a string is that a string is an array of characters that is NULL terminated. It is that NULL terminator that tells string functions (ever wondered what the str in strtok meant?) where the end of the array is.

Quote
Im sending something like: "2;90"

If you are sending the data from a program, change what the program sends. If you make the program send "<2;90>" instead, this code can collect it all, as fast as it arrives:
Code: [Select]

#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';
  }
}

Where the process the packet comment is is where you put the code to parse the string and convert the tokens to ints.

Go Up