The protocol
The protocol is designed for synchronous communications in a master->slave network. The slave will only send data when requested by the master. This allows the protocol to be used on a half-duplex bus like RS485. Some devices may also provide facilities to allow asynchronous communication.
Message Format
Each message consists of a header, optional payload, and checksum. The binary format is specified as follows:
Byte | Type | Name | Description |
---|---|---|---|
0 | u8 | start1 | Start frame identifier, ASCII 'B' |
1 | u8 | start2 | Start frame identifier, ASCII 'R' |
2-3 | u16 | payload_length | Number of bytes in payload. |
4-5 | u16 | message_id | The message id. |
6 | u8 | src_device_id | The device ID of the device sending the message. |
7 | u8 | dst_device_id | The device ID of the intended recipient of the message. |
8-n | u8[] | payload | The message payload. |
(n+1)-(n+2) | u16 | checksum | The message checksum. The checksum is calculated as the sum of all the non-checksum bytes in the message. |
Scheme
Messages are divided into 4 categories:
- general: messages that are used for general purpose signalling and communication.
- read/get: messages that are sent from the device in response to a
general_request
from the host. These messages are designed to read data from the device. - write/set: messages that are sent from the host to configure some parameters on the device. These messages are designed to write data to the device.
- control: messages that are sent from the host to command the device to perform some action. These messages are designed to perform more complex device interactions than atomic read/write.
There are a some messages that are implemented by all devices, referred to as the 'common' message set. Message ids # 0~999 are reserved for the common messages. The request message is a special message in the common set that is used to request the device to respond with a message from the get category. Each device must also define it's own message set specific to the operation of the particular device.
Message Definitions
Device Discovery
If necessary, Ping Protocol enabled devices may be discovered and identified by the host as follows:
- The host first requests the protocol version from the device
- After receiving a protocol version reply, the host switches to the matching protocol version and requests the device type and firmware version
- The host then loads the appropriate message set matching the device
- Communication may then continue using the device-specific messages
Negotiation example
Here we demonstrate a byte-by-byte breakdown of some messages sent between the host application (master) and the device (slave). This example illustrates a few points:
- how to pack and unpack some message data (the byte-order of a message)
- how the request/response mechanism of the protcol works with the general_request
message
- how to identify the protocol version that the device is using
Establishing communication with a sensor using the ping-protocol should begin with negotiating the protocol version. This negotiation process consists of two steps:
- the host application requests a protocol_version
message from the device
- the device responds with a protocol_version
message
Before reading these examples, you should be familiar with the message format specification.
Request protocol version
In order to receive a protocol_version
message from the device, we will first send a general_request
message to the device to ask for the message. The general_request
message has a single payload field, requested_id
. We populate this field with a value of 5 to indicate that we want the sensor to respond with a protocol_version
message:
Byte | Value (hex) | Value (decimal) | Type | Name | Description |
---|---|---|---|---|---|
0 | 0x42 ('B') | 66 | u8 | start1 | This is a message start identifier, an ascii letter 'B' |
1 | 0x52 ('R') | 82 | u8 | start2 | This is a message start identifier, an ascii letter 'R' |
2-3 | 0x0002 | 2 | u16 | payload_length | The number of bytes in the general_request payload |
4-5 | 0x0006 | 6 | u16 | message_id | This message is a general_request (id pingmessage-common#6) message |
6 | 0x00 | 0 | u8 | src_device_id | The device ID of the device sending the message. This field is not currently implemented and should be populated with a value of zero |
7 | 0x00 | 0 | u8 | dst_device_id | The device ID of the intended recipient of the message. This portion is not currently implemented and should be populated with a value of zero |
8-9 | 0x0005 | 5 | u16 | requested_id | This is the message id that we would like the device to transmit to us. Valid ids are those in the get category of messages |
10-11 | 0x00a1 | 161 | u16 | checksum | The message checksum. The checksum is calculated as the sum of all the non-checksum bytes in the message: 66 + 82 + 2 + 6 + 0 + 0 + 5 = 161 |
The bytes should be transmitted in this order:
0x42, 0x52, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0xa1, 0x00
Note that the messages are transmitted in little-endian format (observe the byte-order of the 16 bit fields)
Receive protocol version
If everything is right, the sensor will respond to our request with a protocol_version
message. The protocol_version
message has 4 bytes in the payload: version_major
, version_minor
, version_patch
, and reserved
.
In this example, the device is using (a hypothetical) protocol version 1.2.3: - version_major = 1 - version_minor = 2 - version_patch = 3
Byte | Value (hex) | Value (decimal) | Type | Name | Description |
---|---|---|---|---|---|
0 | 0x42 ('B') | 66 | u8 | start1 | This is a message start identifier, an ascii letter 'B' |
1 | 0x52 ('R') | 82 | u8 | start2 | This is a message start identifier, an ascii letter 'R' |
2-3 | 0x0004 | 4 | u16 | payload_length | The number of bytes in the protocol_version payload |
4-5 | 0x0005 | 5 | u16 | message_id | This message is a protocol_version (id #5) message |
6 | 0x00 | 0 | u8 | src_device_id | The device ID of the device sending the message. This field is not currently implemented and should be populated with a value of zero |
7 | 0x00 | 0 | u8 | dst_device_id | The device ID of the intended recipient of the message. This field is not currently implemented and should be populated with a value of zero |
8 | 0x01 | 1 | u8 | version_major | This is the protocol major version, the first digit in our example: v1.2.3 |
9 | 0x02 | 2 | u8 | version_minor | This is the protocol minor version, the second digit in our example: v1.2.3 |
10 | 0x03 | 3 | u8 | version_patch | This is the protocol patch version, the third digit in our example: v1.2.3 |
11 | 0x00 | 0 | u8 | reserved | This byte is unused and will normally be zero (but it might be any value) |
12-13 | 0x00a3 | 163 | u16 | checksum | The message checksum. The checksum is calculated as the sum of all the non-checksum bytes in the message: 66 + 82 + 4 + 5 + 0 + 0 + 1 + 2 + 3 + 0 = 163 |
The bytes will be received in this order:
0x42, 0x52, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x00, 0xa3, 0x00
Note that the messages are transmitted in little-endian format (observe the byte-order of the 16 bit fields)