Go Down

Topic: Read String from Serial Input, parse it, and write it to the EEPROM. (Read 1 time) previous topic - next topic

overflow

Hello,

I'm trying to do something that should be very simple but it seems much more complicated. I need to receive a String like this:

12:22:-1:30:123:112

This string should then be splitted by the colons and each byte stored in a specific position of the EEPROM:

Please take a look at the following code:

Code: [Select]

  byte tmpBuff[6];
  int pos=0;
  int previousStartConfig=0;
  String configuration = command.substring(9);
  for (int i=0; i < configuration.length(); i++)
  {
    if ( configuration.charAt(i) == ':' )
    {
        tmpBuff[pos] = tmpBuff, configuration.substring(previousStartConfig, i);
        previousStartConfig = i+1;
        pos++;
    }
  }
 
  if ( pos < 6 )
  {
    Serial.println("Bad configuration!");
    return;
  } 


Now, I know that this piece of code doesn't work and I know that the problem is due to the conversion from the string to byte. But how can I fi this?

PS: Feel free to improve this piece of code because I know it sucks (full of variables ).  XD

Arrch


Hello,

I'm trying to do something that should be very simple but it seems much more complicated. I need to receive a String like this:

12:22:-1:30:123:112

This string should then be splitted by the colons and each byte stored in a specific position of the EEPROM:

Please take a look at the following code:

Code: [Select]

 byte tmpBuff[6];
 int pos=0;
 int previousStartConfig=0;
 String configuration = command.substring(9);
 for (int i=0; i < configuration.length(); i++)
 {
   if ( configuration.charAt(i) == ':' )
   {
       tmpBuff[pos] = tmpBuff, configuration.substring(previousStartConfig, i);
       previousStartConfig = i+1;
       pos++;
   }
 }
 
 if ( pos < 6 )
 {
   Serial.println("Bad configuration!");
   return;
 }  


Now, I know that this piece of code doesn't work and I know that the problem is due to the conversion from the string to byte. But how can I fi this?

PS: Feel free to improve this piece of code because I know it sucks (full of variables ).  XD


Does the string have any end of packet marker? New line? carriage return?

Get rid of the String object and use null-terminated char arrays. A finite state machine should be used to keep track of where in the string you are.

Psuedo code:
If serial information available
{
 read character
 if character is a colon or end-of-packet marker
 {
   null terminate the buffer
   convert string to number (atoi())
    store number in EEPROM
   reset the buffer
 }
 else
 {
   store character in buffer
 }
}

overflow



Hello,

I'm trying to do something that should be very simple but it seems much more complicated. I need to receive a String like this:

12:22:-1:30:123:112

This string should then be splitted by the colons and each byte stored in a specific position of the EEPROM:

Please take a look at the following code:

Code: [Select]

  byte tmpBuff[6];
  int pos=0;
  int previousStartConfig=0;
  String configuration = command.substring(9);
  for (int i=0; i < configuration.length(); i++)
  {
    if ( configuration.charAt(i) == ':' )
    {
        tmpBuff[pos] = tmpBuff, configuration.substring(previousStartConfig, i);
        previousStartConfig = i+1;
        pos++;
    }
  }
 
  if ( pos < 6 )
  {
    Serial.println("Bad configuration!");
    return;
  } 


Now, I know that this piece of code doesn't work and I know that the problem is due to the conversion from the string to byte. But how can I fi this?

PS: Feel free to improve this piece of code because I know it sucks (full of variables ).  XD


Does the string have any end of packet marker? New line? carriage return?

Get rid of the String object and use null-terminated char arrays. A finite state machine should be used to keep track of where in the string you are.


Hi Arrch,

Thank you for your reply.

The String ends with a carriage return but this is not passed to my function. Let me show you how I'm parsing the commands:
This is the code that I have in the loop() subroutine:
Code: [Select]

if ( Serial.available() > 0 )
{
      c = Serial.read();
      if (c==13)
        processCommand();
      else
        command.concat(c);
   
   Serial.print(c); //echo purposes
}


I keep appending chars to a global variable called command and when I receive a CR I process the command. I didn't used char arrays because I find them harder to handle but I'm willing to give it a try. Can you show me some example? Can I use string functions like substring with chars?

Thanks!

Arrch


I keep appending chars to a global variable called command and when I receive a CR I process the command.

Instead, you should be appending characters until you receive a carriage return OR colon.
Quote
I didn't used char arrays because I find them harder to handle but I'm willing to give it a try.

They are slightly harder to handle when you don't know much experience with C, but microcontrollers don't have near the amount of memory available that the microprocessors do, so handling data intensive items, such as String objects, is not recommended. There is also a known bug with the String object in the Arduino environment that can cause memory issues. Best practice is not to use it.

Quote
Can you show me some example?


Simple example that will read numbers from the serial line until it gets a new line and convert to an int:

Code: [Select]
static char buffer[6];
static int i=0;
if (Serial.available() > 0)
{
 char inByte = Serial.read();
 if (inByte == '\n')
 {
   // convert the string to a number, and print
   Serial.print("Number received: ");
   Serial.println(atoi(buffer));
   // reset the buffer
   i=0;
   inByte[0] = '\0';
 }
 else
 {
   inByte[i++];  // concatanate the character
   inByte[i] = '\0'; // null terminate the array
 }
}


Quote
Can I use string functions like substring with chars?


List of available string functions:
http://www.cplusplus.com/reference/cstring/

zoomkat

Quote
Simple example that will read numbers from the serial line until it gets a new line and convert to an int:


interesting snippet, but I seem to have a compile error at    inByte[0] = '\0';
Google forum search: Use Google Advanced Search and use Http://forum.arduino.cc/index in the "site or domain:" box.

PaulS

Code: [Select]
    inByte[0] = '\0';
  }
  else
  {
    inByte[i++];  // concatanate the character
    inByte[i] = '\0'; // null terminate the array
  }

should be
Code: [Select]
    buffer[0] = '\0';
  }
  else
  {
    buffer[i++];  // concatanate the character
    buffer[i] = '\0'; // null terminate the array
  }


Is 6 characters enough for the buffer?

overflow

Hello guys,

I've changed all my code to use char arrays instead of strings but I'm still struggling to parse and store data inside the EEPROM.

This is my current code:

Code: [Select]

void writeConfig()
{

  char *myCommand = "saveConfig 2:2:2:2:2:2:2:2";
  char *tmpBuff;
  char configuration[64];
  int comma=0;
 

  for(int i=0; i < strlen(myCommand); i++)
  {
    if( myCommand[i] == ':' ) comma++;
  }

  if ( (comma < 7)  || (strlen(myCommand) < 26) )
  {
    Serial.println("Invalid configuration.");
    return;
  }
   
  strncpy(configuration, (myCommand+11), sizeof(myCommand) - 11);
 
  tmpBuff = strtok(configuration, ":");
    int counter=0;
    while (tmpBuff != NULL)
  {
    EEPROM.write(counter, int(tmpBuff));
    tmpBuff = strtok (NULL, ":");
    counter++;
  }
 

  Serial.println("Configuration saved!");
}


Please ignore the lines below "strncpy(configuration, (myCommand+11), sizeof(myCommand) - 11);" because they werent tested yet. This line is crashing the arduino and it is reseting everytime it is executed and I don't know why!

All i'm trying to do is:

1st - Receive a string (I've hardcoded it for testing purposes -> char *myCommand = "saveConfig 2:2:2:2:2:2:2:2";)
2nd - Cut the head leaving just the configuration argument.
3rd - Split the configuration argument using the commas as delimiter
4th - Save each of those numbers in the EEPROM. Any of them will always be a number ranging from 0 to 255

How can i accomplish this?

PaulS

Quote
for(int i=0; i < strlen(myCommand); i++)
  {
    if( myCommand == ':' ) comma++;
  }

This is a comma --> ,
This is a colon --> :

See the difference?

Code: [Select]
    EEPROM.write(counter, int(tmpBuff));
The int() macro is for people that think (int)tmpBuff looks weird. Personally, I'd have told those people to get over it. The int() macro does NOT convert a char array to an int. The atoi() function does that. All that it does is cast the address of the token to an int, which is hardly the data that you want to be storing in the EEPROM.

overflow


Quote
for(int i=0; i < strlen(myCommand); i++)
  {
    if( myCommand == ':' ) comma++;
  }

This is a comma --> ,
This is a colon --> :

See the difference?

Code: [Select]
    EEPROM.write(counter, int(tmpBuff));
The int() macro is for people that think (int)tmpBuff looks weird. Personally, I'd have told those people to get over it. The int() macro does NOT convert a char array to an int. The atoi() function does that. All that it does is cast the address of the token to an int, which is hardly the data that you want to be storing in the EEPROM.


Hellom PaulS, thank you for your reply :)

About the first part, please bear with me. I was actually thinking in colon ( as you can see, I'm using commas in the hardcoded command and also when I'm searching for it) but switched the names (I'm Portuguese).  ;)

I've also corrected the problem related to the cast but that still doesn't fixe the original issue.

Everytime the strncpy is executed my arduino is resetedand I have no idea why :(

PaulS

Quote
Everytime the strncpy is executed my arduino is resetedand I have no idea why

Nor do I, since the code you posted doesn't call strncpy(). The usual culprit, though, is copying outside the bounds of the destination array. Copy 12 bytes into a 2 element array is not a good thing. Nor is copying to a location pointed to be a non-initialized pointer.

The comma variable should be named delimterCounter, so that it doesn't matter what the delimiter is.

zoomkat

Quote
I've changed all my code to use char arrays instead of strings but I'm still struggling to parse and store data inside the EEPROM.


I couldn't get any of the posted stuff to work either. Below is simple code to captrure a string. It uses the String class thing, but it actually works (at least for me).

Code: [Select]

//zoomkat 3-5-12 simple delimited ',' string parce
//from serial port input (via serial monitor)
//and print result out serial port
// CR/LF could also be a delimiter

String readString;

void setup() {
  Serial.begin(9600);
  Serial.println("serial delimit test 1.0"); // so I can keep track of what is loaded
}

void loop() {

  //expect a string like wer,qwe rty,123 456,hyre kjhg,
  //or like hello world,who are you?,bye!,
 
  if (Serial.available())  {
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == ',') {
      //do stuff
      Serial.println(readString); //prints string to serial port out
      readString=""; //clears variable for new input     
     } 
    else {     
      readString += c; //makes the string readString
    }
  }
}

Google forum search: Use Google Advanced Search and use Http://forum.arduino.cc/index in the "site or domain:" box.

guix

Hello,

If you want simplicity (and have enough memory to waste) you can use sscanf, example:

http://arduino.cc/forum/index.php/topic,134496.msg1011549.html#msg1011549

overflow


Quote
Everytime the strncpy is executed my arduino is resetedand I have no idea why

Nor do I, since the code you posted doesn't call strncpy(). The usual culprit, though, is copying outside the bounds of the destination array. Copy 12 bytes into a 2 element array is not a good thing. Nor is copying to a location pointed to be a non-initialized pointer.

The comma variable should be named delimterCounter, so that it doesn't matter what the delimiter is.


Thanks for the tip related to the variable name. You are right and it is much more clearer that way :D

But the strncpy is in fact called. If you look closely you will see that it is the first thing called after I check if the minimum number of colons is present.

Anyway thanks for the help. I was using wrong index values and now everything seems to work perfectly :)

But now I have another question:

I have my code protected agains buffer overrun, but how can I validate my code against invalid input?

i.e:

Lets imagine that the user pass the following argument:

Quote
1:2:3:1:1:a:!:9


How can I parse the content and be sure I will only read numbers (preferably between 0 and 255)?

What I'm currently doing is using the atoi() function to parse the string, and everytime atoi() returns 0 I compare this value to the original string just to make sure that the "zero" wasn't actually the real value being passed (due to the fact that atoi() returns 0 everytime it wasn't able to parse the string into an int and this may be confused the a real zero).

Is this the most effective way of doing it?

overflow


Quote
I've changed all my code to use char arrays instead of strings but I'm still struggling to parse and store data inside the EEPROM.


I couldn't get any of the posted stuff to work either. Below is simple code to captrure a string. It uses the String class thing, but it actually works (at least for me).

Code: [Select]

//zoomkat 3-5-12 simple delimited ',' string parce
//from serial port input (via serial monitor)
//and print result out serial port
// CR/LF could also be a delimiter

String readString;

void setup() {
  Serial.begin(9600);
  Serial.println("serial delimit test 1.0"); // so I can keep track of what is loaded
}

void loop() {

  //expect a string like wer,qwe rty,123 456,hyre kjhg,
  //or like hello world,who are you?,bye!,
 
  if (Serial.available())  {
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == ',') {
      //do stuff
      Serial.println(readString); //prints string to serial port out
      readString=""; //clears variable for new input     
     } 
    else {     
      readString += c; //makes the string readString
    }
  }
}




Thanks for your response, but my problem wasn't related to reading strings from serial input but parsing them after reading :)

Arrch


Is this the most effective way of doing it?

Loop through each character, checking if it is < '0', >'9' or != ':'

Go Up