How to capture a variable stream of characters and process them on a Arduino using serial

arduinoserial-port

I'm trying to read variable streams of characters and process them on the Arduino once a certain string of bytes is read on the Arduino. I have a sample sketch like the following, but I can't figure out how to compare the "readString" to process something on the Arduino. I would like the Arduino to process "commands" such as {blink}, {open_valve}, {close_valve}, etc.

// Serial - read bytes into string variable for string
String readString;

// Arduino serial read - example
int incomingByte;

// flow_A LED
int led = 4;

void setup() {
    Serial.begin(2400);  // Open serial port and set Baud rate to 2400.
    Serial.write("Power on test");
}

void loop() {
    while (Serial.available()) {
      delay(10);
      if (Serial.available() > 0) {
          char c = Serial.read(); // Gets one byte from serial buffer
          readString += c; // Makes the string readString
      }
    }

    if (readString.length() > 0) {
        Serial.println( readString); // See what was received
    }

    if (readString == '{blink_Flow_A}') {
        digitalWrite(led, HIGH); // Turn the LED on (HIGH is the voltage level).
        delay(1000);             // Wait for one second.
        digitalWrite(led, LOW);  // Turn the LED off by making the voltage LOW.
        delay(1000);             // Wait for a second.
    }

Best Answer

Some definitions first:

  • SOP = Start Of Packet (in your case, an opening brace)
  • EOP = End Of Packet (in your case, a closing brace)
  • PAYLOAD = the characters between SOP and EOP
  • PACKET = SOP + PAYLOAD + EOP

Example:

PACKET= {Abc}
SOP = {
EOP = }
PAYLOAD = Abc

Your code should process one character at a time, and should be structured as a state machine. When the code starts, the parser state is "I'm waiting for the SOP character". While in this state, you throw away every character you receive unless it's equal to SOP.

When you find you received a SOP char, you change the parser state to "I'm receiving the payload". You store every character from now on into a buffer, until you either see an EOP character or exhaust the buffer (more on this in a moment). If you see the EOP char, you "close" the buffer by appending a NULL character (i.e. 0x00) so that it becomes a standard NULL-terminated C-string, and you can work on it with the standard functions (strcmp, strstr, strchr, etc.). At this point you pass the buffer to a "process()" function, which executes the operation specified by the payload (1)

You have to specify the maximum length of a packet, and size the receive buffer accordingly. You also have to keep track of the current payload length during the "payload receive" state, so you don't accidentally try to store more payload bytes into the temporary buffer than it can hold (otherwise you get memory corruption). If you fill the receive buffer without seeing an EOP character, then that packet is either malformed (too long) or a transmission error changed the EOP character into something else. In either case you should discard the buffer contents and go back to "Waiting for SOP" state. Depending on the protocol design, you could send an error code to the PC so the person typing at the terminal or the software on that side knows the last command it sent was invalid or not received correctly.

Finally, the blink code in you snipped should be replaced by non-blocking "blink-without-delay"-style code (look at the example that come with the Arduino IDE).

(1) Example of a "process" function:

void process(char* cmd) {
    if (strcmp(cmd, "open_valve") == 0) {
        open_valve();
    }
    else if (strcmp(cmd, "close_valve") == 0) {
        close_valve();
    }
    else {
        print_error("Unrecognized command.");
    }
}
Related Topic