interrupt/automode driven library for the RFM69

Status
Not open for further replies.

iwanders

Well-known member
For a hobby project I switched from NRF24 radio's to the RFM69 series. After reading the datasheet of the RFM69CW modules and looking at the various libraries out there, I decided to create a library myself.

The libraries I came across were all blocking and used sleeps/while statements, for example to check whether a packet had been sent before switching back to receiving mode. The radio module has a system to automatically change it's mode based on specific events called the automode system, I wanted to use this mechanism in an attempt to use the hardware as efficiently as possible. Additionally, I desired a library which only sends bytes, without introducing overhead or acknowledge systems.

The library can be found at https://github.com/iwanders/plainRFM69 (MIT License). It consists of two parts; bareRFM69, which only exposes the hardware's registers (with a lot of constants and explanation in the header file). The plainRFM69 class builds on top of bareRFM69 to provide convenient methods to send data and receive it. It also provides internal buffering for received packets. For more explanation about the inner workings I recommend reading the readme. Near the bottom of that page are results from my tests about the achieved performance. Examples in Arduino style are provided and the code is extensively commented.

I've been using the library continuously for a few weeks now, so far I'm happy with the results. I hope someone finds it useful.
 
Kudos to you for this work... a good learning experience.

The RadioHead stack includes the RFM69 with a reliable datagram protocol (ACKs, retries), unreliable datagrams (no ACKs), mesh networking, etc.
It is normally used in non-blocking mode by either calling in a loop (polling) things like received message available or transmission complete. It can also be used with the Arduino/teensyduino "YIELD" macro, where the RadioHead protocols call YIELD if there's a busy loop due to the user program making a blocking call. The user program may elect to redefine YIELD as suits. This can be in interrupt context so as to not need polling.

Several types of radios (FSK, GFSK, On-off-keying) are supported - via C++ classes.

http://www.airspayce.com/mikem/arduino/RadioHead/
written in C/C++ and builds for Teensy 3 and any Arduino compatible AVR board, as well as some other ARM Cortex M4 boards.
It's freeware for non-commerical use.
 
Last edited:
You are correct, this was an excellent learning experience!

In regard to RadioHead; I'm well aware of it, I even recommended it in the usage section of the readme, as it has seen quite extensive use and testing. However, I found it to be somewhat over the top in the case you just want to send messages and don't want to use an entire framework to do so. Truth be told, I did not know it had support for the yield mechanism you describe.

Additionally, the way it uses the hardware is different. For example when transmitting messages, RadioHead writes to the FIFO and then forces the radio to TX mode. If the radio is forced to TX mode and the FIFO is empty it outputs the preamble unless the start transmission condition is met (p54), this will be the case for some time between the end of the packet transmission and the MCU setting the radio to RX or standby mode. This is where the automode system comes in to prevent this from happening, the netto difference will be very small of course, but if the option is there - why not use it? :)

So yes, I knew about RadioHead. But - as said - I just wanted to transfer bytes and attempted to do this efficiently.
 
Yeah, I asked Mike to use YIELD.

I don't see a problem with the FIFO use. For the RF69, RH simply fills the FIFO and transmits. The interrupt is transmission complete, not FIFO half or all-empty. On other radio types, RH repeatedly refills the FIFO to allow for unlimited size messages. For the RFM69, Mike chose to not do this, because the AES encryption cannot span multiple FIFO refills. (I don't use AES in the radio).

It looked like your code does not provide reliable datagrams (MAC layer ACKs, ACK timeouts, and retransmissions) as does on of the protocols in RadioHead. The usual way this is handled is to push those error correction duties to the application program. But it's very nice to have the ACKs and retransmissions done by the library. The unreliable datagram option in RH would be used, like UDP rather than TCP.
 
I don't see a problem with the FIFO use. For the RF69, RH simply fills the FIFO and transmits. The interrupt is transmission complete, not FIFO half or all-empty.
It's not really a problem, it's just a different way of making the radio module work. With the automode system it is even possible to leave the interrupt at the end of transmission out if you wish; you can configure it such that the radio goes to sleep right after the PacketSent state is reached, without the MCU handling an interrupt to switch the mode. I think that's quite elegant, which is the reason I wanted to use that mechanism.

It looked like your code does not provide reliable datagrams (MAC layer ACKs, ACK timeouts, and retransmissions) as does on of the protocols in RadioHead. [...] But it's very nice to have the ACKs and retransmissions done by the library. [...]
That is a correct observation, my code does not. Having acks and retransmission can be nice, but that depends on the intended use. If I were to create a retry/ack system, I would make it independent of the hardware. So indeed higher up the network stack to allow for hardware abstraction, just like RadioHead does.

For the RFM69, Mike chose to not do this, because the AES encryption cannot span multiple FIFO refills. (I don't use AES in the radio).
I checked the cipher mode used in the radio module, (I couldn't find it in the datasheet), the AES cipher mode turned out to be ECB unfortunately (check_crypto_mode.py). Also, as required with a block cipher, data is zero padded to multiples of the blocksize (16 bytes) so you lose the performance benefit of transmitting short messages, are these things the reason you do not use the hardware AES? Do you perform encryption in software on the Teensy? If so, I'd love to hear which library and cipher you selected.
 
AES... yes, in the past, with small-frame data radios, I used AES128/CCB with block truncation - at the application level. That way, there are no fill bytes and this is key in small frame radios.
I've done no encryption on Teensy for small frame radios. But what I've done as above with CCB and truncation was from the public domain in C.
I also did integral mutual authentication - using a header's nonce and an 8 byte suffix as a MIC (message integrity check). This allowed each node to have a unique key rather than a shared key. In some applications, you want to avoid shared key as a compromised key affects every node in the shared key approach. And the non-shared key approach used over the air re-keying with a special re-keying key and cypher techique.
But few hobbyists / students need such rigor.

The code speed in C was just fine for the small frames.

Hardware AES - I've not seen CCB/trunction - but with effort, one could likely do so with the ARM chips' encryption capability. Really though, hardware isn't needed for these kinds of radios.

On no using ACKs and retransmissions...
Wireless is, in my opinion, too error prone, mostly due to collisions, to not use ACKs with reliable datagrams. For broadcasts, there's no ACK or NACK, so one must be careful what sort of messages use broadcast. And too, broadcasts don't propagate across routers in most mesh networks, and is an option in a static-route network, both of which RadioHead supports.
 
I also did integral mutual authentication - using a header's nonce and an 8 byte suffix as a MIC (message integrity check). This allowed each node to have a unique key rather than a shared key. In some applications, you want to avoid shared key as a compromised key affects every node in the shared key approach. [...]
But few hobbyists / students need such rigor.
Interesting, thanks for sharing that. I agree that few hobbyists really require this, but it's still interesting to read about and work with. I toyed around with mutual authentication, but I used a shared key for that in the past. Ultimately I scrapped that part back then, as the code had become too convoluted. I'm just sending some values for LED lighting and notifications; I hardly require security, but I do want it :).

I looked at using CTR mode, such that the hardware AES can still be used. But this has the problem that a lot of overhead is introduced in every block in this mode. In the future when I revisit this, I'll most likely not use the radio's AES implementation. (I'll probably end up using something like CBC-MAC, combined with something to prevent replay attacks...)

On no using ACKs and retransmissions...
Wireless is, in my opinion, too error prone, mostly due to collisions, to not use ACKs with reliable datagrams. [...] both of which RadioHead supports.
I had a project in the past, which indeed had the collisions occurring and causing problems. Currently I do not really have these problems as I have I have very - very sparse communication between just two radios. I might take another look at RadioHead in the future, you seem to be quite content with it.
 
Doing encryption at the application level brings the huge benefit of enabling end-to-end security and authentication. Many systems have an end device (sensor+MCU+radio) that communicates wirelessly in a star or mesh. Then there is often an egress node from wireless to either a computer or a bridge to a WAN (say, the Internet). With application layer encryption, the decryption is done only at the final destination. None of the devices and networks between the end points have the key or means to decrypt.

To decrypt at a network element then reencrypt is just not secure. Your trust has to go to that decrypting device and often they are in remote insecure places, often outdoors. Or packets flow over a LAN/WAN and you have no control over that.

The concept above is akin to SSL and other layer 3 security methods in IP networks. You should not trust the network(s) per se.
 
Status
Not open for further replies.
Back
Top