Arduino Projects

How nRF24L01+ Wireless Module Works & Interface with Arduino

Having two or more Arduino boards be able to communicate with each other wirelessly over a distance opens lots of possibilities like remotely monitoring sensor data, controlling robots, home automation and the list goes on. And when it comes down to having inexpensive yet reliable 2-way RF solutions, no one does a better job than nRF24L01+ transceiver module from Nordic Semiconductor.

nRF24L01+ (plus) transceiver module can often be obtained online for less than two dollars, making it one of the most inexpensive data communication options that you can get. And best of all, these modules are super tiny, allowing you to incorporate a wireless interface into almost any project.

Hardware Overview

Radio Frequency

The nRF24L01+ transceiver module is designed to operate in 2.4 GHz worldwide ISM frequency band and uses GFSK modulation for data transmission. The data transfer rate can be one of 250kbps, 1Mbps and 2Mbps.

What is 2.4 GHz ISM band?

2.4 GHz band is one of the Industrial, Scientific, and Medical (ISM) bands reserved internationally for the use of unlicensed low-powered devices. Examples are Cordless phones, Bluetooth devices, near field communication (NFC) devices, and wireless computer networks (WiFi) all use the ISM frequencies.

Power consumption

The operating voltage of the module is from 1.9 to 3.6V, but the good news is that the logic pins are 5-volt tolerant, so we can easily connect it to an Arduino or any 5V logic microcontroller without using any logic level converter.

The module supports programmable output power viz. 0 dBm, -6 dBm, -12 dBm or -18 dBm and consumes unbelievably around 12 mA during transmission at 0 dBm, which is even lower than a single LED. And best of all, it consumes 26 µA in standby mode and 900 nA at power down mode. That’s why they’re the go-to wireless device for low-power applications.

SPI Interface

The nRF24L01+ transceiver module communicates over a 4-pin Serial Peripheral Interface (SPI) with a maximum data rate of 10Mbps. All the parameters such as frequency channel (125 selectable channels), output power (0 dBm, -6 dBm, -12 dBm or -18 dBm), and data rate (250kbps, 1Mbps, or 2Mbps) can be configured through SPI interface.

The SPI bus uses a concept of a Master and Slave, in most common applications our Arduino is the Master and the nRF24L01+ transceiver module is the Slave. Unlike the I2C bus the number of slaves on the SPI bus is limited, on the Arduino Uno you can use a maximum of two SPI slaves i.e. two nRF24L01+ transceiver modules.

Here are complete specifications:

nRF24L01+ transceiver module Specification
Frequency Range 2.4 GHz ISM Band
Maximum Air Data Rate 2 Mb/s
Modulation Format GFSK
Max. Output Power 0 dBm
Operating Supply Voltage 1.9 V to 3.6 V
Max. Operating Current 13.5mA
Min. Current(Standby Mode) 26µA
Logic Inputs 5V Tolerant
Communication Range 800+ m (line of sight)

For more information about nRF24L01+ IC, check out this datasheet.

nRF24L01+ module Vs nRF24L01+ PA/LNA module

There are a variety of modules available based upon the nRF24L01+ chip. Below are the most popular versions.

nRF24L01+ Wireless Transceiver Module
nRF24L01+ Wireless Module

The first version uses on-board antenna. This allows for a more compact version of the breakout. However, the smaller antenna also means a lower transmission range. With this version, you’ll be able to communicate over a distance of 100 meters. Of course that is outdoors in an open space. Your range indoors, especially through walls, will be slightly weakened.

nRF24L01+ PA LNA External Antenna Wireless Transceiver Module
nRF24L01+ PA LNA Wireless Transceiver Module with External Antenna

The second version comes with a SMA connector and a duck-antenna but that’s not the real difference. The real difference is that it comes with a special RFX2401C chip which integrates the PA, LNA, and transmit-receive switching circuitry. This range extender chip along with a duck-antenna helps the module achieve a significantly larger transmission range about 1000m.

What is PA LNA?

The PA stands for Power Amplifier. It merely boosts the power of the signal being transmitted from the nRF24L01+ chip. Whereas, LNA stands for Low-Noise Amplifier. The function of the LNA is to take the

nRF24L01+ RF PA LNA Power Amplifier Low Noise Amplifier Block Diagram
nRF24L01+ PA/LNA Block Diagram

extremely weak and uncertain signal from the antenna (usually on the order of microvolts or under -100 dBm) and amplify it to a more useful level (usually about 0.5 to 1V)

The low-noise amplifier (LNA) of the receive path and the power amplifier (PA) of the transmit path connect to the antenna via a duplexer, which separates the two signals and prevents the relatively powerful PA output from overloading the sensitive LNA input. For more information check out this article on digikey.com

TIP

Except this difference, both modules are drop-in compatible. Meaning, if you build your project with one you can just unplug it and use another without need to make any changes to the system.

How nRF24L01+ transceiver module works?

RF Channel Frequency

The nRF24L01+ transceiver module transmits and receives data on a certain frequency called Channel. Also in order for two or more transceiver modules to communicate with each other, they need to be on the same channel. This channel could be any frequency in the 2.4 GHz ISM band or to be more precise, it could be between 2.400 to 2.525 GHz (2400 to 2525 MHz).

Each channel occupies a bandwidth of less than 1MHz. This gives us 125 possible channels with 1MHz spacing. So, the module can use 125 different channels which give a possibility to have a network of 125 independently working modems in one place.

nRF24L01+ Wireless Transceiver 2.4GHz 125 RF Channels 1MHz Spacing

NOTE

The channel occupies a bandwidth of less than 1MHz at 250kbps and 1Mbps air data rate. However at 2Mbps air data rate, 2MHz bandwidth is occupied (wider than the resolution of RF channel frequency setting). So, to ensure non-overlapping channels and reduce cross-talk in 2Mbps mode, you need to keep 2MHz spacing between two channels.

RF channel frequency of your selected channel is set according to the following formula:

Freq(Selected) = 2400 + CH(Selected)

For example, if you select 108 as your channel for data transmission, the RF channel frequency of your channel would be 2508MHz (2400 + 108)

nRF24L01+ Multiceiver Network

The nRF24L01+ provides a feature called Multiceiver. It’s an abbreviation for Multiple Transmitters Single Receiver. In which each RF channel is logically divided into 6 parallel data channels called Data Pipes. In other words, a data pipe is a logical channel in the physical RF Channel. Each data pipe has its own physical address (Data Pipe Address) and can be configured. This can be illustrated as shown below.

nRF24L01+ Wireless Multiceiver Network Multiple Transmitters Single Receiver
nRF24L01+ Multiceiver Network – Multiple Transmitters Single Receiver

To simplify the above diagram, imagine the primary receiver acting as a hub receiver collecting information from 6 different transmitter nodes simultaneously. The hub receiver can stop listening any time and acts as a transmitter. But this can only be done one pipe/node at a time.

Enhanced ShockBurst Protocol

The nRF24L01+ transceiver module uses a packet structure known as Enhanced ShockBurst. This simple packet structure is broken down into 5 different fields, which is illustrated below.

nRF24L01+ Wireless Transceiver Enhanced ShockBurst Packet Structure
nRF24L01+ Enhanced ShockBurst Packet Structure

The original ShockBurst structure consisted only of Preamble, Address, Payload and the Cyclic Redundancy Check (CRC) fields. Enhanced ShockBurst brought about greater functionality for more enhanced communications using a newly introduced Packet Control Field (PCF).

This new structure is great for a number of reasons. Firstly, it allows for variable length payloads with a payload length specifier, meaning payloads can vary from 1 to 32 bytes.

Secondly, it provides each sent packet with a packet ID, which allows the receiving device to determine whether a message is new or whether it has been retransmitted (and thus can be ignored).

Finally, and most importantly, each message can request an acknowledgement to be sent when it is received by another device.

nRF24L01+ Automatic Packet Handling

Now, let’s discuss three scenarios to get a better understanding of how two nRF24L01+ modules transact with each other.

Transaction with acknowledgement and interruptThis is an example of positive scenario. Here the transmitter starts a communication by sending a data packet to the receiver. Once the whole packet is transmitted, it waits (around 130 µs) for the acknowledgement packet (ACK packet) to receive. When the receiver receives the packet, it sends ACK packet to the transmitter. On receiving the ACK packet the transmitter asserts interrupt (IRQ) signal to indicate the new data is available.nRF24L01+ Transceiver Working Packet Transmission

Transaction with data packet lostThis is a negative scenario where a retransmission is needed due to loss of the packet transmitted. After the packet is transmitted, the transmitter waits for the ACK packet to receive. If the transmitter doesn’t get it within Auto-Retransmit-Delay (ARD) time, the packet is retransmitted. When the retransmitted packet is received by the receiver, the ACK packet is transmitted which in turn generates interrupt at the transmitter.nRF24L01+ Transceiver Working Packet Transmission Data Lost

Transaction with acknowledgement lostThis is again a negative scenario where a retransmission is needed due to loss of the ACK packet. Here even if the receiver receives the packet in the first attempt, due to the loss of ACK packet, transmitter thinks the receiver has not got the packet at all. So, after the Auto-Retransmit-Delay time is over, it retransmits the packet. Now when receiver receives the packet containing same packet ID as previous, it discards it and sends ACK packet again.nRF24L01+ Transceiver Working Packet Transmission Acknowledgement Lost

This whole packet handling is done automatically by the nRF24L01+ chip without involvement of the microcontroller.

nRF24L01+ Transceiver Module Pinout

Let’s have a look at the pinout of both the versions of nRF24L01+ transceiver Module.

Pinout nRF24L01+ Wireless Transceiver Module

Pinout nRF24L01+ PA LNA External Antenna Wireless Transceiver Module

GND is the Ground Pin. It is usually marked by encasing the pin in a square so it can be used as a reference for identifying the other pins.

VCC supplies power for the module. This can be anywhere from 1.9 to 3.9 volts. You can connect it to 3.3V output from your Arduino. Remember connecting it to 5V pin will likely destroy your nRF24L01+ module!

CE (Chip Enable) is an active-HIGH pin. When selected the nRF24L01 will either transmit or receive, depending upon which mode it is currently in.

CSN (Chip Select Not) is an active-LOW pin and is normally kept HIGH. When this pin goes low, the nRF24L01 begins listening on its SPI port for data and processes it accordingly.

SCK (Serial Clock) accepts clock pulses provided by the SPI bus Master.

MOSI (Master Out Slave In) is SPI input to the nRF24L01.

MISO (Master In Slave Out) is SPI output from the nRF24L01.

IRQ is an interrupt pin that can alert the master when new data is available to process.

Wiring – Connecting nRF24L01+ transceiver module to Arduino UNO

Now that we have a complete understanding of how nRF24L01+ transceiver module works, we can begin hooking it up to our Arduino!

To start with, connect VCC pin on the module to 3.3V on the Arduino and GND pin to ground. The pins CSN and CE can be connected to any digital pin on the Arduino. In our case, it’s connected to digital pin#8 and #9 respectively. Now we are remaining with the pins that are used for SPI communication.

As nRF24L01+ transceiver module require a lot of data transfer, they will give the best performance when connected up to the hardware SPI pins on a microcontroller. The hardware SPI pins are much faster than ‘bit-banging’ the interface code using another set of pins.

Note that each Arduino Board has different SPI pins which should be connected accordingly. For Arduino boards such as the UNO/Nano V3.0 those pins are digital 13 (SCK), 12 (MISO) and 11 (MOSI).

If you have a Mega, the pins are different! You’ll want to use digital 50 (MISO), 51 (MOSI), 52 (SCK), and 53 (SS). Refer below table for quick understanding.

SPI Pin Configration of Arduino Uno,Arduino Nano and Arduino Mega
MOSI MISO SCK
Arduino Uno 11 12 13
Arduino Nano 11 12 13
Arduino Mega 51 50 52

In case you’re using different Arduino board than mentioned above, it is advisable to check the Arduino official documentation before proceeding.

Arduino Wiring Fritzing Connections with nRF24L01+ Wireless Transceiver Module
Wiring nRF24L01+ Wireless Transceiver Module to Arduino UNO
Arduino Wiring Fritzing Connections with nRF24L01+ PA LNA External Antenna Wireless Module
Wiring nRF24L01+ PA LNA Wireless Module to Arduino UNO

Remember! You need to make two of these circuits. One acts as a transmitter and the other as a receiver. The wiring for both is identical.

Once you have everything hooked up you are ready to go!

RF24 Arduino Library for nRF24L01+ Module

Interfacing with nRF24L01+ transceiver module is a bunch of work, but luckily for us, there are a number of libraries available. One of the popular libraries is RF24. This library has been around for several years. It is simple to use for beginners, but yet offers a lot for advanced users. In our experiments, we will be using the same library.

You can download the latest version of library on the RF24 GitHub repository fork or, just click this button to download the zip:

To install it, open the Arduino IDE, go to Sketch > Include Library > Add .ZIP Library, and then select the RF24-master file that you just downloaded.  If you need more details on installing a library, visit this Installing an Arduino Library tutorial.

Arduino Code – For Transmitter

In our experiment we will just send a traditional ‘Hello World’ message from the transmitter to the receiver.

Here is the sketch we will be using for our transmitter:

//Include Libraries
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

//create an RF24 object
RF24 radio(9, 8);  // CE, CSN

//address through which two modules communicate.
const byte address[6] = "00001";

void setup()
{
  radio.begin();
  
  //set the address
  radio.openWritingPipe(address);
  
  //Set module as transmitter
  radio.stopListening();
}
void loop()
{
  //Send message to receiver
  const char text[] = "Hello World";
  radio.write(&text, sizeof(text));
  
  delay(1000);
}

The sketch starts by including the libraries. SPI.h library handles the SPI communication while nRF24L01.h and RF24.h controls the module.

//Include Libraries
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

Next, we need to create an RF24 object. The object takes two pin numbers as parameters to which signals CE and CSN are connected.

//create an RF24 object
RF24 radio(9, 8);  // CE, CSN

Next we need to create a byte array which will represent the pipe address through which two nRF24L01+ modules communicate.

//address through which two modules communicate.
const byte address[6] = "00001";

We can change the value of this address to any 5-letter string such as “node1”. The address is necessary if you have a few modules in a network. Thanks to the address, you can choose a particular module to which you are interested in communicating, so in our case we will have the same address for both the transmitter and the receiver.

Next in the setup function: we need to initialize the radio object using radio.begin() and using the radio.openWritingPipe() function we set the address of the transmitter.

//set the address
radio.openWritingPipe(address);

Finally, we will use the radio.stopListening() function which sets module as transmitter.

//Set module as transmitter
radio.stopListening();

In the loop section: we create an array of characters to which we assign the message “Hello World”. Using the radio.write() function we will send that message to the receiver. The first argument here is the message that we want to send. The second argument is the number of bytes present in that message.

const char text[] = "Hello World";
radio.write(&text, sizeof(text));

Through this method, you can send up to 32 bytes at a time. Because that is the maximum size of a single packet nRF24L01+ can handle. If you need a confirmation that the receiver received data, the method radio.write() returns a bool value. If it returns TRUE, the data reached the receiver. If it returns FALSE, the data has been lost.

NOTE

the radio.write() function blocks the program until it receives the acknowledgment or runs out of all attempts of retransmission.

Arduino Code – For Receiver

Here is the sketch we will be using for our receiver

//Include Libraries
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

//create an RF24 object
RF24 radio(9, 8);  // CE, CSN

//address through which two modules communicate.
const byte address[6] = "00001";

void setup()
{
  while (!Serial);
    Serial.begin(9600);
  
  radio.begin();
  
  //set the address
  radio.openReadingPipe(0, address);
  
  //Set module as receiver
  radio.startListening();
}

void loop()
{
  //Read the data if available in buffer
  if (radio.available())
  {
    char text[32] = {0};
    radio.read(&text, sizeof(text));
    Serial.println(text);
  }
}

This program looks quite similar to the program of the transmitter except some changes.

At the beginning of the setup function we start the serial communication. Next using radio.setReadingPipe() function we set the same address as transmitter and in that way we enable the communication between transmitter and receiver.

  //set the address
  radio.openReadingPipe(0, address);

The first argument is the number of the stream. You can create up to 6 streams that respond to different addresses. We created only address for the stream number 0. The second argument is the address to which the stream will react to collect the data.

The next step is to set the module as a receiver and start receiving data. To do that we use radio.startListening() function. From that moment the modem waits for data sent to the specified address.

//Set module as receiver
  radio.startListening();

In the loop function: The sketch checks whether any data has arrived at the address using radio.available() method. This method returns TRUE value if we any data is available in buffer.

if (radio.available())
  {
    char text[32] = {0};
    radio.read(&text, sizeof(text));
    Serial.println(text);
  }

If the data is received, then it creates an array of 32 characters filled with zeros (later the program will fill it with the received data). To read the data we use the method radio.read (& text, sizeof (text)). This will store the received data in to our character array.

At the end we just print the received message on serial monitor. If you did everything ok and there are no mistakes in connections, you should see something like this in your Serial Monitor.

nRF24L01+ Transceiver RF24 Library Sketch Output on Serial Monitor
nRF24L01+ Transceiver Output on Serial Monitor

Improving range of nRF24L01+ transceiver Module

A key parameter for a wireless communication system is the communication range. In many cases it’s the deciding factor for choosing an RF solution. So, let’s discuss what we can do to get a better range for our module.

Reduce Power Supply Noise

An RF circuit that generates a Radio Frequency (RF) signal, is very sensitive to power supply noise. If not controlled, the power supply noise can significantly reduce the range you can get.

Unless the power source is a stand-alone battery, there is a good chance that there is noise associated with the generation of the power.  To prevent this noise from entering the system, it is advised to place a 10 µf filter capacitor across the power supply line as physically close to the nRF24L01+ module as possible.

An easiest way to get over with is to use a very inexpensive Adapter Module for nRF24L01.

nRF24L01+ 5V Power Adapter
nRF24L01+ Adapter

The adapter module has an 8-pin female connector to allow you to plug in your nRF24L01 module. It can accommodate both the module we discussed earlier, the one with integrated antenna and other with external antenna (PA/LNA). It also has a 6-pin male connector for the SPI and Interrupt connections and a 2-pin connector for power input.

The adapter module has its own 3.3 volt voltage regulator and a set of filter capacitors, so you can power it with a 5-volt power supply.

Change your channel frequency

Another potential source of noise for an RF circuit is the outside environment, especially if you have neighboring networks set on the same channel or interference from other electronics.

To prevent these signals from causing issues, we suggest using the highest 25 channels your nRF24L01+ module. Reason for this is WiFi uses most of the lower channels.

Lower Data Rate

The nRF24L01+ offers highest receiver sensitivity at 250Kbps speed which is -94dBm. However at 2MBps data rate, the receiver sensitivity drops to -82dBm. If you speak this language, you know that the receiver at 250Kbps is nearly 10 times more sensitive than at 2Mbps. That means the receiver can decode a signal that is 10 times weak.

What does Receiver (Rx) sensitivity mean?

Receiver sensitivity is the lowest power level at which the receiver can detect an RF signal. The larger the absolute value of the negative number, the better the receiver sensitivity. For example, a receiver sensitivity of −94 dBm is better than a receiver sensitivity of −82 dBm by 12 dB.

So, lowering the data rate can significantly improve the range you can achieve. Also, for most of our projects, 250Kbps speed is more than sufficient.

Higher Output Power

Setting maximum output power can also improve the communication range. The nRF24L01+ lets you choose one of the output power viz. 0 dBm, -6 dBm, -12 dBm or -18 dBm. Selecting 0 dBm output power sends stronger signal over the air.