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 do not use NRF ACK mechanism.
We use nrf24l01 buffers only. Send packages instantly on receive via uart. 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. MAC address is being assigned to a device during installation sequence.
Every device consists of a number of units. Every unit represents one isolated functional, like one sensor, one actuator, etc. Units always number from 0 consecutively without any gaps. Units contain readable-writable nodes and functions to read or write data.
Unit 0 always corresponds to the controller itself and contains slave info and rf control functions.
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
Every unit should have predefined by a protocol nodes enlisted in a table below. Node numbers 0 .. 0x0F and function numbers 0x80 .. 0x8F are standard and required.
Custom device nodes to control the hardware should start from 0x10. Custom device functions to control the hardware should start from 0x90.
Every data abstraction has 1-byte id from a common mapping namespace (unique per unit). If MSB of id is 0, then it is node, if it is 1, then it is a method.
1{transaction id}{unit id}{function id}{data}
1 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.
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.
2{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 |
|
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 |
|
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 |
Request: (only for units 1+) 0 --- get unit mapping id and unit functions list pages count other --- get unit functions list page+1 Response: Unit 0: Unit 1+, page 0: Unit 1+, pages 1+, per function: for node: for method: |
0x00 ok 0xA0 bad unit id 0xE2 wrong page number |
No limit for functions number per unit Unit mapping id is unique for every mapping. Units with identical mapping id should have identical functions list. |
2 |
Text description |
RW |
Response: {utf8 text} |
0x00 ok 0x08 bad unit id |
User description of the unit. |
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 |
Incorrect package version field |
|
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