Unpredictable behavior on parsing key-value delimited input from serial I am having trouble with a "simple" command parser; I'm guessing I'm doing something wrong with memory allocation - appreciate any pointers :)

For context: I'm running on an Arduino Uno (if it matters). The commands I expect are of the form &lt;key1=value1;key2=value2;key3=value3&gt;. For simplicity here (as well as for my own failed attempts at debugging), I've removed everything extraneous, so that this code just prints back to the serial monitor whatever the current value associated with the key "text" is every time a command enclosed in &lt; > is encountered.

This seems to work correctly until after the first time the key is found; then it behaves somewhat unpredictably - haven't yet been able to discern the pattern.  But, for instance, if I send:

&lt;a=5&gt;
-&gt; EMPTY
&lt;b=7;text=here;d=9&gt;
-&gt; here
&lt;d=abc&gt;
-&gt; here
&lt;d=ghi;e=afds&gt;
-&gt; afds


That last displayed value is wrong, since no new key of text was sent over serial. The complete code is here:


// command indicators
char START_MARKER = '&lt;';
char END_MARKER = '&gt;';

const int MAX_KEY_VALUE_PAIRS = 3;  // maximum number of key-value pairs in message
const int MAX_ELEMENT_CHARS = 25;  // the maximum number of characters (+1 for terminator) in a key or a value

// message format: &lt;key1=value1;key2=value2;key3=value3&gt;
const int MAX_MESSAGE_CHARS = (MAX_KEY_VALUE_PAIRS * (MAX_ELEMENT_CHARS + 1)) * 2 + (MAX_KEY_VALUE_PAIRS - 1) + 2;  // maximum message size

char received_chars[MAX_MESSAGE_CHARS];  // &lt;- EDIT#1
char received_chars[30] ={'\0'}; 
bool new_data = false;

char *text;  // the value identified by the "text" keyword, i.e.: in &lt;text=abc;time=814a&gt;, text would be abc.

void ParseData(char *str) {
  // This picks off the ;-delimited key-value pairs and assigns them to a multi-dim array
  char * pch;
  int pairs_count = 0;
  char config[MAX_KEY_VALUE_PAIRS][2][MAX_ELEMENT_CHARS];

  pch = strtok(str, "=");

  while (pch != NULL)
  {
    strcpy(config[pairs_count][0], pch);
    pch = strtok(NULL, ";");
    if (pch == NULL) break;

    strcpy(config[pairs_count][1], pch);
    pairs_count++;
    pch = strtok(NULL, "=");
    if (pch == NULL) break;
  }

  for(int i=0;i&lt;pairs_count;i++) {
    if (strcmp(config[i][0], "text")==0) text = config[i][1];    
  }
  Serial.println(text);
}

void ReadSerial(){
  // After calling ReceiveText to empty the buffer, if a complete command has been found,
  // parse that command.
  new_data = ReceiveText();
  if (new_data == true) {
    char temp_chars[MAX_MESSAGE_CHARS];  // temporary array for use when parsing
    strcpy(temp_chars, received_chars);
    received_chars[0] = '\0';  // &lt;- EDIT#2
    ParseData(temp_chars);
  }
}

boolean ReceiveText() {
  // This dumps the characters on the buffer so far received_chars, searching for an END_MARKER
  // along the way; if it finds one, it goes back to find a START_MARKER; if that is also found,
  // the string within is the ;-delimited set of key value pairs
  static boolean recv_in_progress = false;
  static byte ndx = 0;
  char rc;

  boolean new_data = false;
  while (Serial.available() &gt; 0 &amp;&amp; new_data == false) {
    rc = Serial.read();

    if (recv_in_progress == true) {
      if (rc != END_MARKER) {
        received_chars[ndx] = rc;
        ndx++;
        if (ndx &gt;= MAX_MESSAGE_CHARS) {
          ndx = MAX_MESSAGE_CHARS - 1;
        }
      } else {
        received_chars[ndx] = '\0'; // terminate the string
        recv_in_progress = false;
        ndx = 0;
        new_data = true;
      }
    } else if (rc == START_MARKER) {
        recv_in_progress = true;
    }
  }
  return new_data;
}

void setup() {  
  Serial.begin(9600);
  text = (char*)malloc(25);
  strcpy(text, "EMPTY");
  received_chars[0] = '\0';
}

void loop() {
  ReadSerial();
}


Thanks!
Welcome to the I forgot to empty my buffer char club (I lost some days finding a simlar problem)

 strcpy(temp_chars, received_chars)  // strcpy empties temp_chars and adds the received_chars


but you have no (for me) visible routine to empty received_chars
Do this either by setting 

received_chars[0] ='\0'; // set the pointer to first index


or 

 received_chars ="0";


or

strcpy(received_chars,"0");


after you used the buffer content. By using 

static byte ndx = 0;


you just set the pointer and then start to write, as a safequard you should first reset the end terminator and then start to write. This is probably the reason you see characters even if no key is pressed due to a still "full" charbuffer.To prevent heap corruption define your received_chars as a fixed arraybefore setup:

char received_chars[128] ={'\0'}; // 127 chars + terminator


if you use malloc you have to use free, but doing that with a lot of dynamic data causes heap fracturing -> crash in the end

EDIT
The program does now the following 


ignores all char not enclosed by &lt;>
check for text and displays everything after = and before;
ignores everything after ; if its not text= again
if its text= again ignores the first and only displays the second param


You do not need strtok here as it deletes chars I use a modified substring routne for char arrays.
Here is the code working as described above:

// command indicators
const char START_MARKER = '&lt;';
const char END_MARKER = '&gt;';

const uint8_t MAX_KEY_VALUE_PAIRS = 3;  // maximum number of key-value pairs in message
const uint8_t MAX_ELEMENT_CHARS = 25;  // the maximum number of characters (+1 for terminator) in a key or a value

// message format: &lt;key1=value1;key2=value2;key3=value3&gt;
const uint8_t MAX_MESSAGE_CHARS = (MAX_KEY_VALUE_PAIRS * (MAX_ELEMENT_CHARS + 1)) * 2 + (MAX_KEY_VALUE_PAIRS - 1) + 2;  // maximum message size

//char received_chars[MAX_MESSAGE_CHARS] = {'\0'};// &lt;- EDIT#1
char received_chars[256] = {'\0'};
bool new_data = false;
char text[25] =  {'\0'};      // the value identified by the "text" keyword, i.e.: in &lt;text=abc;time=814a&gt;, text would be abc.

void ParseData(char *str) {
  // This picks off the ;-delimited key-value pairs and assigns them to a multi-dim array
  char * pch;
  int pairs_count = 0;
  char config[MAX_KEY_VALUE_PAIRS][2][MAX_ELEMENT_CHARS];

  pch = strtok(str, "=");

  while (pch != NULL)  {
    strcpy(config[pairs_count][0], pch);
    pch = strtok(NULL, ";");
    if (pch == NULL) break;

    strcpy(config[pairs_count][1], pch);
    pairs_count++;
    pch = strtok(NULL, "=");
    if (pch == NULL) break;
  }

  for (uint8_t i = 0; i &lt; pairs_count; i++) {
    if (strcmp(config[i][0], "text") == 0) strcpy (text,config[i][1]);
  }
  Serial.println(text);
}

void ReadSerial() {
  // After calling ReceiveText to empty the buffer, if a complete command has been found,
  // parse that command.
  new_data = ReceiveText();
  if (new_data == true) {
    char temp_chars[MAX_MESSAGE_CHARS];  // temporary array for use when parsing
    strcpy(temp_chars, received_chars);
    received_chars[0] = '\0';  // &lt;- EDIT#2
    ParseData(temp_chars);
  }
}

bool ReceiveText() {
  // This dumps the characters on the buffer so far received_chars, searching for an END_MARKER
  // along the way; if it finds one, it goes back to find a START_MARKER; if that is also found,
  // the string within is the ;-delimited set of key value pairs
  static boolean recv_in_progress = false;
  static byte ndx = 0;
  char rc;

  bool new_data = false;
  while (Serial.available() &gt; 0 &amp;&amp; new_data == false) {
    rc = Serial.read();

    if (recv_in_progress == true) {
      if (rc != END_MARKER) {
        received_chars[ndx] = rc;
        ndx++;
        if (ndx &gt;= MAX_MESSAGE_CHARS) {
          ndx = MAX_MESSAGE_CHARS - 1;
        }
      } else {
        received_chars[ndx] = '\0'; // terminate the string
        recv_in_progress = false;
        ndx = 0;
        new_data = true;
      }
    } else if (rc == START_MARKER) {
      recv_in_progress = true;
    }
  }
  return new_data;
}

void setup() {
  Serial.begin(115200);
   strcpy (text, "EMPTY"); 
 //  text = (char*)malloc(25);
//  received_chars[0] = '\0';
}

void loop() {
  ReadSerial();
}


If you could describe what you really want in the end I'm sure we can help with some basic routines.