Computing stuff tied to the physical world

RF69 native on ATmega’s

The RF69 driver on GitHub is highly portable, due to the use of C++ templates to keep all platform-specific details nicely separate. All we need, to use this driver in the Arduino IDE for use with JeeNodes, is to implement an SPI class with the proper API (see spi.h):

template< int N>
class SpiDev {
  static uint8_t spiTransferByte (uint8_t out) {
    SPDR = out;
    while ((SPSR & (1<<SPIF)) == 0)
    return SPDR;

  static void master (int div) {
    digitalWrite(N, 1);
    pinMode(N, OUTPUT);

    pinMode(10, OUTPUT);
    pinMode(11, OUTPUT);
    pinMode(12, INPUT);
    pinMode(13, OUTPUT);

    SPCR = _BV(SPE) | _BV(MSTR);
    SPSR |= _BV(SPI2X);

  static uint8_t rwReg (uint8_t cmd, uint8_t val) {
    digitalWrite(N, 0);
    uint8_t in = spiTransferByte(val);
    digitalWrite(N, 1);
    return in;

typedef SpiDev<10> SpiDev10;

The template argument “N” is the pin we are going to use as master SPI select, i.e. 10.

Note that there is also an “SPI” class defined in the newer Arduino IDE releases, which you could use as basis for this SpiDev definition. It would make things more compatible if you intend to use multiple SPI devices – but the above definition will be fine for simple uses.

Using the new driver takes a little setup, since the Arduino IDE expects files to be in a specific place and the sketch directory to have a specific name, whereas this driver lives in the embello respository, which has a different layout (and is more general than the IDE). See the README on Github for how to set things up in your Arduino IDE environment.

The actual demo is very similar to the ones for the LPC8xx and the Raspberry Pi, both using this same RF69 driver. And not surprisingly, so is its output:

OK 80180801020304050607 (130+38:3)
OK 8018090102030405060708 (130+6:4)
 > #1, 1b
OK 80180A010203040506070809 (132+18:3)
OK 80180B0102030405060708090A (130+22:4)
 > #2, 2b
OK 80180C0102030405060708090A0B (128+6:4)
OK 80180D0102030405060708090A0B0C (130+20:4)

The numbers in parentheses are (-2*RSSI ± AFC : LNA).

The API of this new RF69 driver is slightly different from the RF12 driver API:

#include "spi.h"
#include "rf69.h"
RF69<SpiDev10> rf;
rf.init(28, 42, 8686); // node 28, group 42, 868.6 MHz
rf.send(0, txBuf, txLen);
int len = rf.receive(rxBuf, sizeof rxBuf);
if (len > 0) ...

Some notes (see rf69demo.ino for the complete example):

  • the spi.h header has to be included before the rf69.h header
  • you need to declare an RF69 instance to use this driver
  • initialisation takes a node ID (RF69 supports 1..60), group, and frequency
  • the frequency is specified in MHz, but you can add as many decimals as you want
  • sending is essentially the same as with the RF12 driver
  • the rf.receive() caller must supply the buffer to hold the incoming packet
  • the return value is the actual packet size, which may exceed the data stored in rxBuf if that buffer is too small to hold the entire packet
  • the first two bytes returned in rxBuf are a header byte and an origin byte – for a maximum-size packet, the buffer has to be at least 64 bytes

Several conventions have changed slightly with this new RF69 native driver:

  • more node ID’s, also: 61 is for send-only nodes, 62 is reserved, 63 is receive-all
  • the payload length can be 0..62 bytes (compared to max 66 for the RF12)
  • the header byte has the destination ID in bits 0..5 (or 0 if this was a broadcast)
  • the origin byte has some flags in bits 6..7, and the origin node ID in bits 0..5

Last but not least, there’s no need to frequently poll (as with rf12_recvDone) – you can wait to read out an incoming packet when your code is ready for it, the RFM69 will keep the entire packet in its FIFO buffer until that time (or until you re-use it for sending).

Encryption is easy with the new RF69 driver, just define a 1..16-char encryption key:


Note that encryption will be applied to all nodes in the same group, since the receiver cannot change its encryption mode for packets coming from different node IDs. If you need to disable encryption again, call “rf.encrypt(0)“.

To reduce the transmit power (0 is lowest, 31 is highest and the default), use this:

rf.txPower(15); // -3 dBm

This can be useful to limit power consumption and for close-range communication.

So there you have it: a simple new RF69 driver which can be used on all RFM69-based JeeNodes and JeeLinks to run these wireless radio modules in native packet mode.

[Back to article index]