This review aims to list and discuss a number of inspiring solutions for how to interface 8-pins nRF24L01+ wireless module with low-end(-cost) 8-pins microcontrollers (MCU). Typical MCU popular among hobbyists is the 8-bit AVR family with the limited peripheral set sub-family called ATtiny, specifically 8-pins 25/45/85 series (t25, t45, t85), ATtiny13A (t13a), ATtiny102, ATtiny 0-series 202/402, ATtiny 1-series 212/412.
There are several reasons why one would bother with such a combination of nRF24 and a low pin-count MCU:
- Resource utilization and (production) cost reduction. Can we provide the same functionality but with a cheaper MCU? It makes economic sense.
- Global chip shortage that makes many mid- or high-end MCUs prohibitively expensive or unavailable.
- What hobbyists and makers love to do - to have great fun while "hacking" and learning something new.
The marriage of low-end 8-pins MCU with nRF24L01 has both hardware and software issues. The hardware challenge is shown in the following figure:
RF module nRF24 has six data signals that have to be connected to MCU:
- Regular 4-wire SPI interface (MOSI, MISO, CSN, SCK).
- Chip Enable (CE) signal that activates radio to receive or transmit mode.
- Maskable interrupt (IRQ) signal to indicate data received, data sent, and reaching maximum retransmission number.
Thus, no pins are left to connect with sensors or other devices. In reality, we have available only 5 data pins because one of the data pins (e.g. PB5) is by default used as RESET/dWIRE for programming and debugging. Configuring pin PB5 as GPIO would later require high-voltage programming (firstly applying 12V to reset fuses) which is a cumbersome solution and not feasible for designs with In-Circuit Serial Programming (ICSP). Therefore, we have to minimize the number of pins on MCU required for communication and control with the nRF24 module. This has also implications for the software driver.
Driver for nRF24 has to be constructed to reflect "pin optimization" and it shall have as lowest as possible memory footprint to fit into MCU's memory. Furthermore, there should be left enough room for the application logic and drivers of attached sensors. This represents an interesting software challenge for small-memory types, such as t13a (1KiB flash + 64B SRAM). On the other hand, t85 has 8KiB flash that is fairly large to safely accommodate a driver.
Driver for Arduino/Raspberry LinuxProbably the most favorite and widely used SW driver. It was created for Arduino (ATmega328) and mini-computer Raspberry Pi. Default hardware configuration assumes that all nRF24's interface pins except for IRQ are directly connected to MCU. Memory footprint on Arduino is several kilobytes in flash and a few hundreds of bytes in SRAM. For ATtiny sub-family, there exists an optimized fork: https://nrf24.github.io/RF24/md_docs_attiny.html
The fork requires SpenceKonde ATTinyCore board manager to be installed in the Arduino IDE. It supports 8-pins MCU (ATtiny 25/45/85), 14-pins (ATtiny 24/44/84), and 20-pins (ATtiny4313). The 20-pins ATtiny2313 is not supported due to insufficient memory (2KiB flash + 128B SRAM). The following hardware configurations on 8-pins MCUs are supported by the fork:
The first option is the same as the default - all signals are connected except for IRQ. The second configuration uses Nerd Ralph's 3-pins solution (below we review the improved 2-pins solution). The 3-wire solution requires the CE pin on nRF24 to be permanently pulled high. As long as the module is operating in the receiver mode, it works identically as it would with CE connected to MCU. Nevertheless, it brings a certain energy inefficiency in the transmission mode. Let's have a look at the state machine (datasheet Sec. 6.1.1):
If the transmission is over and there is no other payload to be sent, the module goes either to StandBy-1 (if CE==0) or StandBy-2 (if CE==1) mode. Because we have CE permanently pulled high, the module stays in the StandBy-2 state which consumes 320uA. It is nearly 15 times more than in the StandBy-1. For comparison, that is the same energy as consuming ATtiny13A on 3.3V @ 1MHz in full active mode and 100% load, or 45uA while idling, or less than 5us when MCU is sleeping. Because IRQ signal is not connected, MCU has to regularly pool nRF24 state (requiring both MISO and MOSI signals) to detect end-of-the transmission and to power off the module (clearing PWR_UP bit in the Configuration register). If CE signal would be controlled with MCU, it should go high for only 10us to change the state from StandBy-1 to TX mode. Once the transmission (including all re-transmissions, timeouts, etc.) is over, the module would immediately switch back to the StandBy-1 mode which consumes only 22uA (instead of 320uA) before we power off the module. Nevertheless, such energy inefficiency in TX mode might be for many IoT and sensor applications insignificant.
Advantages:
- 3-pins HW configuration leaves 2 (or 3 if RST disabled) pins on MCU free.
- SW driver is based on probably the most popular and well-known library.
Disadvantages:
- Memory footprint is considerably large and it cannot fit all 8-pins MCUs, e.g. t13a. Furthermore, it depends on Arduino's core library that further consumes precious bytes from memory segments.
- Default configuration occupies all MCU's pins. For sensors, field busses and application leaves only RST pin that can be reprogrammed to serve as an IO line. This has some limitations (see section Challenge ) for practical usage.
- Unlike the default HW configuration, the 3-pins solution in the transmission mode does not fully utilize the energy conservation offered by the nRF24 module. Thus, it might not be the best fit for some ultra-low-power applications running on batteries or with energy harvesting.
- Need for additional passive components in case of the 3-pins solution.
http://nerdralph.blogspot.com/2015/05/nrf24l01-control-with-2-mcu-pins-using.html
This is a further evolution of the 3-pins solution described above. It merges two signals (MISO and MOSI) into a single pin with an additional resistor. Technically speaking, this "merge" resembles the so-called 3-wire SPI:
- IRQ signal is not used (SW polling)
- CE is permanently pulled high.
- SCK and CSN on the module side are "merged" via a capacitor and a resistor into a single pin on the MCU side.
- MISO and MOSI are merged via a resistor into a single MOMI line.
The proposal is validated on ATtiny85 (8KiB flash) with a driver specifically tailored for ATtiny84 (14-pins, 8KiB MCU).
Advantage:
- Using only 2-pins on MCU, thus leaving 3 (or 4 if RST is not used) for the (sensor) application.
Disadvantages:
- CE signal permanently pulled high prevents fully exploiting energy optimization features of nRF24 while in the transmission mode. This might not be a problem for many applications.
- Need for additional passive components (two resistors and capacitor).
- SW driver has to be updated to support bi-directional transfers via a single IO pin. This should not be a challenge for a skilled hobbyist.
- Used SW driver relies on Universal Serial Interface (USI) peripheral which is not available in all ATtiny series, e.g. t13a.
https://github.com/Alx2000y/tiny13_sensors/tree/master/nrf24l01
This solution does not reduce the number of required pins to interface nRF24. However, it exploits nRF24's state machine to share CE signal pin with either DHT22 or DS18B20 sensor:
- IRQ signal is not used (SW polling).
- 4-wire SPI: MOSI, MISO, SCK, CSN connected to PB0, PB1, PB2, PB4.
- CE on PB3 is also a data pin for sensors with single-wire protocols.
According to nRF24 documentation, CE activates the radio part of the nRF24 if two conditions are met: (1) nRF24 is in StandBy-1 state (activated by SW via SPI commands) and (2) when it is held high for more than 10us. In case the nRF24 is in the PowerDown state, the module does not react on CE. Thus, this signal can be utilized to communicate with single-wire protocol sensors while the radio is off. After the communication with sensors is over, SW brings the nRF24 from PowerDown to StandBy-1 and by setting CE=high into TX or RX mode. The CE pin is held high until the transmission is over. It seems that holding CE for such a long period does not interfere with sensors and used single-wire protocols.
As SW driver is used a fairly stripped-down version of the Mirf library that allows only transmission. This is for a vast majority of sensor applications sufficient.
Advantage:
- Connecting all nRF24 pins to MCU (except for the IRQ), thus reducing passive components and potentially simplifying SW driver. Greater control of the module power consumption.
Disadvantages:
- Leaves only one pin usable for attaching a sensor or a field bus.
- The solution is not generic. It can be used only with devices/field busses where holding CE high for milliseconds does not create instability. It works well for selected sensors.
In case we have a simple device that only sends measurements and does not receive data, do we really need MISO signal for reading data from the nRF24 module? Actually, we do not!
To transmit data is a relatively trivial algorithm that requires only to write configuration and to write data into the buffer. In theory, we do not need to read the status of the device (we set it into a well-known state) and its RX buffers (we clear them). After triggering the transmission (10us pulse on CE pin), we wait for the maximum time needed for retransmission to either make a new transmission or power off the module (clearing PWR_UP in the Config register). Because we do not know exactly when the transmission is over and we assume the worst-case scenario (max timeout with max re-transmissions), it might appear as a wasting of energy. However, if the CE pin is low (after 10us pulse), the module will automatically enter StandBy-1 mode after the transmission is completed or the max timeout is reached. In this state, the consumption is 22uA compared to 320uA with CE permanently high (Nerd Ralp's solutions). Do not confuse this solution with the 3-wire SPI that is bi-directional.
Advantages:
- Saves one data pin without a need for additional passive components as in the case of 3-wire SPI.
- The potentially smaller footprint of the SW driver because many features are not used.
Disadvantages:
- Applicable only for "data source" devices that do not need to read the status of the module and do not receive data.
- Generic SW driver that relies on a bi-directional SPI interface has to be adapted.
No winner here and the classic "there is no free lunch." Which proposal to use depends on the application and its deployment. Each solution has pros and cons and compromises some aspects while improving others. For instance, if we are very strict with the energy consumption, then leaving CE permanently high (Nerd Ralph's 3(-2)-pins solution) is not the ideal solution although we save many pins. If we are concerned about the footprint, the Arduino/RPi library might not be the best choice but offers the greatest programming comfort. In case we need more signals out of MCU and attached devices will not correctly handle the Shared CE signal, then we better seek another option. However, a combination of these ideas might be a feasible solution too. For example, uni-directional SPI with shared/pulled high CE for a simple only-transmitting device is worth a try.
If you are aware of some interesting technical solution (HW/SW) that is not covered in the review, leave a comment and I will update it.
Comments