Since NRF24 protocol does not resolve packet collision, we won’t implement that ourselves, but will implement a pulling mechanism of transmission. Thus, we do not have a requirement that all the devices have to hear each other.
We use nrf24l01 buffers only. Send packages instantly on receive via uart. Received packages are being sent back to uart instantly as well. Packages with modem mac address considered command requests and are being parsed generating package-response. Received packages are being stored in the internal buffer (up to 8) and shall be read by a command.
Every client device has to have a unique MAC address. We could either pre-program them during flashing firmware, or obtain them dynamically on every installation.
Every device consists of a number of units. Every unit represents one isolated functional, like one sensor, one actuator, etc. It contains channels that could be read, write or both. Units always number from 0 consecutively without any gaps. Units contain functions to read or write data.
Unit 0 always corresponds to the controller itself and contains slave info and rf control functions.
Code |
Name |
Description |
Channels |
Sensors |
|||
0x00 |
Custom |
||
0x01 |
Switch |
Binary output, could be manual switch, pirometer movement sensor and so on |
0x00 readable |
0x02 |
Analog input |
One analog input channel (TODO) |
|
Actuators |
|||
0x40 |
Custom |
||
0x41 |
Relay |
One binary channel |
0x10 writable |
0x42 |
Dimmed output |
||
0x43 |
RGB output |
In RF write channel packet number of channels limited to 64 (6 bits) given data type field is 2 bit long.
Diapason |
Permissions |
|
0x00 --- 0x0F |
Read only |
|
0x10 --- 0x1F |
Write only |
|
0x20 --- 0x2F |
Read and write |
Integer data types are divided by 7-bit bytes. MSB of every byte is 1 if it is not the last byte in a sequence and a 0 if it is the last one. Bytes are going from least significant (little endian). Negative numbers are represented only by sign (MSB of data). Higher 0 bits may be omitted.
In RF write channel packet number of channels limited to 64 (6 bits) given data type field is 2 bit long.
Code |
Name |
0 |
Bit |
1 |
Unsigned int (7-bit packing) |
2 |
Signed int (7-bit packing, sign is MSB, value is positive always, most significant zeros may be omitted) |
3 |
Array (first byte is length, rest is data) |
Device enters installation mode on button press. At this point it constantly sends (every 500ms) device info packet to its address (from eeprom) — todo: determine single address. In this state it still replies to all the commands. After that, the concentrator decides which address should be given to it and sends the package “change MAC address”, “change RF channel” and finally “change mode to normal slave”.
Modem address: 00:00:00:00:01 A5:42:42:42:XX
Broadcast address for endpoint devices: 00:00:00:00:00
Let’s try modified modbus TCP protocol for our network since we have only 32 byte maximum package size.
Functions from 0 to 0x0F are predefined by protocol and are standard and required. Custom device functions to control the hardware should start from 0x10.
TBD: For the custom functions the least significant bit is data direction: 0 for read functions, 1 for write functions.
Every data abstraction has 1-byte id from a common namespace (unique per unit). If MSB of id is 0, then it is node, if it is 1, then it is a method.
0{transaction id}{unit id}{function id}{data}
0 is protocol version
Transaction id, 1 byte, for synchronization between messages of server and client. Slave does not keep track of it.
Unit id, 1 byte, id of unit behind that transceiver, basically enhansion of the address
Function id, 1 byte, data abstraction id, see the table below
Data, up to 28 bytes payload, arguments to a function. Functions with variable length of the data will specify the format of that length.
Besides the NRF ACK, a slave unit shall send a response to a master for every request. If a request can not be parsed, the slave shall send an error response to the master. List of error responses is below.
0{transaction id}{code}{response data}
Transaction id should be equal to transaction id of the request regardless of whether it is valid or not.
Response code, see table Slave error responses below
Response data is for code 0 (up to 29 bytes)
For the advertisement packet it is a response to a function GetProperties of unit 0 with transaction id 0xAA.
Code |
Description |
Access |
Data format _________________________________ |
Error codes |
Comment |
Unit 0 custom functions |
|||||
0x10 |
Session key |
W |
Request: {cipher type}{key} Length is total length of the data Cipher type is 1 byte (TODO) Key is 16 bytes long array (AES-128) or 24 bytes long (AES-192) |
Should be encrypted by current session key unless it is “setup” mode, which is being activated only once during install process |
|
0x11 |
Get number of internal units (function 0 for unit 0) |
R |
Response: {number} uint8_t {uint32} build number |
Units are consequent from 0, no gaps allowed |
|
0x12 |
MAC address |
W |
Request: {address} Address is 5 bytes long array |
0x00 ok 0x01 bad address length |
|
0x16 |
RF channel |
W |
Request: {RF channel} 0 --- 127 |
0x00 ok 0x80 bad channel |
Applied only after the response |
0x13 |
Device statistics |
R |
Response: All fields are uint16_t, LSB first (little-endian)
{requests}{responses}{errors} {transaction errors}{ack t/o}{validation errors}
Requests --- total requests received Responses --- all responses with return code less that 0x80 Errors --- total responses with return code > 0x80 Transaction errors --- separate counter of transaction errors Ack t/o --- count of responses with timeouted ack from master Validation errors --- any other packet validation errors |
Master should regularly check on statistics of all slave units to be aware of any troubles with sending packets. There are: lost response packets (without ack), sent packets (with ack), received packets, other error counters |
|
0x90 |
NOP |
0x00 |
Returns code OK, nothing else |
||
0x91 |
Reset transaction ID |
||||
0x17 |
Slave mode |
W |
Request: 1 --- advertisement mode 2 --- normal slave mode Response: none |
In advertisement mode slave constantly sends adv packet (every 500ms), but still replies to all the commands |
|
0x20 |
Get build version |
R |
Response: Uint32 build number (date) |
Not implemented yet |
|
Standard unit-related functions, for all units |
|||||
0 |
Get number of units and serial number (for unit 0) Get list of unit functions (for other units) |
R |
Response: Unit 0:
Unit 1+, per function: for node: for method: |
0x00 ok 0xA0 bad unit id |
Up to 12 functions per unit |
2 |
Text description |
RW |
Response: {utf8 text} |
0x00 ok 0x08 bad unit id |
There are two kinds of status codes: critical error, when the message was not passed to a given function, and when the function was run. First group error codes have MSB set. Second group errors depend on the function called.
Name |
Code (hex) |
Description |
Response data |
None |
0 |
No error, normal response |
|
BadVersion |
0x90 |
Current version is 0 |
|
BadUnitId |
0xA0 |
No such unit |
|
NotConsecutiveTransactionId |
0xB0 |
Probably, some packets from master was not received |
Current transaction id, next valid should be one above |
SameTransactionId |
31 |
Probably ACK to master was lost |
|
BadFunctionId |
0xC0 |
There is no such a function in a given unit |
|
ResponseTooBig |
0xD0 |
Requested data can not fit into packet |
|
BadRequestData |
0xE0 |
General request data parsing error |
For example if packet length is too short |
NodePermissionViolation |
0xE1 |
Reading unreadable or writing unwritable |
|
BadArguments |
0xE2 |
Argument validation failed, i.e. incorrect argument length |
|
Function not implemented |
0x7F |
Requested function id is legal, but not implemented |
|
Internal error | 0xFF | Some bug happened |
Data type |
Code |
Length |
none |
0 |
0 |
bool |
1 |
1 |
byte |
2 |
1 |
int32 |
3 |
4 |
string |
4 |
|
byte array |
5 |
|
unspecified |
0xF |
Has 3 modes, idle by default:
rmIdle — do nothing, just put received packets (data and ack) into a buffer, also ack timeout is possible as it is a hardware feature.
rmSlave — always in a listen mode, process received packets using RF protocol module and responses back.
rmMaster — utilizes timeout feature. For every sent packet it listens to the same address and expects the response in a specified period of time, otherwise it puts eptResponseTimeout packet into a buffer. It should stop listening to the timeouted address.
Add new comment