Building an Arduino Wireless Network with Multiple NRF24L01 Modules

Building an Arduino Wireless Network with Multiple NRF24L01 Modules

Building an Arduino Wireless Network with Multiple NRF24L01 Modules

Today, we will introduce how to build a wireless network consisting of multiple NRF24L01 transceiver modules with Arduino. In this project, we built a network consisting of 5 nodes, where each node can communicate with any other node in the network, and they can act as both transmitters and receivers. In fact, this project sets up in this way to explain how to build a larger network, or more precisely, we can have a total of 3125 modules communicating with each other on a single RF channel. Let’s take a look at how it works.

Building an Arduino Wireless Network with Multiple NRF24L01 Modules

We will use the NRF24L01 module and the RF24 library for wireless communication between two Arduino development boards. In addition to this library, we also use the RF24Network library, which makes it easy to build a wireless network where many Arduino boards can communicate with each other. Below is how the network topology works.

A single NRF24L01 module can actively listen to up to 6 other modules simultaneously.

Building an Arduino Wireless Network with Multiple NRF24L01 Modules

The RF24 network library utilizes this capability to generate a network arranged in a tree topology, where one node is the base, and all other nodes are child nodes of that node or another node. Each node can have up to 5 child nodes, which can reach a depth of 5 levels, meaning we can create a network of a total of 3125 nodes. Each node must be defined with a 15-bit address that precisely describes the position of the node in the tree.

Building an Arduino Wireless Network with Multiple NRF24L01 Modules

We can actually define the node’s address in octal format. Therefore, the master or base address is 00, the base child addresses are 01~05, and the child addresses of node 01 are 011~051, and so on.

Note that if node 011 wants to communicate with node 02, the communication must go through node 01 and the base node 00, so these two nodes must always be active for successful communication.

Building an Arduino Wireless Network with Multiple NRF24L01 Modules

Arduino Wireless Servo Control Using RF24Network Library

Before we start, to better understand how the library works, let us illustrate a simple example of two Arduinos communicating with each other. Here is the circuit diagram for this example.

Building an Arduino Wireless Network with Multiple NRF24L01 Modules

The relevant components we will use are:

  • NRF24L01

  • Servo Motor

  • Potentiometer

  • Arduino Nano

We are using a potentiometer on the first Arduino development board to control the servo motor on the second Arduino. Now let’s take a look at the source code.

Here is the code for the potentiometer side:

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
 == Example 01 - Servo Control / Node 00 - Potentiometer ==
  by Dejan, www.HowToMechatronics.com
  Libraries:
  nRF24/RF24, https://github.com/nRF24/RF24
  nRF24/RF24Network, https://github.com/nRF24/RF24Network
*/
#include 
#include 
#include 

RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 00;   // Address of this node in Octal format ( 04,031, etc)
const uint16_t node01 = 01;

void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node);  //(channel, node address)
}

void loop() {
  network.update();
  unsigned long potValue = analogRead(A0);  // Read the potentiometer value
  unsigned long angleValue = map(potValue, 0, 1023, 0, 180); // Convert the value to 0-180
  RF24NetworkHeader header(node01);     // (Address where the data is going)
  bool ok = network.write(header, &angleValue, sizeof(angleValue)); // Send the data
}

First, we need to include the RF24 and RF24Network libraries as well as the SPI library. Then we create an RF24 object and include it in the RF24Network object. Here we need to define the node’s address in octal format, that is, define 00 for this node and 01 for the other node on the servo side.

In the setup() function, we need to initialize the network by setting the channel and address of this node.

In the loop() function, we need to frequently call the update() function, which handles all operations in the network. Then we read the value of the potentiometer and convert it to a value between 0 and 180, which is suitable for servo control. Then we create a network data header, where we allocate the address of the node where the data is going. Finally, we use the write() function to send the data to another node. The first parameter contains the address information, the second parameter contains the data to be sent, and the third parameter is the size of the data.

Here is the code for the servo motor side:

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
  == Example 01 - Servo Control / Node 01 - Servo motor ==
*/
#include 
#include 
#include 
#include 

RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 01;   // Address of our node in Octal format ( 04,031, etc)

Servo myservo;  // create servo object to control a servo

void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node); //(channel, node address)
  myservo.attach(3);   // (servo pin)
}

void loop() {
  network.update();
  while ( network.available() ) {     // Is there any incoming data?
    RF24NetworkHeader header;
    unsigned long incomingData;
    network.read(header, &incomingData, sizeof(incomingData)); // Read the incoming data
    myservo.write(incomingData);  // tell servo to go to a particular angle
  }
}

On the other side with the servo motor, we need to define the library and objects in the same way as mentioned before. Here the octal format for the node address is 01. After defining the servo motor address, in the loop() function, we continuously check for incoming data using a while() loop and the available() function. If true, we create a network data header to receive data and create a variable to store the data. Then we use the read() function to read the data and store it in the incomingData variable. Finally, we use this data to move the servo motor according to the potentiometer of the other node.

Using an Arduino Wireless Network with Multiple NRF24L01 Modules

After understanding the above operations, we can continue with our work, building a wireless network of 5 Arduino development boards communicating with each other. Below is a rough block diagram.

Building an Arduino Wireless Network with Multiple NRF24L01 Modules

So, starting from the base node, we will use the potentiometer to control the servo motor at node 01, using a second potentiometer, we will control the LED at node 022, using a button we will control the LED at node 012, and the LED at the base node will be controlled by the potentiometer at node 02. Similarly, using the infrared sensor at node 012, we will control the LED at node 01. Thus, we can see that this example explains how to send and receive data simultaneously and how to communicate with nodes from different branches. Now let’s take a look at the Arduino code.

Code for Base Node 00

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
          == Base/ Master Node 00==
  by Dejan, www.HowToMechatronics.com
  Libraries:
  nRF24/RF24, https://github.com/nRF24/RF24
  nRF24/RF24Network, https://github.com/nRF24/RF24Network
*/

#include 
#include 
#include 

#define button 2
#define led 3

RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 00;   // Address of this node in Octal format ( 04,031, etc)
const uint16_t node01 = 01;      // Address of the other node in Octal format
const uint16_t node012 = 012;
const uint16_t node022 = 022;

void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node);  //(channel, node address)
  radio.setDataRate(RF24_2MBPS);
  pinMode(button, INPUT_PULLUP);
  pinMode(led, OUTPUT);
}

void loop() {
  network.update();
  //===== Receiving =====//
  while ( network.available() ) {     // Is there any incoming data?
    RF24NetworkHeader header;
    unsigned long incomingData;
    network.read(header, &incomingData, sizeof(incomingData)); // Read the incoming data
    analogWrite(led, incomingData);    // PWM output to LED 01 (dimming)
  }
  //===== Sending =====//
  // Servo control at Node 01
  unsigned long potValue = analogRead(A0);
  unsigned long angleValue = map(potValue, 0, 1023, 0, 180); // Suitable for servo control
  RF24NetworkHeader header2(node01);     // (Address where the data is going)
  bool ok = network.write(header2, &angleValue, sizeof(angleValue)); // Send the data

  // LED Control at Node 012
  unsigned long buttonState = digitalRead(button);
  RF24NetworkHeader header4(node012);    // (Address where the data is going)
  bool ok3 = network.write(header4, &buttonState, sizeof(buttonState)); // Send the data

  // LEDs control at Node 022
  unsigned long pot2Value = analogRead(A1);
  RF24NetworkHeader header3(node022);    // (Address where the data is going)
  bool ok2 = network.write(header3, &pot2Value, sizeof(pot2Value)); // Send the data
}

Thus, in the base or master node, we need to define the library and objects as previously described, and define the addresses of all other nodes to which the master node will send data. In the loop() function, we first continuously check for incoming data. If there is, we read the data and store it in the incomingData variable, then use it to control the brightness of the LED. This data actually comes from the potentiometer at node 02. If we look at its code, we can notice that the setup is almost the same. It is important to assign the correct addresses to the locations where we want to send data. In this example, it is the master address 00. So after reading the value of the potentiometer and converting it to a suitable PWM value between 0 and 255, we send this data to the host. Here we can notice that I use the millis() function to send data at intervals of 10 milliseconds.

Code for Node 02

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
        == Node 02 (Child of Master node 00) ==
*/

#include 
#include 
#include 

RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 02;   // Address of our node in Octal format ( 04,031, etc)
const uint16_t master00 = 00;    // Address of the other node in Octal format

const unsigned long interval = 10;  //ms  // How often to send data to the other unit
unsigned long last_sent;            // When did we last send?

void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node);  //(channel, node address)
  radio.setDataRate(RF24_2MBPS);
}

void loop() {
  network.update();
  //===== Sending =====//
  unsigned long now = millis();
  if (now - last_sent >= interval) {   // If it's time to send a data, send it!
    last_sent = now;
    unsigned long potValue = analogRead(A0);
    unsigned long ledBrightness = map(potValue, 0, 1023, 0, 255);
    RF24NetworkHeader header(master00);   // (Address where the data is going)
    bool ok = network.write(header, &ledBrightness, sizeof(ledBrightness)); // Send the data
  }
}

Next, we send potentiometer data from the host to node 01 to control the servo motor.

Code for Node 01

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
        == Node 01 (Child of Master node 00) ==
*/

#include 
#include 
#include 
#include 

#define led 2

RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 01;   // Address of our node in Octal format ( 04,031, etc)
const uint16_t master00 = 00;    // Address of the other node in Octal format

Servo myservo;  // create servo object to control a servo

void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node); //(channel, node address)
  radio.setDataRate(RF24_2MBPS);
  myservo.attach(3);   // (servo pin)
  pinMode(led, OUTPUT);
}

void loop() {
  network.update();
  //===== Receiving =====//
  while ( network.available() ) {     // Is there any incoming data?
    RF24NetworkHeader header;
    unsigned long incomingData;
    network.read(header, &incomingData, sizeof(incomingData)); // Read the incoming data
    if (header.from_node == 0) {    // If data comes from Node 02
      myservo.write(incomingData);  // tell servo to go to a particular angle
    }
    if (header.from_node == 10) {    // If data comes from Node 012
      digitalWrite(led, !incomingData);  // Turn on or off the LED 02
    }
  }
}

Node 01 actually receives data from two different nodes, one for servo control and the other for LED control from node 012.

Code for Node 012

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
            == Node 012 (child of Node 02)==
*/

#include 
#include 
#include 

#define led 2
#define IR 3

RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 012;  // Address of our node in Octal format ( 04,031, etc)
const uint16_t node01 = 01;    // Address of the other node in Octal format

void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node);  //(channel, node address)
  radio.setDataRate(RF24_2MBPS);
  pinMode(led, OUTPUT);
  pinMode(IR, INPUT);
}

void loop() {
  network.update();
  //===== Receiving =====//
  while ( network.available() ) {     // Is there any incoming data?
    RF24NetworkHeader header;
    unsigned long buttonState;
    network.read(header, &buttonState, sizeof(buttonState)); // Read the incoming data
    digitalWrite(led, !buttonState); // Turn on or off the LED
  }
  //===== Sending =====//
  unsigned long irV = digitalRead(IR); // Read IR sensor
  RF24NetworkHeader header8(node01);
  bool ok = network.write(header8, &irV, sizeof(irV)); // Send the data
}

In this segment of code, we use the header.from_node attribute to get information about which node the data comes from. If the input data comes from the master device, we use it to control the servo, and if the input data comes from node 012, we use it to control the LED.

In node 012, we have both sending and receiving. The infrared sensor controls the LED at node 01, and this LED is controlled by the button on the host.

Code for Node 022

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
            == Node 022 (child of Node 02)==
*/

#include 
#include 
#include 

#define led1 2
#define led2 3
#define led3 4
#define led4 5

RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 022;  // Address of our node in Octal format ( 04,031, etc)
const uint16_t master00 = 00;    // Address of the other node in Octal format

void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node);  //(channel, node address)
  radio.setDataRate(RF24_2MBPS);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
  pinMode(led4, OUTPUT);
}

void loop() {
  network.update();
  //===== Receiving =====//
  while ( network.available() ) {     // Is there any incoming data?
    RF24NetworkHeader header;
    unsigned long potValue;
    network.read(header, &potValue, sizeof(potValue)); // Read the incoming data
    // Turn on the LEDs as depending on the incoming value from the potentiometer
    if (potValue > 240) {
      digitalWrite(led1, HIGH);
    } else {
      digitalWrite(led1, LOW);
    }
    if (potValue > 480) {
      digitalWrite(led2, HIGH);
    } else {
      digitalWrite(led2, LOW);
    }
    if (potValue > 720) {
      digitalWrite(led3, HIGH);
    } else {
      digitalWrite(led3, LOW);
    }
    if (potValue > 960) {
      digitalWrite(led4, HIGH);
    } else {
      digitalWrite(led4, LOW);
    }
  }
}

Finally, we use data from another potentiometer on the host to control the LEDs at node 022.

Therefore, if everything is connected correctly and all nodes are always active, our work can be accomplished by precisely addressing the nodes, and all the heavy lifting is done by the powerful RF24Network library.

Follow the wheel of technology, and we will meet again next time!

Building an Arduino Wireless Network with Multiple NRF24L01 Modules

Leave a Comment

Your email address will not be published. Required fields are marked *