Overview
The protocol is based on TCP and is based around commands targeting object IDs that work similar to OIDs in SNMP. Messages are protected against transfer failures by means of a 2-byte CRC checksum. The protocol in itself isn't very complicated, but there are some things like escaping, length calculation and plant communication to look out for.
There is no method to tell that a communication was not understood at the protocol level, meaning that it is up to the implementor to figure out a way to let the client know that something was not understood. Vendor devices may simply fail to respond or respond with an empty payload, or do something else. It is advised to pay attention to the checksums and errors during payload decoding. Vendor devices do facilitate special OIDs that contain information about their state, but that is an implementation detail of the specific devices. If implementing an application, one could specify some OIDs that can be used to report if the previous command resulted in an error, for example.
Protocol Data Units
The Protocol Data Units (PDU) are comprised of:
A start token (
+
)The command byte
The length of the ID and data payload
For plant communication, the address, omitted for standard frames
The object ID
Payload (optional)
CRC16 checksum
Data is encoded as big endian, a leading 0x00
before the start token is allowed.
Element |
Bytes |
Remarks |
---|---|---|
Start token |
1 |
|
Command |
1 |
|
Payload length |
1 |
All but long read / long write |
2 |
Long read / long write |
|
Address |
4 |
For plant communication |
0 |
Omitted for standard frames |
|
Object ID |
4 |
|
Payload |
N |
Payload is optional for some commands |
CRC16 |
2 |
Hint
The OIDs are actually an implementation detail of the device. The protocol only defines that they are 4 bytes in length. All other details like the data type, whether a payload can be used and so on are up to the implementer, so in order to implement your own application, you would simply define your own OIDs with associated data types instead of using the ones in the Registry or the examples.
Escaping
Certain characters are escaped by inserting the escape token -
(0x2d
) into the stream before the byte that
requires escaping. When the start token (+
) or escape token is encountered in the data stream (unless it's the very
first byte for the start token), the escape token is inserted before the token that it escapes. On decoding, when the
escape token is encountered, the next character is interpreted as data and not as start token or escape token. Thus, if
the task is to encode a plus sign (usually in a string), then a minus is added before the plus sign to escape it.
Checksum
The checksum algorithm used is a special version of CRC16 using a CCITT polynom (0x1021
). It varies from other
implementation by appending a NULL byte to the input if its length is uneven before commencing with the calculation.
Commands
There are two groups of commands: Standard communication commands that are sent to a device and the device replies,
as well as Plant communication commands, which are standard commands ORed with 0x40
.
Commands not listed here are either not known or are reserved, and should not be used with the devices as it is not known what effect this could have.
Command |
Value |
Description |
---|---|---|
READ |
|
Request the current value of an object ID. No payload. |
WRITE |
|
Write the payload to the object ID. |
LONG_WRITE |
|
When writing "long" payloads. |
RESERVED |
|
|
RESPONSE |
|
Normal response to a read or write command. |
LONG_RESPONSE |
|
Response with a "long" payload. |
RESERVED |
|
|
READ_PERIODICALLY |
|
Request automatic, periodic sending of an OIDs value. |
Reserved |
|
|
PLANT_READ |
|
READ for plant communication. |
PLANT_WRITE |
|
WRITE for plant communication. |
PLANT_LONG_WRITE |
|
LONG_WRITE for plant communication. |
RESERVED |
|
|
PLANT_RESPONSE |
|
RESPONSE for plant communication. |
PLANT_LONG_RESPONSE |
|
LONG_RESPONSE for plant communication. |
RESERVED |
|
|
PLANT_READ_PERIODICALLY |
|
READ_PERIODICALLY for plant communication. |
EXTENSION |
|
Unknown, see below. |
The EXTENSION command
EXTENSION does not follow the semantics of other commands and cannot be parsed by rctclient. It is believed to be a
single-byte payload; a frame often observed is 0x2b3ce1
, which is sent by the official app uppon connecting to a
device to "switch to COM protocol". In this case, 0xe1
is the commands payload, and a normal frame follows
immediately after, which leads to the conclusion of this command always being three bytes in length.
READ_PERIODICALLY
Registers a OID for being sent periodically. The device will send the current value of the OID at an interval defined
in pas.period
(see Registry). Up to 64 OIDs can be registered with vendor devices, but the protocol does not
impose a limit, and all registered OIDs will be served at the same interval setting. When sending this command, the
device immediately responds with the current value of the OID, and will then periodically send the current value.
To disable, set pas.period
to 0, which clears the list of registered OIDs, effectively disabling the feature. No
method exists for removing a single OID, one has to clear it, then set pas.period
and re-register all desired OIDs.
Warning
The implementation has not been tested yet, please don't hesitate to open an issue if you run into problems or have more insight into the matter.
Frame length
The frame length is 1 byte for all commands except LONG_RESPONSE and LONG_WRITE and their PLANT_ counterparts,
which use 2 bytes (most siginificant byte first). The length denotes how many bytes of data follow it. Escape tokens
are not counted, and it does not include the two-byte header before it (start token and command) and does also not
include the two-byte CRC16 at the end of the frame. In order to fully receive a frame, after reversing any escaping,
the buffer should therefor hold 2 + length + 2
bytes.
Plant communication
With plant communication, one device acts as plant leader and relays commands addressed at subordinate devices to them
and their responses back to the client. Vendor devices need to be linked together, each has its own address
.
To use plant communication, use the PLANT_*
variations of the normal commands (READ
→ PLANT_READ
and so on)
and supply the address
property. The leader device forwards the command to the device that has the address set, all
other devices ignore the frame. The answer from the addressed device is then sent back to the client by the leader,
with a PLANT_*
response command and the address
set to that of the addressed device.
Warning
Plant communication has not been tested and the implementation simply follows what is known. The authors do not have a setup to test this kind of communication. We'd greatly appreciate traffic dumps of actual plant communication or feedback if it works or not.
Frame by example
The following is a dissection of a frame sent to the device (read request) and its response from the device.
Request
Setting:
READ request, so command is
0x01
The OID
battery.soc
is0x959930BF
No payload and no address and nothing to escape.
Data: 2b 01 04 959930bf 0d65
ID: 1 2 3 4 5
ID |
Bytes |
Description |
---|---|---|
1 |
|
Start token |
2 |
|
Command: READ |
3 |
|
Length of the data that follows, it's the OID of 4 bytes. |
4 |
|
Data, which in this example consists of the OID only. |
5 |
|
CRC16 checksum. |
Response
The response for the command (read battery state of charge) is disected below. The string has been split up for ease of reading, but it is a single byte stream.
The raw response looks like this (in hexadecimal): 002b0508959930bf3e97b1919c86
Data: 00 2b 05 08 959930bf 3e97b191 9c86
ID: 1 2 3 4 5 6 7
ID |
Bytes |
Description |
---|---|---|
1 |
|
Data before the start of the command. It is ignored. |
2 |
|
Start token, all data before this is ignored. |
3 |
|
Command, this is a RESPONSE. |
4 |
|
Length field, 4 byte OID and 4 byte payload. |
5 |
|
The OID this response carries. |
6 |
|
Payload data, as per the OID this is a big endian float value of roughly 0.296 |
7 |
|
CRC16 checksum. |
The payload in this example is a big endian floating point number. The data type can be looked up in the Registry.