Efficient reading and parsing of continous serial data

Hello,

I have a sensor which is outputting data at a high rate (200 Hz). The data always starts with a value of 222 followed by the information and then it repeats.

I would appreciate example code showing how to correctly and efficiently parse this data then display EACH value before reading the next value ...? I am using the code below but it is not efficient as I am throwing away the remainder of the buffer as you can see below ....

float takeReading()
{
    digitalWrite(rs485Pin, LOW);                        // Disable RS485 Transmit
    len =  Serial1.readBytes((char *)buf, 8);       //Process after 8 Bytes

    if (len > 0)
    {
      for (int i = 0; i <= len - 2; i++)
      {
        if (buf[i] == 222)
        {
          measure =  (int(buf[i + 1]) +  (buf[i + 2]));
          i = i + 2;
          break;       //Leave for
        }
      }
    }
    Serial1.flush();
    return measure;
}

Have a look at Robin2's updated Serial Input Basics :slight_smile:

Serial.flush is for sending, not receiving. Just think of a toilet.

Why does takeReading() return a global variable? Why is the return type float, when the value being returned is an int (regardless of the type that measure is).

Thank you very much @septillion for the link, I appreciate it. It also taught me the correct way to read the serial stream.

Thank you @aarg, I deleted that statement.

Thank you @PaulS. Point well taken. No need to declare it as a global variable (bad programming practice on my side).

For more info on parsing input, google "LALR(1) parser"

enum ReadingState {
  WAITING_FOR_222,
  WAITING_FOR_FIRST_BYTE,
  WAITING_OR_SECOND_BYTE,
  GOT_READING
} reading_state = WAITING_FOR_222;

int measure;

void loop() {
  take_reading();
  if(reading_state == GOT_READING) {
    // do something with the reading
    reading_state = WAITING_FOR_222;
  }
}

void take_reading() {
  static byte first_byte;
  static byte second_byte;

  if(!Serial1.available()) return;

  // the outer loop has not consumed the reading, so don't read the serial
  // This may or may not be the right thing to do here
  if(state == GOT_READING) return; 

  byte input = Serial1.read();

  switch(reading_state) {
  case WAITING_FOR_222:
    if(input == 222) {
      reading_state = WAITING_FOR_FIRST_BYTE;
    } 
    break;

  case WAITING_FOR_FIRST_BYTE:
    first_byte = input;
    reading_state = WAITING_FOR_SECOND_BYTE;
    break;

  case WAITING_FOR_SECOND_BYTE:
    second_byte = input;

    // I'm surprised that the original code just adds these without bit-shifting them
    // also, it's important to use unsigned because of possible sign-extension
    measure =  (unsigned int) first_byte + (unsigned int) second_byte;
    reading_state = GOT_READING;
    break;

  case GOT_READING:
    // discard the byte we just read. This may or may not be the right thing to do here
    break;
  }
}

Several ways to do it, of course. 'Take reading' could return a boolean rather than using a GOT_READING state, or it could return a -1 if there was no reading (which the loop would have to check for). Or you could bury it all in a class and have a "yep_we_got_a_reading" and a "I_have_consumed_the_reading" method in the class.

enum ReadingState {
  WAITING_FOR_222,
  WAITING_FOR_FIRST_BYTE,
  WAITING_OR_SECOND_BYTE
} reading_state = WAITING_FOR_222;

int measure;

void loop() {
  if(take_reading()) {
    // do something with the reading
  }
}

boolean take_reading() {
  static byte first_byte;
  static byte second_byte;

  if(!Serial1.available()) return false;

  byte input = Serial1.read();

  switch(reading_state) {
  case WAITING_FOR_222:
    if(input == 222) {
      reading_state = WAITING_FOR_FIRST_BYTE;
    } 
    return false;

  case WAITING_FOR_FIRST_BYTE:
    first_byte = input;
    reading_state = WAITING_FOR_SECOND_BYTE;
    return false;

  case WAITING_FOR_SECOND_BYTE:
    second_byte = input;
    reading_state = WAITING_FOR_222;

    // I'm surprised that the original code just adds these without bit-shifting them
    // also, it's important to use unsigned because of possible sign-extension
    measure =  (unsigned int) first_byte + (unsigned int) second_byte;
    return true;

  }
}