Last time I discussed the rationale for building a custom control and telemetry solution. Here I’ll describe the protocol design a little bit, before discussing the implementation in a future post.
Frame design and frequency hopping
The basic idea is that the transmitter sends a frame to the receiver every 20ms, and each frame is sent on a different radio frequency. A set of frequencies and their order is generated pseudo-randomly based on a “key” that the transmitter and receiver each share ahead of time. The receiver replies on the same frequency with its telemetry. Then the transmitter and receiver each switch to the next frequency in the list to get ready for the next frame.
Frequency selection uses a similar scheme as to DSMX. The “key” is a 32 bit transmitter ID. From it a simple pseudo-random number generator is used to generate candidate channels from 125 of the nrf24l01+’s available frequencies. Channels are discarded if they repeat and they are also discarded to maintain roughly equal distribution across the available frequencies of the radio. That is accomplished by grouping the frequencies into 4 separate “bands”, and enforcing a limit of how many frequencies can be chosen from each band.
The end result is that given an ID, the transmitter and receiver agree on ordered set of 23 frequencies to cycle between which do not repeat and cover most of the available spectrum.
Transmitter and receiver implementation
The transmitters job in this scheme is relatively straightforward. It transmits one frame, waits a millisecond to see if a reply comes in, then switches frequencies and waits until the next 20ms boundary to send the next frame.
The receiver is slightly more complicated. During an initial “synchronization” phase, it sits on one frequency for 20 periods waiting to receive a message. If no message is received, then it advances to the next channel. This ensures that even if one channel is completely unusable, eventually the transmitter and receiver will connect.
Once the receiver receives a packet and replies, it then enters the “locked” mode. In this state, after each reception, the channel is changed awaiting the next frame. If a frame is 10ms overdue, then it is assumed lost and the channel is switched anyway. If some number of frames are dropped in a row, then the receiver re-enters the “synchronization” phase.
Mapping onto the nrf24l01+
The following parameters are used with the nrf24l01+:
- 5 byte address field (the address is derived from the ID)
- dynamic payload length
- 2 byte CRC
- no automatic retransmission
- auto acknowledgment
- data rate is configurable
Replies are handled using the nrf24l01+’s “Enhanced Shockburst” “auto acknowledgment” feature. This ensures that the receiver replies to the transmitter within a few microseconds of receiving the transmitter’s packet, relieving the microcontroller of that timing requirement.
“slot” data scheduling
The final piece of this is how to package up the data. I decided on letting each of the transmitter and receiver define up to 15 slots. Each slot contains up to 15 bytes of data and can be configured, via a bitmask termed “priority”, to be sent in every frame, every other frame, or some more interesting division. On the wire, the 32 byte RF packet is filled with slots based on the current frame. Each slot gets a 1 byte header, with 4 bits denoting which slot is next, and 4 bits denoting the size. The host is responsible for selecting the bitmasks and sizes such that no frame is over-allocated.
So, if the transmitter has 4 slots that look like:
|0 (data denoted as “A”)||8||
|1 (data denoted as “B”)||4||
|2 (data denoted as “C”)||5||
|3 (data denoted as “D”)||6||
Then it would incorporate those slots into frames as follows. Each frame is written in hex, with ABCD corresponding to the data from slot 0123 respectively:
Frame 0: 08 AAAAAAAAAAAAAAAA 25 CCCCCCCCCC Frame 1: 08 AAAAAAAAAAAAAAAA 14 BBBBBBBB Frame 2: 08 AAAAAAAAAAAAAAAA 25 CCCCCCCCCC Frame 3: 08 AAAAAAAAAAAAAAAA 14 BBBBBBBB 36 DDDDDDDDDDDD
It is up to the transmitter and receive to agree upon how data is allocated to slots and the semantics of data inside each slot. For the quad A1 application, that is hard-coded ahead of time.
Comparison and references
Aside from bidirectional data, the RF portion is pretty similar to DSMX with only a few differences. First, DSMX uses a Cypress CYRF6936, where I’m using a slightly more available nrf24l01+. Second, the frequency selection is the same in theory, but has slightly different parameters in implementation. Third, bidirectional transmission is achieved using a single RF IC and amplifier.
The slot data scheduler is unique, as DSMX only allows one type of data to be transmitted in the control direction – “transmitter channels”.
Next I’ll demonstrate my implementation by itself before any integration!