Transport Layer Description
Message Structure
The transport layer protocol is encapsulated by Start of Transmission (STX = 0x02) and End of Transmission (ETX = 0x03) characters.
All other characters at the transport layer will be printable ASCII characters in the range ‘0’-‘9’ and ‘A’-F’. This implies that the reception of STX or ETX has an unambiguous meaning – they always indicate the start or end of a message.
The protocol data and CRC data are encoded as hex strings. This means a single byte is sent as 2 characters in ASCII, in the order high nibble then low nibble. For example, the value 0x2A will be sent as ASCII chars ‘2’, ‘A’, which is equivalent to 0x32, 0x41 in hex.
Uppercase letters are used for the hex representations of A-F. If lowercase letters are received (a-f), they will be converted to uppercase A-F.
A CRC is computed over the protocol data. The CRC is computed over the data before encoding into the hex string form (i.e. compute the CRC before encoding into ASCII). The STX and ETX are not included in the CRC calculation. See the CRC section for more detail on the CRC.
The table below shows the message structure:
Start (STX) | Protocol Data | CRC1 High Nibble | CRC1 Low Nibble | CRC2 High Nibble | CRC2 Low Nibble | Stop (ETX) |
---|---|---|---|---|---|---|
0x02 | Protocol Data, as hex string, where each protocol byte is encoded to 2 characters for transport | 1 byte, '0'-'9', 'A'-'F' | 1 byte, '0'-'9', 'A'-'F' | 1 byte, '0'-'9', 'A'-'F' | 1 byte, '0'-'9', 'A'-'F' | 0x03 |
Only Protocol Data (before ASCII encoding) is included in CRC calculation |
To illustrate this, consider the Configuration Write Request:
- Application layer: the message is simply the operation code
0x05
followed by 3 bytes of parameters0x05 0x00 0x01
. The result is0x05 0x05 0x00 0x01
- Transport layer:
- Compute the CRC as 0x54C3 and append it (low to high byte order):
0x05 0x05 0x00 0x01 0xC3 0x54
- Encode as hex string:
0x30 0x35 0x30 0x35 0x30 0x30 0x30 0x31 0x43 0x33 0x35 0x34
- Add STX and ETX, which gives the result to be sent over the UART:
0x02 0x30 0x35 0x30 0x35 0x30 0x30 0x30 0x31 0x43 0x33 0x35 0x34 0x03
- Compute the CRC as 0x54C3 and append it (low to high byte order):
Hex String Examples
All data in the protocol is converted to hex strings for transport. This can be illustrated with some examples.
First, consider that the same data can have a variety of representations. We will work in the hex notation.
For example, the ASCII string Hello World!
can be represented as a hex array 0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64 0x21
. We will work with our data as bytes or collections of bytes with a value 0x00 to 0xFF.
This hex data is encoded as high and low nibbles. Take the first character H above, which is 0x48. This is encoded to 0x34 (‘4’) and 0x38 (‘8’), So our example array becomes:
H | e | l | l | o | ‘ ‘ | w | o | r | l | d | ! | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
48 | 65 | 6C | 6C | 6F | 20 | 57 | 6F | 72 | 6C | 64 | 21 | ||||||||||||
4 | 8 | 6 | 5 | 6 | C | 6 | C | 6 | F | 2 | 0 | 5 | 7 | 6 | F | 7 | 2 | 6 | C | 6 | 4 | 2 | 1 |
0x34 | 0x38 | 0x36 | 0x35 | 0x36 | 0x43 | 0x36 | 0x43 | 0x36 | 0x46 | 0x32 | 0x30 | 0x35 | 0x37 | 0x36 | 0x46 | 0x37 | 0x32 | 0x36 | 0x43 | 0x36 | 0x34 | 0x32 | 0x31 |
The last row of the table shows the bytes sent over the serial link.
Request / Answer
The application protocol uses a request/answer mechanism, where all requests are initiated by the asset.
The asset should wait for an answer to each request before sending another request.
Transport Layer Timing and Timeouts
Answers to requests can be expected to be received within 100 ms after reception of the last byte of the request. Past that delay, the asset can consider that a transmission error has occurred, and the asset should retry.
There are exceptions: configuration save request (CFG_SR), configuration factor reset (CFG_FR), performance counters clear (PER_CR) and context save (CTX_S) may take up to 1.5 sec to reply. Enqueue payload request (PLD_ER) may take up to 1.2 sec to reply.
A 100ms inter-byte timeout is implemented on the module to recover from incomplete requests. Within a single message, bytes should not be spaced by more than 100ms. With more than 100 ms between bytes, the module will discard the in-progress receive operation and wait for the next start byte.
CRC
The CRC bytes allow the integrity of the message to be checked.
The CRC is calculated over all bytes from the application layer before the hex string encoding. The Start (STX) and End (ETX) bytes are excluded from the CRC calculation.
The result is 2 bytes, which are appended to the application layer data as the low then the high byte. The application layer data with CRC should then be encoded to the hex string form.
A CRC-16-CCITT checksum is used, defined by the polynomial x16+x12+x5+1, or 0x1021. The syndrome is initialised to 0xFFFF at the start. This is used in protocols such as Bluetooth.
Examples of Implementation
// Computes the CRC-CCITT
// data The buffer containing the data bytes.
// dataLength The number of bytes to read from the buffer.
uint16_t crc_compute(const uint8_t* data, uint16_t data_length)
{
uint16_t x = 0;
uint16_t crc = 0xFFFF;
while (data_length--)
{
x = crc >> 8 ^ *data++;
x ^= x >> 4;
crc = (crc << 8) ^ (x << 12) ^ (x << 5) ^ (x);
}
return crc;
}
def crc16(data):
table = [
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
]
crc = 0xFFFF
for byte in data:
crc = (crc << 8) ^ table[(crc >> 8) ^ byte]
crc &= 0xFFFF # important, crc must stay 16bits all the way through
return crc
def generate_crc(data):
crc_tmp = '{:04X}'.format(crc16(data))
crc = crc_tmp[0]
crc += crc_tmp[1]
crc += crc_tmp[2]
crc += crc_tmp[3]
return crc
import crcmod
crc16 = crcmod.mkCrcFun(0x11021, rev=False, initCrc=0xffff, xorOut=0x0000)
def generate_crc(data):
crc_tmp = '{:04X}'.format(crc16(data))
crc = crc_tmp[0]
crc += crc_tmp[1]
crc += crc_tmp[2]
crc += crc_tmp[3]
return crc
Remember to invert the byte order when appending the CRC code to the application layer data.
Verification Examples
Data | CRC-16 CCITT | Inverted CRC | Encoded CRC (application layer) |
---|---|---|---|
00 00 | 1D 0F | 0F 1D | 0x30 0x46 0x31 0x44 |
00 00 00 | CC 9C | 9C CC | 0x39 0x43 0x43 0x43 |
AB CD EF 01 | 04 A2 | A2 04 | 0x41 0x32 0x30 0x34 |
14 56 F8 9A 00 01 | 7F D5 | D5 7F | 0x44 0x35 0x37 0x46 |