This article provides an in-depth explanation of the communication protocol used between the Scrutiny server and a Scrutiny device. This protocol is not something a user needs to understand; instead, this information is intended to serve as developer documentation for those who wish to modify the software or debug their communication.
The communication between the server and the device is done through a custom binary protocol. The protocol is half-duplex and works in a request/response scheme. It draws inspiration from the UDS protocol used in the automotive industry defined by ISO-14229.
All fields are encoded in big-endian format and have a fixed size, with the exception of addresses, which depends on the device architecture. A CommControl-GetParams request is required to know the address size, but also the protocol version used and the rx/tx buffer sizes. If a timeout occurs during data reception, the internal state machine resets and waits for a new command ID. Requests that have an invalid CRC32 are silently ignored.
Before a server can send a request to a device, it must first establish a session with the device using a "Connect" request. If no connection is established, the device will only answer to these 2 requests CommControl-Discover and to CommControl-Connect. All other requests will be silently ignored.
Once a session is established, periodic CommControl-Heartbeat Requests must be sent to keep the session active. The server uses the heartbeat mechanism to detect if a device is disconnected and the device uses them to detect that a server is gone.
Request
- Direction: Request or Response. 0 for request
- Command ID: A 7 bit ID indicating the action that we want to perform
- Subfunction: A subcommand ID that specify the command
- Data Length: Number of bytes in the data field. Maximum value: 65520 bytes
- CRC32: A CRC code that validates the frame integrity
Response
- Direction: Request or Response. 1 for response
- Command ID: An echo of the request Command ID
- Subfunction: An echo of the request Subfunction
-
Code: A response code indicating the success or the reason of a failure
- OK (0): Request completed successfully
- InvalidRequest (1): Request was not properly crafted
- UnsupportedFeature (2): Request ask for a feature that the device does not know of
- Overflow (3): The response does not fit in the transmit buffer
- Busy (4): Cannot process this request in the current state
- FailureToProceed (5): Default failure message if anything prevents the device from completing the request
- Data Length: Number of bytes in the data field. Maximum value: 65520 bytes
- CRC32: A CRC code that validates the frame integrity
CRC
The CRC algorithm used is a standard CRC32. A C++ implementation would look like this
uint32_t crc32(const uint8_t *data, const uint32_t size, const uint32_t start_value){
uint32_t crc = ~start_value;
for (uint32_t i = 0; i < size; i++){
uint8_t byte = data[i];
for (unsigned int j = 0; j < 8; j++){
const unsigned int lsb = (byte ^ crc) & 1;
crc >>= 1;
if (lsb){
crc ^= 0xEDB88320;
}
byte >>= 1;
}
}
return ~crc;
}
Data types
On multiple occasion, data types are communicated. Each datatype has a 1 byte ID where the high part represent the type type (int, float, uint) and the low part represent the size
Type | ID |
---|---|
sint8 | 0x00 |
sint16 | 0x01 |
sint32 | 0x02 |
sint64 | 0x03 |
uint8 | 0x10 |
uint16 | 0x11 |
uint32 | 0x12 |
uint64 | 0x13 |
float32 | 0x22 |
float64 | 0x23 |
boolean | 0x30 |