
Written primarly for communication between Arduino and PC but, I believe, can be used in any applications (if suitable, see Key feautures below)
Key features
- Byte stuffing
- Header only
- Max length of a (raw) message = 126 bytes
- Stream message decoding (byte by byte) possible
Description & terminology
Suppose we have n bytes of data a₀, a₁, a₂, ..., aₙ₋₁ to send from one device to another over a serial communication channel: i.e. one byte (even bit) at a time. It means we need to synchronize the sender and the receiver, so the latter knows, f.e., when the message starts and when it ends. To do this we will first encode the message into a frame b₀, b₁, b₂, ..., bₘ₋₂, bₘ₋₁ in such a way that a frame always starts with the same byte b₀ (HDR) and ends with the same (different in our case from the start byte) byte bₘ₋₁ (FTR). But then we need to handle properly the start and end bytes if they appear in the data itself - in other words we have to escape those bytes. This is pretty much all the code does - it allows you to encode and decode data. See Fig.1 below and the article in the Acknowledgements section for details.
Fig.1
Definitions
- data : the raw message we want to send
- special bytes : metabytes, serve special roles in the encoding process
- frame : the encoded message starting with HDR, ending with FTR, with all special bytes escaped. The actual message to send
Internals
The whole functionality is placed into the proto::Bicoder class template. One can say that a good practice is to divide the encoder and decoder, but the class is quite small too store everything inside. This is where the name comes from.
This class has a buffer in which it puts the encoded/decoded message.
- Warning
- The buffer is shared between the decoder and encoder: it is overwritten each time a new operation has been started. So be sure you've done with the result of the last operation before you start a new one.
Encoding
The encoding process is quite straightforward. The algorithm is shown in Fig.2:
Fig.2
Note how a special byte is encoded:
Fig.3
i.e. a special byte aₛ is encoded with two bytes:
- Note
- The HDR and FTR characters are actually printable ASCII '{' and '}' respectively. So if your data has no special bytes you can debug your channel without any additional software: just put the frames by hand. I do this for my project: I send this to my Arduino board right from the serial monitor:
Decoding
The decoder part of the proto::Bicoder class is a finite state machine with the following diagram:
Fig.4
- Note
- The decoding of a special byte is quite simple: if the ESC byte is found in the encoded message then the next byte is a special byte xor'ed with the XOR byte. So in order to extract the original byte we need to xor the encoded byte again with the XOR byte.
Fig.5
Example
#include <cassert>
#include "DataLinkSerialProtocol.h"
bool compare( const uint8_t* buff1, uint8_t size1,
const uint8_t* buff2, uint8_t size2 )
{
if( size1 != size2 )
return false;
for( uint8_t i = 0; i < size1; ++i )
{
if( buff1[i] != buff2[i] )
return false;
}
return true;
}
int main ()
{
constexpr uint8_t HDR = ESpecial::eHDR;
constexpr uint8_t FTR = ESpecial::eFTR;
constexpr uint8_t ESC = ESpecial::eESC;
constexpr uint8_t XOR = ESpecial::eXOR;
constexpr uint8_t data[] = { HDR, ESC };
constexpr uint8_t frame[] = {
HDR,
ESC, HDR ^ XOR,
ESC, ESC ^ XOR,
FTR
};
assert(compare(bicoder.
buff(), bicoder.
size(),
frame, sizeof(frame)));
for(uint8_t i = 0; i < sizeof(frame); ++i)
{
{
assert(compare(bicoder.
buff(), bicoder.
size(),
data, sizeof(data)));
break;
}
}
}
Definition DataLinkSerialProtocol.h:14
Definition DataLinkSerialProtocol.h:76
uint8_t size() const
Definition DataLinkSerialProtocol.h:172
void reset()
Definition DataLinkSerialProtocol.h:220
bool isCompleted() const
Definition DataLinkSerialProtocol.h:164
bool decodeByte(const uint8_t data)
Definition DataLinkSerialProtocol.h:228
bool encodeMessage(const uint8_t *data, uint8_t size)
Definition DataLinkSerialProtocol.h:266
const uint8_t * buff() const
Definition DataLinkSerialProtocol.h:179
Documentation
Available here: https://lrdprdx.github.io/DataLinkSerialProtocol/index.html
Acknowledgements
This protocol is based on this article : https://eli.thegreenplace.net/2009/08/12/framing-in-serial-communications/
TODO