1

I am sending a packet of sensor information over an Arduino XBee setup. The first byte of the packet is the packet header for synchronization. The second byte is the number of bytes in the packet (excluding header and packet size byte). The first byte of the packet payload is an identifying number for the sensor. The second payload byte is the data from the sensor. The third payload byte is an XOR for simple error checking. The first payload byte for the sensor ID will never need to be escaped, but the other two payload bytes may need to be escaped. The following is code I wrote for sending a single packet:

Serial.write(PACKET_HEADER); // Packet header for synchronization
Serial.write(calculatePacketSize(data, XOR)); // Size of packet           
Serial.write(unitInfo);
// Check if the data byte needs to be escaped
if(data == ESCAPE || data == PACKET_HEADER) {
    Serial.write(ESCAPE);
}
Serial.write(data);
// Check if the XOR byte needs to be escaped
if(XOR == ESCAPE || XOR == PACKET_HEADER) {
    Serial.write(ESCAPE);
}
Serial.write(XOR);

What I am unsure about is how to process a packet on the receiving end. One issue I see is that because there are a variable number of payload bytes, I cannot check via Serial.available() whether or not there are enough bytes to process the payload during this run through of loop(). How do I go about processing a packet, and making sure the packet was received error free? I need to just discard incomplete packets or anything without a header.

1

A state machine is what you want. That and to increase your preamble to two bytes, not one. The reason being is that due to the escaping of the preamble character in the data it is impossible to have that character twice in a row. By then having it twice in a row as the preamble it is unambiguous that it is the preamble.

You should also (if needed) escape the packet size:

Serial.write(PACKET_HEADER); // Packet header for synchronization
Serial.write(PACKET_HEADER); // Packet header for synchronization
uint8_t ps = calculatePacketSize(data, XOR); // Size of packet
if(ps == ESCAPE || ps == PACKET_HEADER) {
    Serial.write(ESCAPE);
}
Serial.write(ps);
Serial.write(unitInfo);
... etc ...

You might like to abstract the escaping into a function:

void sendEscape(uint8_t c) {
    if (c == ESCAPE || c == PACKET_HEADER) Serial.write(ESCAPE);
    Serial.write(c);
}

Then you can just call:

sendEscape(calculatePacketSize(data, XOR));

etc.

Now for receiving. Implement a Finite State Machine. In that machine you have a number of states, such as look for first preamble, look for second preample, look for size, look for id, and look for data. As bytes arrive they trigger the next state.

So you start by looking for the first preamble byte. When that arrives it triggers looking for the second preamble byte - any other byte besides the second preamble byte causes the FSM to reset. If it arrives, then it triggers the reading of the packet length. And so on.

In pseudocode it may be something like:

State is idle.
If character available:
    Read character into variable C

    Select on state:
        State IDLE:
            If C is PACKET_HEADER:
                Set state to PREAMBLE
        State PREAMBLE:
            If C is PACKET_HEADER:
                Set state to GETSIZE
            Else
                Set state to IDLE
        State GETSIZE:
            If C is ESCAPE and you haven't just seen ESCAPE
                Remeber you had an escape 
            Else 
                Store C in variable SIZE
                Clear array DATA and prepare for reception
                Reset Seen ESCAPE flag if needed
                Set state to GETID
        State GETID:
            If C is ESCAPE and you haven't just seen ESCAPE
                Remeber you had an escape 
            Else
                Store C in variable ID
                Reset Seen ESCAPE flag if needed
                Set state to GETDATA
        State GETDATA:
            If C is ESCAPE and you haven't just seen ESCAPE
                Remeber you had an escape 
            Else
                Append C to array DATA
                Decrement variable SIZE
                Reset Seen ESCAPE flag if needed
                If SIZE is 0
                    If checksum matches
                        Process packet
                    Set state to IDLE

The main thing to watch is the remembering (and subsequently forgetting) that you have just seen an ESCAPE. That way you can know if you have two ESCAPE characters in a row and you only discard the first one - the second gets handled as a real character.

The beauty of an FSM is that at any point in the sequence, if you decide that the packet is invalid (maybe the size is too big, or the sensor ID is invalid) you can just reset the FSM to the IDLE state and reset any flags/counters and you instantly start looking for a new packet.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.