Detecting the end of an incoming serial transmission

I've been trying to figure out how I can determine if an incoming serial transmission has completed. This is assuming that the incoming transmission will never exceed the buffer, and that a new transmission will not start before the completed transmission has been read. The incoming transmission will end with a carriage return, but may also have carriage returns within the transmission.

I am considering using a loop on Serial.available:

#include SoftwareSerial;

softwareSerial mySerial(rx, tx);

...
void loop() {

//do stuff

//check for incoming transmission
if(incomingTransmission(myBuffer) > 0) {

//do something with incoming transmisison

}

//do other stuff

}

//function to retrieve incoming transmission

int incomingTransmission(char charBuffer) {

int charactersAvailable = 0, charsRead = 0;

//first check whether an incoming transmission has started
while(mySerial.available() > 0){

//check if characters are still arriving
if(charsAvailable != mySerial.available()) {

charsAvailable = mySerial.available();

}

//otherwise read all the characters
else {

//read all the characters
while(mySerial.available() > 0){

//read and add character to buffer
charBuffer(charsRead) = mySerial.read();
charsRead++;

}
}
}
return charsRead;
}

What kind of trouble am I getting myself into?

Please show code in Code Tags </> and without excess empty lines.

If nothing helps check for a gap in the transmission, that indicates a pause between transmissions.

Do you have any information on the message being sent?

Sometimes msgs have headers with a byte or two of fixed info to help align the receiver but also a byte or two with information on, say, how many bytes in this msg etc.

If you don't have that I'd suggest a character timeout of a few character times at the baud rate you're communicating at.

Use a value that’s never part of your transmission.
If you’re transmitting plain text, any value >127 will do. 255 is a good one, allowing you to use extended ASCII in your message. 0 is also good, as c strings are null terminated anyway.
Serial is so slow, you normally don’t have to expect more than one character in the Serial buffer. At 9600 bps it takes 1 ms for a character to be transmitted, that’s a long time for an Arduino. Nonetheless, by just checking mySerial.available() you can see whether there is more in the buffer. No need for fancy things there, either.

You’d get something like this:

const byte MAX_SIZE = 100;
char buffer[MAX_SIZE + 1]; // must fit maximum length of the message plus the null terminator.
byte messageLength = 0;

void loop() {
  if (mySerial.available()) readSerial();
  if (messageComplete) {
    handleMessage();
    messageComplete = false;
    messageLength = 0;
  }
}

void readSerial() {
  while (mySerial.available()) {
    char c = mySerial.read();
    if (c == o) { // End of message: the null terminator.
      messageComplete = true;
      buffer[messageLength] = 0; // NULL terminator.
      messageLength++; // Total size of the string, including terminator.
      break; // Make sure you don't continue reading - just in case there's more in the buffer.
    }
    else {
      if (messageLength < MAX_SIZE) {
        buffer[messageLength] = c;
        messageLength++;
      }
      else {
        // Message too long!
      }
    }
  }
}

Blackfin: No, other than knowing that the message will end with a final carriage return.

wvmarie: I am receiving communication from an external device that will transmit a string of characters, ending with a final CR, but possibly including CRs within the message. I do not have control over the characters being sent by the external device - that is, I cannot direct the device to end its message with anything other than a CR.

Dr Diettrich: The incoming transmissions from the external device are prompted by an outgoing message to the device. There will be no incoming transmission if the external device has not been prompted.

In my current version of the application I am building, I have a delay set in the function that reads from the incoming serial stream, in order that the read loop will not terminate before the last character has been read. I have been hoping to avoid this delay.

This is all in the context of a difficulty I was having in the application: echoing the incoming message to an LCD (also using SoftwareSerial), was causing some interference with the reading of the incoming message. I need some way to ensure that the incoming message is read completely before anything is sent to the LCD, and my skill level is not up to managing interrupts.

Then you have no choice but to consider every CR as end of message, and deal with each subsequent message-part.

Code as I suggested remains the same, just change the end marker value to 13.

Is there a well-defined time interval between messages?

If so you could record the time when a character is received and if the interval after a character exceeds your threshold you would know that you are between messages.

The second example in Serial Input Basics could be amended to work like that.

Of course it may be sufficient to use it as intended with the end-marker changed to a carriage-return

...R

wvmarie: I had not considered treating each part of the total incoming stream as a separate message. There are occasions on which the incoming message will consist of 30 CR terminated segments. I can read and echo each one separately, rather than trying to read, and then echo, all 30 at once.

Thanks.

This is assuming that the incoming transmission will never exceed the buffer,

Not a good assumption.

and that a new transmission will not start before the completed transmission has been read.

Also not good.

The incoming transmission will end with a carriage return,

You can't assume that either.

The root of all the above assumptions is the last one. What if it is the CR at the end which is the ONLY character smashed by a glitch?

Always check there is space in the buffer for EVERY incoming character. If the buffer is unaccoutably full then the usual course of action is to discard the whole thing and wait for a new start-of-message character. It is usually not worthwhile scanning the full buffer to find a new start char.

MorganS:
Always check there is space in the buffer for EVERY incoming character.

Even with all those assumptions I still actually added this check in my example code :slight_smile: