Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Arduino serial data parsing

Tags:

arduino

I'm writing an app to control my robot with my Android phone over Bluetooth, everything is goes well, data is echoed and verified, but I'm having some trouble with the protocol, specifically I want my robot's wheels to turn when I send a command such as s,10,100 or s,-30,-10... (values in percent).

My problem is that when I want to parse my wheel speed command on my Arduino I must parse from up to 4 separate bytes to int, for example s,-100,-100 makes my robot go backwards at full speed, but how do I parse this so I can call setSpeed(left, right); with leftand right equal to -100?

I know I can separately analyse every byte and put them together to get an integer, but it's not very elegant and there's probably a better solution to all this already, unfortunately I haven't found it yet.

EDIT

Here's my Arduino function for parsing my commands:

void parseCommand(char* command, int* returnValues)
{
  // parsing state machine
  byte i = 2, j = 0, sign = 0;
  int temp = 0;
  while(*(command + i) != '\0')
  {
    switch(*(command + i))
    {
      case ',':
        returnValues[j++] = sign?-temp:temp;
        sign = 0;
        temp = 0;
        break;
      case '-':
        sign = 1;
        break;
      default:
        temp = temp * 10 + *(command + i) - 48;
    }
    i++;
  }
  // set last return value
  returnValues[j] = sign?-temp:temp;
}

You call it this way when parsing something like s,100,-100 (must be \0 terminated):

char serialData[16];
void loop()
{
  if(Serial.available() > 0)
  {
    Serial.readBytesUntil('\0', serialData, 15);
    switch(serialData[0])
    {
      case 's':
        int speed[2];
        parseCommand(serialData, speed);
        setSpeed(speed[0], speed[1]);
        break;
    }
    // always echo
    Serial.write(serialData);
    // end of message is maked with a \0
    Serial.print('\0');

    // clear serialData array
    memset(serialData, 0, sizeof(serialData));
  }
}
like image 411
Solenoid Avatar asked Aug 28 '12 14:08

Solenoid


People also ask

What does serial parseInt do in Arduino?

The parseInt() function from the Serial library is made to scan down the serial receive buffer one byte at a time in search of the first valid numerical digit. So if you have “314” in the serial receive buffer, you'd get 314 returned the first time you call Serial.

How does Arduino send serial data?

USB is one of the most common methods used for serial communication, hence the name Universal Serial Bus. Using Arduino we can easily send and receive data over a USB cable with the built-in Arduino Serial Library.

Can Arduino read serial monitor?

Serial monitor is used to see receive data, send data,print data and so on. Serial monitor is connected to the Arduino through serial communication. This serial communication occurs using RX (pin 0) and TX (pin 1) terminal of Arduino. Any kind of data can send through this serial monitor.


2 Answers

Just read character by character into a state machine. It's simple and efficient.

To read in a number digit by digit, do this: Start with zero. For each digit, multiply the number by ten and add the value of the digit. So, for example, reading 97 would work like this:

  1. You read in a digit with no prior digit, you start with 0.

  2. You read in 9 and compute (0*10)+9 -> 9

  3. You read in 7 and compute (9*10)+7 -> 97

  4. You read in a non-digit, you output the 97.

Here's a fuller example s,10,100:

  1. You start in the "ready to read command state".

  2. You read "s", "s" is the command. You switch to the "ready to read first comma" state.

  3. You read the first comma, you switch to the "ready to figure out the sign of the first parameter" state.

  4. You read a digit. Since this wasn't a "-", the first parameter is positive. You set the first number to the value of the digit, 1. You are now in "reading first number" state.

  5. You read a digit, 0. You set the first number to 1*10+0 -> 10. You are still in "reading first number" state.

  6. You read a comma. You are now in the "ready to figure out sign of the second parameter" state.

  7. You read 1. The second number is positive (since this wasn't a "-"). You set the second number to 1. You are in the "reading second number" state.

  8. You read 0. The second number is now set to 1x10+0 -> 10. You are still in "reading second number" state.

  9. You read 0. The second number is now set to 10x10+0 -> 100. You are still in "reading second number" state.

  10. You read an end of line. You execute your results: The command is "s", the first number is positive, the first number is 10, the second number is positive, the second number is 100.

  11. You switch back to "ready to read command" state.

like image 190
David Schwartz Avatar answered Oct 19 '22 18:10

David Schwartz


I like the answer by David Swartz, but thought I'd play devil's advocate.

Reading the data in as binary can be elegant, it just depends on what you need to do with it.

In the following example, data is read from serial until it sees the binary delimiter 0X7F. The bytes read are stored in the inData char array. Take a look at the documentation for Serial.readBytesUntil()

char inData[16];
int bRead;
bRead = Serial.readBytesUntil(0x7F,inData,4);

This byte can then be cast to an integer or otherwise manipulated. Keep in mind the maximum value for this would be +/-126 because this is a signed char (127 is a delimiter and wouldn't be seen as a value).

You could access these values by with something like the following:

Serial.print("Bytes Read: ");
Serial.println(bRead);
Serial.println("First Byte");
Serial.println((int)inData[0]);
Serial.println(
            map((int)inData[0],0,126,0,1024)
);
Serial.println("Second Byte");
Serial.println((int)inData[1]);

I tested this out with the following bash command (after making sure serial speeds were set properly):

echo -ne '\x01\x02\x7F' > /dev/ttyACM0

Some rough sample code that I wrote can be found Here

like image 39
ZnArK Avatar answered Oct 19 '22 17:10

ZnArK