Using NRF24L01+ modules between arduino’s

I picked up a couple of 2.4GHz Antenna Wireless Transceiver module from a webshop to use them between arduino’s. There are several types of the modules but I picked up the NRF24L01+ modules:

modules

These modules are low power versions with a range off 60 meters maximum. I measured 20 meters running with the PA level High. This is the same distance I get with Bluetooth but there is no pairing involved! So a big pro is easier setup and a more stable long-time communication solution (I can reset the sensors any time) but I loose the possibility to use the Firmata protocol. I have to write my own protocol.

This is part 8 of a serie of blogs about device communication between Arduino, RaspberryPi etc:

  • Part 1: Using I2C to connect two Arduino Nano’s
  • Part 2: Using I2C between RPi Master and Arduino Slave
  • Part 3: Use Bluetooth between Win 10 UWP and Arduino
  • Part 4: Add virtual Arduino ports to your UWP app
  • Part 5: Custom Firmata function called by Windows 10 IoT Core
  • Part 6: Better long running custom Firmata functions
  • Part 7: Custom servo usage in Firmata
  • Part 8: Using NRF24L01+ modules between arduino’s
  • Part 9: Cheap Arduino mesh using RF24 radio modules

First I had to find out the pin layout. These babies have 8 pins, what to do with them? I checked out this nice article which worked fine for me. I got the devices working on a Mini pro and two Nano’s.

WP_20160306_01_00_44_Pro_LI

Watch out for the voltage. These ones work on 3.3v volt. The pin layout that worked for me was:

  1. GND  BLACK on  GND
  2. VCC  RED on 3.3V!
  3. CE   ORANGE on 7
  4. CSN  YELLOW on 8
  5. SCK  GREEN on 13
  6. MOSI BLUE on 11
  7. MISO VIOLET on 12
  8. ignored, not used

These work nicely with the RF24 library. This library supports both Raspberry Pi and Arduino. And there is a getting started demo. In this demo you can switch between transmitter and receiver but it did not make sense to me what was happening at first. And watch out for the unique number, do not deploy the same number twice…

So I took that demo and splitted it in half. I removed a lot of garbage and added some useful success/fail ratio counters.

Transmitter

The transmitter used this code:

#include <SPI.h>
#include "RF24.h"

// on Nano or Mini Pro
// |--------------------|
// |    UPSIDE      8 7 |
// |     PINS       6 5 |\
// |      ON        4 3 |\\
// |  OTHER SIDE    2 1 |\\
// |--------------------|\\
//                     \  \
// # DESC COLOR  Arduino port
// 1 GND  BLACK           GND
// 2 VCC  RED           3.3V!
// 3 CE   ORANGE            7
// 4 CSN  YELLOW            8
// 5 SCK  GREEN            13
// 6 MOSI BLUE             11
// 7 MISO VIOLET           12

/* Hardware configuration: */
/* Set up nRF24L01 radio on SPI bus plus pins 7 & 8 */
RF24 radio(7, 8);

byte addresses[2][6] = {"XXXXX","XXXXX"};

int failcount = 0;
int successcount = 0;
bool mustlistenforanswer;

void setup() {
  Serial.begin(115200);
  Serial.println(F("RF24 Transmitter started"));

  // Set the PA Level low (RF24_PA_LOW) to prevent 
  // power supply related issues since this is a
  // getting_started sketch, and the likelihood of 
  // close proximity of the devices. RF24_PA_MAX is default.
  // Open a writing and reading pipe on each radio, 
  // with opposite addresses
}

void loop() {
  radio.begin();
  radio.setPALevel(RF24_PA_MAX);
  radio.openWritingPipe(addresses[1]);
  radio.openReadingPipe(1, addresses[0]);

  // First, stop listening so we can talk.
  radio.stopListening();

  mustlistenforanswer = true;

  // Remember this time, and send it. 
  // This will block until complete
  unsigned long timeSent = micros();

  if (!radio.write( &timeSent, sizeof(unsigned long) ))
  {
    mustlistenforanswer = false;

    failcount += 1;

    Serial.println(F("failed on sending"));
  }

  // invariant: request is sent, or not

  if (mustlistenforanswer)
  {
    // invariant: request is sent

    radio.startListening();                                    
    unsigned long started_waiting_at = micros();
    boolean timeoutoccurred = false;

    while ( !radio.available() ) {                            
      unsigned long timeNow = micros();

      if (timeNow - started_waiting_at > 400000 ) {           
        timeoutoccurred = true;
        break;
      }
    }

    if ( timeoutoccurred )
    {
      // Describe the results

      failcount += 1;

      Serial.println(F("Failed, response timed out."));
    }
    else
    {
      unsigned long timeReceived;
      radio.read( &timeReceived, sizeof(unsigned long) );

      unsigned long timeNow = micros();

      // Spew it
      Serial.print(F("Response "));
      Serial.print(timeReceived);
      Serial.print(F(", Round-trip delay "));
      Serial.print((timeNow - timeReceived) / 1000);
      Serial.print(F(" milliseconds; Error ratio:"));

      successcount += 1;
      char buffer[8];
      sprintf(buffer, "%03d/%03d;", failcount,  successcount);

      Serial.println(buffer);
    }
  }

  delay(1500);
}

First we initialize the radio for two channels, for reading and writing, with a high PA level. This uses more power but gives us better range. We directly start transmitting the current time (in microseconds). And then we start waiting for an answer. Once received, we convert it back to the microsconds and so we know how long it took to send the bytes around. And then we send the next timestamp for the next roundtrip…

I also count the number of successful roundtrips and failures. This gives me the following log:

Sender

Receiver

But how does the code of the receiver look like? Well, it looks like this:

#include <SPI.h>
#include "RF24.h"
 
// on Nano or Mini Pro
// |--------------------|
// |    UPSIDE      8 7 |
// |     PINS       6 5 |\
// |      ON        4 3 |\\
// |  OTHER SIDE    2 1 |\\
// |--------------------|\\
//                     \  \
// # DESC COLOR  Arduino port
// 1 GND  BLACK           GND
// 2 VCC  RED           3.3V!
// 3 CE   ORANGE            7
// 4 CSN  YELLOW            8
// 5 SCK  GREEN            13
// 6 MOSI BLUE             11
// 7 MISO VIOLET           12
 
/* Hardware configuration: Set up nRF24L01 radio /*
/* on SPI bus plus pins 7 & 8 */
RF24 radio(7,8);
 
byte addresses[2][6] = {"XXXXX","XXXXX"};
 
void setup() {
  Serial.begin(115200);
  Serial.println(F("RF24 Receiver started"));

  // Set the PA Level low (RF24_PA_LOW) to prevent
  // power supply related issues since this is a
  // getting_started sketch, and the likelihood of
  // close proximity of the devices. RF24_PA_MAX is default.
  // Open a writing and reading pipe on each radio,
  // with opposite addresses

  radio.begin();
  radio.setPALevel(RF24_PA_MAX);
  radio.openWritingPipe(addresses[0]);
  radio.openReadingPipe(1,addresses[1]);
}
 
void loop() {
  radio.startListening();

  if( radio.available()){

    unsigned long timeReceived;
 
    while (radio.available()) {
      radio.read( &timeReceived, sizeof(unsigned long) );
    }

    radio.stopListening();
    radio.write( &timeReceived, sizeof(unsigned long) );
 
    Serial.print(F("Sent response "));
    Serial.println(timeReceived);
  }
}

The radio is initialized with the same channels and starts listening directly. Every byte stream received is converted to a unsigned long and sent back again.

So the logic of the receiver is very simple. Here is how the output looks:

Receiver

Here we send a simple unsigned long back and forward. But you can send multiple values in an array also. This makes it even more flexible.

 

 

Advertenties