Thursday, 5 September 2013

RF24 with ATtiny84

An ATtiny84 and nrf24l01+ transceiver
The nrf24l01+ is an amazing little wireless transceiver which is (a) incredibly cheap (how about 10 for €8?) (b) can be put into an extremely low-power mode under software when not transmitting and (c) is controlled over SPI (available on Arduino). Furthermore Arduino has support for them via the Mirf library. Unfortunately this library isn't super-easy to use (although I've seen worse).

Luckily for us, someone called Maniacbug has produced an excellent pair of libraries (RF24 and RF24Network) which simplify use of these devices considerably, and in a manner consistent with the Arduino way. The latter library makes building meshed wireless networks very easy indeed. (If you're thinking XBee at a twentieth-the-cost, you're way ahead of me.) An RF24Network article describes the design and implementation of an impressively low-cost, whole-building, wireless sensor network scalable to thousands of sensors.

While reading about this sensor network, a question arose: why was it restricted to ATmegas? Surely an ATtiny would be equally up to the job? It turns out that I wasn't the first to think about this. One source of problems was the ambiguous nature of the SPI interface on ATtiny chips.

Although these chips have pins named after SPI functions (SCK, MISO, MOSI) they are primarily for programming the chip over SPI, not for use in the SPI master role, to control other devices. However USI (for Universal Serial Interface), which can be used to implement SPI, is provided. In principle, therefore, it should be possible to talk to an nrf24l01 radio from an ATtiny, and it turns out by forking the Mirf library, it has been done.

While USI-SPI is demonstrated only for ATtiny85, mutatis mutandis it is pretty easy to make work on an ATtiny84 as well. (Blindly mapping pins by name works pretty well.) A bigger problem is that RF24 relies on the built-in Arduino SPI library, which only works on ATmegas. This library could be a poster-child for the library-dependency problem --- i.e., how to replace an underlying dependency of a library cleanly. In short, with the current Arduino library handling semantics, it's impossible: only one approach works and that's to edit it. (One small consolation is that it's not necessary to edit it in place, the IDE will search ~/sketchbook/libraries before its built-in libraries.)

My cross-platform SPI library is here, (credit is due to Nick Gammon, SPI85 and JeeLib's RF12 driver). It supports ATtiny-x4 and -x5 as well as ATmegas.

The SPI library uses the pins marked DI/DO/USCK in each of the ATtiny pinouts linked here. (Note that DI/DO are not the same as MISO and MOSI!) For the ATtiny84, SS is mapped to PA7 (pin 6), while on the '85 it's mapped to PB3 (pin 2).

Armed with this library, and the ATtiny development environment described yesterday, it was a simple matter to compile the helloworld_tx example from RF24Network. And, while it compiled fine, it failed to link because the resulting binary was too large for the ATtiny84's 8kB code space. An inspection of the library code revealed extensive use of printf_P, which eats up a lot of flash space on an ATtiny. A quick hack with the C preprocessor and the code size was down to 5700 bytes, leaving a couple of kB for user sketches.

When connecting a radio, DO connects to MOSI, DI to MISO and USCK to SCK. CE and CSN can be set to anything reasonable (check cores/tiny/pins_arduino.c for the Arduino pin mappings).

Here are my forked RF24 and RF24Network libraries.


  1. Hi, whanted to thank you for that post! Would you mind posting the pin connections to ATtiny85/84? I've tryed guessing from the code but something is not working for me. Same code works on Mega328.

    Thanks again!

    1. Good idea. I've added some explanation of the SPI mappings and connecting to a radio. Hope it helps!

  2. Hello. Before anything, thank you for the sharing of your work.
    I would like to know one thing, since you went to Jee Labs for some inspiration and base work, did you consider having encryption in your fork of the RF24, with xxtea like Jee Labs has on is, for the RF he uses?
    Thank you.

    1. I hadn't noticed that feature, it's interesting! It might even be possible to add it in the ATtiny's remaining space. Not sure if it would leave much room for the actual sketch though!

      To be clear though: all I did with RF24 was blindly reduce its footprint for ATtiny. I didn't think about changing it in any other way. It would probably be better to ask Maniacbug to add it to his upstream codebase? (More room on the ATmega anyway.)

  3. Hi Steve..

    I'm the blogger for and I'm glad to find your blog...

    I had made these attiny84 PCB and was trying to get the RF24 to compile on the attiny84... luckily I found your SPI forks for attiny from a mentioned on the Arduino forum...

    I'm able to upload the codes to the attiny84 but my Arduino as a hub codes was not receiving anything from the attiny84/nRF ...

    Without all those print_details statements, how do I know if the radio is working ?? How do you do the debugging ?

    My Tiny nRF PCBs :

    1. Hi Stanley,
      As far as I can remember now (it's been about a year since the original investigation) I had a pretty hard time getting it all working initially.

      I ended up with two ATmegas running Maniacbug's code (with prints) trying to talk to each other (and failing) until I put decoupling capacitors across the radios' power pins. Once this was working, I kept one ATmega and moved the other to ATtiny.

      Hope this helps,

      BTW your RF24 code for Raspberry Pi was very useful to me later on: thanks!

  4. Hi there Steve.

    Just one question, I downloaded your libraries for the RF24, but if I try to get, for example, the pingpair example on the attiny84, first I get errors because of the Serial communication, I get by those making some modifications in the code, but after that, the example is too big to fit in the attiny84.
    My question, how do you tested communication or if the RF is operational?

    I'm thinking on making my one little simple sketch, but if something is already done, I don't think I should invent the wheel again.

    Thank you.

    1. Hi Helder,
      As you've discovered, there isn't enough room for Serial comms and RF24 on the ATtiny. You can have one or the other!

      My recommendation is that you test your sketches on regular Arduinos and when they're working, strip out the Serial debugging and compile them for ATtiny.


    2. :-) That was what I was doing right this moment. I was commenting all serial call and library import.

      Thank you very much Steve.

  5. Hello Steve,

    Very good work, the best I've seen about Attiny85 and NRF24l01. Is it possible to use your libraries with this modification too?

    It would be great as it leaves more pins for other purposes.

    thanks in advance

    1. Hey that's cute: I hadn't seen it before!

      This looks like it would be fine if power consumption was no object, however for battery operation:

      CE is permanently tied high (so it's probably impossible to tell the radio to sleep)
      When SCK goes low the capacitor charges through the 1k resistor (and this happens every clock cycle)
      The LED will dissipate power too (could just drop it if powering the Tiny from 3v3)

      Try it out, measure the power consumption and let us know!

    2. I will let you know as soon as I have it working. I am using the RF24Network library and it seems impossible to be able to fit any sketch in 8kb. Any ideas? I can't strip it down more.

    3. You need the three libraries mentioned above: SPI, RF24 and RF24Network, all available from my GitHub, here

  6. Hi Steve,

    I downloaded the SPI, RF24 and RF24network libraries from your page. I also replaced the code of RF24_config.h from your quick hack code But I am getting the following error:

    \Arduino\Arduino-1.0.5\Arduino\libraries\RF24Networkmaster\RF24Network.cpp: In member function 'void RF24Network::begin(uint8_t, uint16_t)':

    \Arduino\Arduino-1.0.5\Arduino\libraries\RF24Networkmaster\RF24Network.cpp:51: error: 'class RF24' has no member named 'printDetails'

    Can you please help

    1. Hi Usama,
      Whoops! My bad: I was in the middle of cleaning up the RF24 library by separating out the debugging into a separate class and I should have been doing this on a separate branch.

      I haven't looked at the RF24Network library yet (stay tuned). In the meantime, you have several choices:
      - add a dummy void printDetails() method to the RF24 class
      - comment out the call to printDetails() from RF24Network.cpp
      - grab the RF24 library version before all these changes (it has SHA f06cb9a27c418988cc0161a2df91ddec82135204 on github)

      The first two of these options are the easiest but only the last is guaranteed to work. I hope to look at RF24Network over the weekend...

    2. In the interests of backwards-compatibility, I have added back this API to RF24. Right now it does nothing, see the pingpair sketch as an example of how to enable debugging now.