A Simple Serial Protocol

We can send and receive commands wirelessly with Arduino by using these cheap RF data link transmitters/receivers. I like these RF modules because they can be hooked up to pretty much any device that supports serial communications (e.g. devices equipped with either hardware or software UART).

One of the headaches many people face is that these simple RF transceivers do not guarantee the integrity of the messages and thus at the receiving end messages often appear garbled. This is especially problematic in situations where environmental interference is strong.

To make sending and receiving messages more robust, we need a protocol that can help us detect corrupted messages. In this post, I will introduce a simple protocol that can be used to ensure the correctness of the received messages. The Arduino library along with some example code can be downloaded towards the end.

I call this protocol simple because it does not define the message format, nor does it define the length of each message. In fact, the protocol itself does not have any error correction capability, it is up to the end user program to decide how error correction should be handled.

In this approach, each message is defined by a “frame” that starts and ends with predefined symbols. For instance:

            ^.......

By default, a message begins with a carrot symbol ^. The content of the message then follows. And at the end of the message transmission, a line feed (chr(10)) is appended, indicating the end of the message. The message begin and message end symbol can also be redefined via the overloaded constructor.

The library exposes a function pointer (CmdReceivedPtr) and upon receiving a complete message (e.g. content between ^ and ), CmdReceivedPtr is called and the client program can do whatever it deems necessary to interpret the message.

Here is an example of sending and receiving three letters ‘A’, ‘B’ and ‘C’. The sender side of the code looks like this:

#include <SimpleSerialProtocol.h>

SimpleSerialProtocol p;
byte b[]={65, 66, 67, 198}; // last byte is the check sum (65+66+67) % 256

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    p.send(b, 4); //sending A B C
}

To ensure the correctness of the message, a very crude CRC method is used by summing all the message bytes together and taking the remainder after dividing by 256. The result is an 8 bit CRC code.

Here is the code on the receiver side:

#include <SimpleSerialProtocol.h>

SimpleSerialProtocol p;

void setup()
{
    Serial.begin(9600);
    p.CmdReceivedPtr = CmdReceived;
}

void loop()
{
    p.receive();
}

void CmdReceived(byte* cmd, byte cmdLength)
{
    if (cmdLength > 0 && cmdLength < SimpleSerialProtocol::BUF_MAX_LENGTH) {
        int sum = 0;
        for (int i = 0; i < cmdLength - 1; i++) sum +=cmd[i];
        
        if ((sum & 0xFF) == cmd[cmdLength - 1]) { //test if the CRC matches
            Serial.print(char(cmd[0])); //A
            Serial.print(char(cmd[1])); //B
            Serial.print(char(cmd[2])); //C
        }
    }
}

CmdReceived is automatically called when a message frame is detected. Both the command buffer and the detected command length are provided. Given the CRC algorithm we used on the transmitter side, we can calculate the CRC of the received data inside the command buffer and process the command only after the CRC has been verified. In this case, I simply printed out the received message.

Note that the protocol itself does not define a message length or the error correction mechanism, and thus is extremely light weight, giving end user the full control of defining these messages based on his or her need. Since the communication channel is assumed to be unreliable, the detection of message begin and message end symbols does not automatically guarantee the actual begin and end of a message. But if the message length is not correctly detected the likelihood of a CRC match is very slim as the CRC byte being compared to is not the actual CRC byte calculated on the transmitter side. Of course, if the message length is known (in this case it is fixed at 4), we can always discard messages that do not have the correct length. Anyway, this example is just a simple illustration. In practice, multi-byte CRC codes are usually used to improve messaging reliability.

The maximum buffer size is set to 64 byte, which is the UART buffer size in Arduino implementation. When used with a 32 bit CRC, up to 60 bytes of data can be sent and received reliably at once.

Downloads

SimpleSerialProtocol.tar.gz

Be Sociable, Share!

Leave a Reply