SerialProtocol is a software protocol for sending structured data via serial between two Arduino, two Raspberry Pi, or an Arduino and a Raspberry Pi.
Sending data through serial is trivial both on Arduino and Raspberry Pi. On Arduino, you can use the
Serial class and on Raspberry Pi you can use, termios or an other library. The problem is how to send structured data that can be understood by both the sender and receiver.
For the purpose of this blog post I am going to use the following data structure:
It represents a message with an identifier and a value corresponding to that message. You can imagine this as being the data sent by sensors from Arduino. For example, the message with ID
100 is the value sent by a temperature sensor and the message with ID
101 is from an accelerometer.
Data through the serial port is sent sequential. That means that you get a series of bytes at the other end of the wire and you have to be able to give them a meaning. Also, the data must be intact - meaning no bytes were lost or altered.
The message starts with a
Header byte. This is just a marker that signals the start of the message and prevents the program from doing more work if the data received is protocol data.
Following the header, is another byte that specifies the
Size of the message. This byte has dual purpose: (1) to tell how many bytes to read after it and (2) it is the first defense against incorrect data. If the size of the message does not match the expected size, everything read so far is discarded.
Payload is the actual data.
The last byte, the
Checksum, is used to verify the integrity of the message. Its only purpose is to validate the bytes received so far. If the checksum is incorrect, the message read so far is discarded. The checksum is composed of the
Size field and the
The structure above (
MyMessage) is two bytes in size (one byte for each
uint8_t field). Let’s say that the temperature sensor (MessageId = 100) sends the value 42. When this message is sent, the
Size field has the value 2. After that, the two fields are sent and, finally, the checksum. The checksum is computed by applying the
XOR operator on all the message bytes and the size. In our case
2 XOR 100 XOR 42 = 76.
After the message is received, and before it is passed to the user code, it is validated against the received checksum. The receiver computes the checksum from the bytes it got and it looks to see if it matches the checksum in the message.
In order to use SerialProtocol in your applications, you have to include the appropriate header and compile the code along with your application.
The headers are:
StreamSerialProtocolfor Raspberry Pi
Complete installation instructions and samples, along with the complete source code are available here.
The hardware setup for running the samples is simple: connect the Arduino using a USB cable to Raspberry PI. No other components are needed.
When sending data between two architecturally difference devices, you must make sure that both encode/decode the data in the same way. The following structure is 3 bytes on Arduino but 4 on Raspberry Pi (with the default compiler optimization and structure padding). This is because the
A field is padded with an extra byte.
If you try to send the structure, as is, from Arduino to Raspberry Pi, it will fail because the sent
Size field will be 3 while the expected
Size field, on Raspberry Pi, will be 4. In order to prevent structure padding, decorate the structure with
__attribute__((packed)) as shown in these samples.
The full source code, samples, and installation instructions are available on GitHub.