Computing stuff tied to the physical world

Radio blips from an LPC810

It may sound like a bit of a stretch: using an 8-DIP ┬ÁC to drive an RFM69 wireless radio?

After all, we need two pins for power and at least 4 pins to drive the radio module. That leaves only two free I/O pins. But keep in mind that two pins is all you need to hook up to all sorts of I2C devices, such as I/O expanders, ADC converters, temperature / humidity, even entire 9-DOF sensors which can report acceleration, rotation, and magnetic heading.

Here’s a tiny hookup, just to see if it can be done – all held together with soldered wires:

DSC 4892  Version 2

That “big” round black plastic is a battery holder for a common CR2032 coin cell.

Sooo, let’s see if we can make this thing send out a test packet once a second, eh?

We’re going to need a “driver” for the RFM69, which knows all about how to set it up, how to put it into transmit mode, and how to get data packets into the radio’s buffer. There is a fresh RF69 driver in the “embello” repository on GitHub which does just that.

The RF69 class is defined as follows:

template< typename SPI >
class RF69 {
public:
    void init (uint8_t id, uint8_t group, int freq);
    void encrypt (const char* key);
    void txPower (uint8_t level);

    int receive (void* ptr, int len);
    void send (uint8_t header, const void* ptr, int len);
    void sleep ();

    int16_t afc;
    uint8_t rssi;
    [...]
};

This uses “templates”, an advanced mechanism in C++ to “specialise” the RF69 class for a specific way of talking to the SPI hardware in this case. Most of the gory details are hidden, but this means we have to define an instance of the RF69 class as follows:

    RF69<SpiDevice> rf;

With “SpiDevice” being another (low-level) class which implements the interfacing to the actual SPI hardware present in the LPC810. The benefit of using templates over other layering mechanisms such as passing in details at run time or defining virtual methods, is that the resulting code is considerably more efficient because the compiler can optimise the code a lot more. As a result, the RF69 driver is super compact – great with 4 KB flash!

Here is the main code, also available in full from GitHub:

int main () {
    LPC_SWM->PINENABLE0 |= (3<<2) | (1<<6); // disable SWCLK/SWDIO and RESET

    // NSS=1, SCK=0, MISO=2, MOSI=5
    LPC_SWM->PINASSIGN3 = 0x00FFFFFF;   // sck  -    -    -
    LPC_SWM->PINASSIGN4 = 0xFF010205;   // -    nss  miso mosi

    sleepSetup();

    rf.init(1, 42, 8683);
    rf.encrypt("mysecret");
    rf.txPower(0); // minimal

    int cnt = 0;
    while (true) {
        rf.send(0, &++cnt, sizeof cnt); // send out one packet
        rf.sleep();
        sleep(1000);                    // power down for 1 second
    }
}

Most of this should be fairly self-explanatory, but here are some extra notes:

  • rf.init(1, 42, 8683) – sets node ID 1, net group 42, frequency 868.3 MHz
  • rf.encrypt("mysecret") – turn on the RFM69’s 128-bit AES encryption
  • rf.txPower(0) – lowest transmit power level (and lowest current consumption)

And that’s all there is to it, really. The packet we’re sending out contains 4 bytes with an incrementing counter value – an int is 32 bits, we’re in the ARM world now, remember?

All we need to do is upload this firmware, which is less than 1 KB, plug the LPC810 into the above circuit, and insert a coin cell. It will keep going for many months.

But evidently this is all useless until we also have a way to receive that data. Stay tuned…

[Back to article index]