Introduction
Let embedded devices communicate to each other is very useful when it comes to remotely control a nearby device.
You could also allow those devices communicate via SoftwareSerial
communication, but in this way the devices movements would be limited by a couple of cables (TX
to send and RX
to receive information).
Benefits
nRF24L01
module has some benefits:
- Connectivity: It is very easy to pair
nRF24L01
module with a variety of microcontroller systems by using theSPI
protocol or anRF24
library when pairing with Arduino boards. - High transmission range:
nRF24L01
module is able to transmit wavelengths over several kilometres. Moreover, the IPX antenna (shown in the article’s main image) for wireless communication allow up to1'000
meters communication. - Operating Frequency:
2.4Ghz
operating frequency allows for higher bit rate usages rather than other lower operating frequencies. It usesGFSK
modulation for data transmission: data transfer rate can either be250kbps
,1Mbps
or2Mbps
. - Low cost:
nRF24L01
module is one of the cheapest wireless transceiver modules in the market.
nRF24L01+ VS nRF24L01+ PA
Before comparing those modules, let’s talk about a finesse that you may have not noticed.
Some nRF24L01
module on the market are followed by a “+
” sign. The nRF24L01+
is a newer version of the nRF24L01
, capable of doing an extra 250kbps of on-air data rate while the one without “+
” has only 1Mbps
and 2Mbps
frequencies.
Apart from that, they can both be considered as identical to one another. Both versions can be mixed together without any problems as long as 1
or 2
MBps
is being used as the data rate.
There are a lot of modules available which are based on the nRF24L01+
chip.
For simplicity, during this article I’ll treat nRF24L01
module in the same way as nRF24L01+
version.
nRF24L01+
This wireless module comes with an onboard antenna. Its compact size could be suitable for narrow spaces. As cons, it loses out in transmission range since it has no antenna to expand the signal.
2.4G
wireless module nRF24L01+ PA
This module comes with an IPX antenna. The antenna allows for a wider transmission range of up to 1000 meters, as already I already told you above.
Common Software Setup
To avoid repeating myself later on, let’s describe the different environments you’ll need to set up to get everything up and running.
Software Setup for Arduino
Please refer to the official Getting Started guide to install the Arduino IDE on the OS you prefer.
To control the wireless module, you need to install RF24
library from GitHub.
Please refer to the official Arduino tutorial about Installing Libraries if you have never installed a library.
The steps to install RF24.h
library are:
- Press on the green rounded button
Code
right above the number of commits - Press on Download ZIP.
- Open the IDE and click
Sketch
>
Include library
>
Add .ZIP Library...
and select the previously downloaded.zip
file
Software Setup for Python
One of the best environments for Python programming (and definitely my favorite) is PyCharm by JetBrains. However, PyCharm is not really suitable for the Raspberry Pi board since it’s very resource-consuming. As an alternative, some light-weight IDEs are:
- Visual Studio Code which is also available for Raspberry PI by following this guide
- Thonny which is installed by default on Raspberry Pi OS
- Mu
You need to install circuitpython-nrf24l01 library from pypi.org. You can find the official documentation on readthedocs.io.
It is recommended to install python libraries in a virtual environment:
# clone with SSH
git clone git@gitlab.com:PitPietro/arduino-projects.git
# or clone with HTTP
# git clone https://gitlab.com/PitPietro/arduino-projects.git
cd projects/nrf24l01-module/raspberry-pi-to-arduino/[PROJECT-FOLDER]
sudo apt install -y python3.10-venv
python3 -m venv .env
source .env/bin/activate
The .env
environment has now been activated: you should see (.env)
before the $
sign in your terminal.
You can now install the library you need:
pip3 install circuitpython-nrf24l01
Please Note: You need to replace “[PROJECT-FOLDER]
” with the actual current project you’re working on.
Once done, if you want to remove the .env
files and save some unused space:
cd projects/nrf24l01-module/raspberry-pi-to-arduino/[PROJECT-FOLDER]
rm -rf .env
Arduino to Arduino
This article is quite different from the others, since there isn’t a single wiring schema but there are as many schemas as the number of examples shown.
In first place, let’s take a look at the wiring between the Arduino Uno board and the nRF24L01
module.
Numeric Payload
In this example you will be use a couple of Arduino boards with the same code uploaded into them.
The main difference is that you have to baptize a board as a sender and the other as receiver and communicate this decision through the Serial Monitor
.
Since Arduino IDE can only handle a single Serial Monitor at a time, you should use one of the following solutions to be able to control different Arduino boards from the same computer:
- Arduino Web Editor
- Arduino IDE 2.0 (which is at its 9th Release Candidate version)
- Arduino CLI (Command Line Interface to control your board from a Terminal window)
Hardware requirements
- 2 Arduino Uno boards
- 2
nRF24L01(+)
modules with or without the antenna
Please Note: Follow the wiring schema above.
Code
Download numeric-payload.ino
sketch.
Load the code in the first board, open the Serial Monitor and send the string “TX
” (without typing the double quotes, too) to set the Arduino as sender.
Load the code in the second board, open the Serial Monitor and send the string “RX
” (without typing the double quotes, too) to set the Arduino as receiver.
The comments inside the code describe every single step in a very detailed way, please read them carefully.
Please Note: It’s mandatory to open the Serial Monitors, otherwise the code will not execute. Moreover, I strongly recommend annotating the port to whom the boards are connected. In this way, you know what port is associated to the sender, and what to the receiver. Moreover, don’t forget that if you restart your PC, the port name could change.
The code also allow you to change the behaviour of the board during the execution, without having to reset the board. Take a look at the section below.
Serial Monitor execution output
Plug both the sender and the receiver to the computer, load the code and open the Serial Monitor(s).
Take a look at sender-log and receiver-log to see how the transmission works.
Remote LED control with button
Hardware requirements
- 2 Arduino Uno boards
- 2
nRF24L01(+)
modules with or without the antenna - 1 colored LED
- 1 switch push button
10 kOhms
resistor for the button220 Ohms
resistor for the LED- Hook-up wires of different colors
- 2 breadboards
The sender board (in the right, into the wiring schema) has a button that will light-up the LED attached to the receiver board (in the left, into the wiring schema). In this case, there are two different sketches to load on the boards.
Moreover, it is not mandatory to open the Serial Monitor since the code will execute anyway.
Code
This project is made up by two sketches:
After uploading the code, you can place the boards in different rooms of the house, power them and try to press the button.
Send messages to remote LCD
If you need a bidirectional wireless communication between two boards, you could use a couple of Serial Monitor attached to them and send the users input as payloads.
This constant change of role (from sender to receiver and vice versa) of the boards, could become quite difficult to implement.
But let’s say you need a unidirectional wireless communication, so that the receiver does not need the ability to reply.
The use of an LCD
display allow you to visualize the received messages without the use of the second Serial Monitor.
Hardware requirements
- 2 Arduino Uno boards
- 2
nRF24L01(+)
modules with or without the antenna 16x2
LCD display220 Ohms
resistor- potentiometer (usually
10k Ohms
) - Hook-up wires of different colors
- Breadboard
At this point, you can follow the below wiring schema. The sender is on the left, while the receiver is on the right.
Code
This project is made up by two sketches:
Let’s explain a little more in depth the major difficulties faced in the development of the two sketches.
Sender Code
In the sender’s code, the correct sending of the string message through the wireless module has been quite tricky.
Arduino implements String
data type (unlike standard C
programming language), but its use can lead to errors: it is indeed different from an array of char
.
To properly send the message, you need to loop through each character of the string, and send it one by one.
The message terminates with the line-feed character '\n'
, which is an escape sequence. It’s now up to the receiver understanding when the message is terminated and act accordingly.
Receiver Code
In the receiver’s code, receiving the string message through the wireless module has been the funny part.
Since the message could be longer than the length of the display, one of the hardest task to perform was to split the message over all the display rows.
Moreover, the code must cover as much edge cases as possible so the number of columns and rows cannot be hardcoded: if you use a different LCD, the code must work the same way.
The solution has been to split the original message into n
different sub-messages, one for each row. The carrier position has been set on the left edge of each row before printing the sub-message.
Another very difficult task was to understand when to clear the screen and prepare it for the arrival of the new message.
I don’t want to go any further to make room for the other projects as well but don’t worry, everything is well-explained in the comments inside the code.
Raspberry Pi to Arduino
The main problem of communication between Raspberry Pi and Arduino is that they’re programmed in different languages, which has different data types that may not collide.
Raspberry Pi boards are programmed in Python
, while Arduino boards in their C/C++
language.
To overcome this issue, you can import struct
module to perform conversions between Python values and C structs represented as Python bytes objects.
struct
module uses Format Strings as compact descriptions of the layout of the C structs and the intended conversion to/from Python values.
Please refer to Python struct
module in-depth analysis if you want to learn more.
Simple Payloads
Similar to what was done in Arduino to Arduino’s Numeric Payload example, in this project you’ll send payloads of different data types from the Raspberry Pi to the Arduino and vice versa.
Code
This project is made up by two sketches:
- Raspberry Pi’s
main.py
- Arduino’s
arduino-simple-payloads.ino
The default payload sent/received is a char
of 1 byte. You can change the payload type by looking in both the codes sketches for lines followed by comments that start with “*uncomment if *” or something similar.
The available payload data types are: char
, bool
, short
, int
, long
, float
and double
.
There are many other data types you can share between wireless devices. Please refer to Python struct module slice to find out more.
RPI sends and Arduino receives
Assuming arduino-projects
GitLab repository as base director, open a Terminal and type:
cd projects/nrf24l01-module/raspberry-pi-to-arduino/simple-payloads
# run the program in TX mode
python3 main.py -r TX
# verbose alternative
python3 main.py --role TX
Then load the Arduino sketch, open the Serial Monitor, wait for the fist line to appear and type:
RX
# the question you are replying to is:
# "# is this device a sender (TX) or a receiver (RX)?"
Arduino sends and RPI receives
Assuming arduino-projects
GitLab repository as base director, open a Terminal and type:
cd projects/nrf24l01-module/raspberry-pi-to-arduino/simple-payloads
# run the program in RX mode
python3 main.py -r RX
# verbose alternative
python3 main.py --role RX
Then load the Arduino sketch, open the Serial Monitor and type:
TX
# the question you are replying to is:
# "# is this device a sender (TX) or a receiver (RX)?"
Conclusion
Here are some useful in-depth analysis to conclude the article.
Software Driven SPI
It may happen that the Arduino needs to be connected to an I/O device that uses the same SPI
pins as the nRF24L01
wireless module.
Luckily for us, RF24
library provides the ability to change the default SPI
pins by manually editing RF24_config.h
file.
Assuming you’re working with a Unix-like machine, you need to open a Terminal window:
# move to RF24 directory, which is inside Arduino libraries folder
cd Arduino/libraries/RF24/
# edit the config file
nano RF24_config.h
Uncomment the following line, that should be n° 27
or nearly:
#define SOFTSPI // Requires library from https://github.com/greiman/DigitalIO
Add below the following directives:
#define SOFT_SPI_MISO_PIN 7
#define SOFT_SPI_MOSI_PIN 8
#define SOFT_SPI_SCK_PIN 9
Of course, it is not mandatory to choose the pins I used: you can choose your own, but be aware of changing the wiring schema accordingly.
Install and include DigitalIO
library on the top of your sketches:
#include <SPI.h>
#include "printf.h"
#include "RF24.h"
#include <DigitalIO.h>
// instantiate an object for the nRF24L01 transceiver
// using pin 10 for the CE pin, and pin 13 for the CSN pin
RF24 radio(10, 13);
// ...
In this case, the wiring schema would be:
Please Note: Since RF24
library has been changed globally, every Arduino board that need to be attached to nRF24L01
module must now follow the above writing schema.
If you want to come back to the previous pin configuration, you must undo the edits made to RF24_config.h
file.
Python struct module
Format strings are the mechanism used to specify the expected layout when packing and unpacking data. They are built up from Format Characters, which specify the type of data.
Moreover, the special characters @
, =
, <
, >
and !
control:
- Byte Order:
native
,little-endian
,big-endian
ornetwork
- Size:
native
orstandard
- Alignment:
native
ornone
To communicate with the Arduino boards, you need to use <
, which stands for:
- Byte Order:
little-endian
- Size:
standard
- Alignment:
none
Here below is shown a short version of the Format characters table.
Standard size column refers to the size of the packed value in bytes when using standard size: so when the format string starts with '<'
, '>'
, '!'
or '='
.
*
_Bool type defined by C99. See Boolean type support library. *
**
On the Uno and other ATMEGA based boards, double occupies 4 bytes (which is the same as the float: no gain in precision), while on the Arduino Due, double variables occupies 8 bytes (as it should usually be). See double data type. **
Please Note: long long data type exists in C language, while it does not exist in Arduino language, so it was not possible to send or receive a payload made up by 8 bytes.
Documentation
Here are some useful links:
- Code repository:
nRF24L01-module
directory fromarduino-projects
GitLab repository
- Articles:
seeedstudio.com
’s Getting Started with NRF24L01 Transceiver: Arduino Guide
- Libraries & modules:
RF24
library for ArduinoLiquidCrystal
library for Arduinoreadthedocs.io
python
library for Linux-based SoC computers like the Raspberry Pistruct
Python module
- Other links: