Friday, April 19, 2024
More
    HomeMCUsArduinoArduino Nano R3 + nRF24L01 = RF-Nano

    Arduino Nano R3 + nRF24L01 = RF-Nano

    Review-Tutorials?

    Before we begin, we want to briefly explain the format of this Review article.

    Where most publications produce Reviews as strictly-informational articles covering a particular product or service; Flowduino is all about education!

    For this reason, we present every Review with at least one Tutorial element.

    After all, how better to review a Microcontroller, Sensor, IO device, or Development Board than to walk through a project using them?

    So, please enjoy this Review-Tutorial (“Reviewtorial?”)

    The Problem…

    We here at Flowduino build a lot of wirelessly interconnected devices. No, seriously! More than half of all of the projects we develop require multiple devices to communicate without wires, sometimes over vast distances.

    We have, at times, gone so far as to build line-of-sight optical laser communications into our devices, sometimes even with optical repeater devices necessary to span distances of multiple kilometres, where low-latency is a concern.

    Readers with more experience working with Arduino and other MCUs will very likely have experienced adding radio modules to their devices. There is certainly no shortage of choice in this area, either!

    From the most simple modules, such as the 433MHz HC-12 UART radio, to the slightly more complex nRF24L01 SPI, or Bluetooth (JDY-33, DW-CT14, AT-09 etc), or WiFi, or LoRa, or even GPRS/LTE… the options are practically limitless.

    Arduino Nano 33 IoT
    Arduino Nano 33 IoT

    These days, Arduino manufacture their IoT range of development boards, which feature WiFi and/or Bluetooth on-chip. However, these come at a higher price point, and – as with all things – have their own disadvantages.

    So, what if you need radio communication between multiple devices, but want to use a simple, low-profile development board without having to attach a separate radio to it? What if Bluetooth and WiFi aren’t suitable?

    The RF-Nano (a Solution)

    Introducing the RF-Nano V3.0, from eMakefun!

    The RF-Nano combines the simplicity and compatibility of the Arduino Nano R3’s ATmega328 with the usefulness of the nRF24L01+ 2.4GHz radio transceiver.

    No more multi-wire SPI connection required!

    RF-Nano Long-Angle Shot
    RF-Nano – Profile 2 – © Flowduino.com
    RF-Nano Top-down Shot
    RF-Nano – Profile 3 – © Flowduino.com

    Working our way down the board from the top of this photograph:

    • A Micro-USB socket… an improvement over the official Arduino Nano R3’s Mini-USB socket.
    • Beneath that in the middle we have the ATmega328 chip
    • You can then see the Reset button
    • Followed by some SMD components and the LEDs for power, TX, RX etc.
    • Beneath that is the nRF24L01+ chip. This is what gives the RF-Nano the integrated 2.4GHz radio functionality.
    • At the bottom, in the centre, we see a PCB Antenna for the nRF24L01+
    • Bottom-right, we have a socket for connecting an SMA or RP-SMA antenna pigtail. This allows us to use an External Antenna, increasing (considerably) the communication range.

    Down the left- and right-hand sides, we have the GPIO pins, which are verbatim identical to those from the Arduino Nano R3.

    Packaging

    The RF-Nano comes in a very small cardboard box.

    The printing on the box is nice, albeit bearing an illustration of the Arduino Nano R3 rather than the RF-Nano.

    Inside, it contains the RF-Nano board itself, embedded into a piece of foam to offer some protection from physical impacts.

    RF Nano Packaging

    Just be aware that there is no form of static-proof packaging, or even plastic coverings on either the board or the box.

    This means that damage could occur in storage or transit, depending on the conditions under which your chosen supplier looks after its stock.

    Fortunately for me, my preferred supplier takes impeccable care of their stock, ensures that all outbound packages are suitably packed and protected, and I have had no problems with the 7 RF-Nano boards I have ordered for testing.

    Specifications

    FeatureValue
    MicrocontrollerATmega328P
    Clock16MHz
    Flash32KB
    GASH2KB
    EEPROM1KB
    USB-Serial BridgeCH340G
    Digital I/O Pins14
    PWM Pins6
    Analog Input Pins8
    DC Current-per-Pin40mA
    Input Voltage6-20V recommended (5V works fine)
    5V Max Current800mA
    3.3V Max Current180mA
    Weight15g
    Addressable Registers3 (PORTB, PORTC, PORTD)
    Radios-on-boardYes: nRF24L01+
    AntennasYes: 1x PCB + 1x U.FL 1.15mm Pigtail Socket
    Persistent StorageNo
    Solder PinsYes, Pre-soldered
    PackagingFoam insert in Cardboard. No Static-Proof Bag!
    USB InterfaceMicro-USB Socket (Female)
    Buttons on-boardYes: Reset
    LEDs on-boardYes: Power, Flash, RX, TX
    RF Nano - Angle Shot
    RF-Nano V3.0 Underside
    RF-Nano Long-Angle Shot
    RF-Nano Closeup of NRF24L01 Chip
    Closeup of the NRF24L01+ Chip

    As you can see, the Microcontroller specifications are identical to the Arduino Nano R3 development board. This means you can do a direct MCU swap on existing projects or prototypes using the Arduino Nano R3 dev board, for Nano-RF dev boards.

    Unavailable Pins!

    It must be noted that the nRF24L01+ chip is connected to the ATmega328P chip directly on the board. This means that there are SPI pins on the GPIO that you can no longer use for other purposes.

    I shall list these pins, and what they’re connected to, now:

    GPIO Pin NumbernRF24L01+ SPI
    D9CE (Chip Enable)
    D10CS/CSN (Chip Select)
    D11MOSI
    D12MISO
    D13SCK

    Important Note: The RF-Nano official documentation has CE and CS listed in the wrong order! Our table here has the correct Pin assignments. This matters because, if you reverse CE and CS, the radio will initialise just fine, but you won’t be able to transmit or receive anything with it. We wasted an awful lot of time trying to figure this one out! Hopefully, we just saved yours.

    So, just be aware that you cannot use these listed SPI pins on the GPIO pin header for anything else. This isn’t a problem, mind you, as you would have to use these same pins on an Arduino Nano R3 if you wanted to connect an external SPI radio module.

    Let’s build something with two RF-Nanos!

    Okay, so we’ve taken a look at the specifications, and acknowledged the handy advantages that a board integrating a 2.4GHz radio can have for our projects.

    Now, let’s actually test these boards and see how quickly and easily we can make two devices talk to each other!

    We’re going to make a simple RGB light unit, with a Radio Remote Controller!

    Ingredients…

    This recipe requires two breadboards, and two RF-Nano development boards.

    Per-board, you will need:

    • 1x RGB LED (or a separate Red, Green, and Blue LED – respectively)
    • 3x 330 Ohm Resistors

    One of the two boards will also require:

    • 3x Momentary-Push Buttons
    • 3x 1 kOhm (1000 Ohm) Resistors

    Wiring…

    There are two similar wiring diagrams for this project.

    The RGB Display (Receiver) Board

    RF-Nano Demo - Receiver [Fritzing]
    RF-Nano Demo – Receiver [Fritzing]

    I’ve chosen to illustrate this wiring diagram using separate LEDs for Red, Green, and Blue (respectively) simply because it is easier to visualise. You can, of course, just as easily connect the resistors to the R, G, and B legs of a single RGB LED.

    I ask that you forgive that the dev board shown in the illustration is the Arduino Nano R3 rather than the RF-Nano… I was unable to find the RF-Nano part available for Fritzing.

    GPIO Pin wiring:

    GPIO PinDirectionConnects to…
    D2OutputRed LED via 330Ohm resistor
    D3OutputGreen LED via 330Ohm resistor
    D4OutputBlue LED via 330Ohm resistor

    The Remote (Transmitter/Controller) Board

    The Remote (Transmitter/Controller) Board is wired identically with LEDs and resistors, to the same pins of the RF-Nano.

    However, we add 3 components to this board: push buttons.

    RF-Nano Demo - Transmitter [Fritzing]
    RF-Nano Demo – Transmitter [Fritzing]

    So, for the Remote (Transmitter/Controller) Board, the GPIO Pin Wiring looks like this:

    GPIO PinDirectionConnects to…
    D2OutputRed LED via 330Ohm resistor
    D3OutputGreen LED via 330Ohm resistor
    D4OutputBlue LED via 330Ohm resistor
    D5InputButton to control Red LED
    D6InputButton to control Green LED
    D7InputButton to control Red LED

    The Code

    Let’s quickly put together the code for this project, but – before we do – we are going to begin with an obligatory reminder about coding standards and principles.

    Examples are NOT the way you should write your code!

    Please note that, for the sake of keeping this article concise, these code examples will follow the common “Arduino IDE” approach of putting all of our code into a single source file (a Sketch in the context of the Arduino IDE).

    I want to preface this by stating that I do not condone developing code in this manner for real-world projects. Real-world code should always be written with the SOLID principles of development at its core.

    On to the code!

    For this project, we will be using the RF24 library. This Library is available in both the Arduino IDE, as well as PlatformIO.

    We develop and maintain our projects using PlatformIO.

    RGB Remote (Transmitter) Code

    #include <Arduino.h>
    
    #include <SPI.h>
    #include <RF24.h>
    
    #define PIN_LED_RED     2   // D2
    #define PIN_LED_GREEN   3   // D3
    #define PIN_LED_BLUE    4   // D4
    #define PIN_BTN_RED     5   // D5
    #define PIN_BTN_GREEN   6   // D6
    #define PIN_BTN_BLUE    7   // D7
    
    #define PIN_RF_CE       9   // D9 = Chip Enable (CE) for nRF24L01+
    #define PIN_RF_CS       10  // D10 = Chip Select (CS) for nRF24L01+
    
    #define CHANNEL_READ    987654321
    #define CHANNEL_WRITE   123456789
    
    /*
      RGBState
      We will Transmit and Receive this State data.
    */
    struct RGBState {
      bool Red;   // True if the Red button is Down
      bool Green; // True if the Green button is Down
      bool Blue;  // True if the Blue button is Down
    };
    
    RGBState currentState;
    RF24 radio(PIN_RF_CE, PIN_RF_CS);
    bool radioReady = false;
    
    void setup() {
      // Initialise Serial first
      Serial.begin(115200);
    
      // Pin modes
      pinMode(PIN_LED_RED, OUTPUT);
      pinMode(PIN_LED_GREEN, OUTPUT);
      pinMode(PIN_LED_BLUE, OUTPUT);
      pinMode(PIN_BTN_RED, INPUT);
      pinMode(PIN_BTN_GREEN, INPUT);
      pinMode(PIN_BTN_BLUE, INPUT);
    
      // Configure the Radio
      int failCount = 0;
    
      while (!radioReady && failCount < 10) {
        radioReady = radio.begin();
        if (!radioReady) {
          failCount++;
          Serial.print("Radio Initialisation Failed ");
          Serial.print(failCount);
          Serial.println(" times.");
        }
      }
    
      if (!radioReady) {
        Serial.println("Radio could not be initialised!");
        return;
      }
    
      radio.setDataRate(RF24_2MBPS); // Maximum Bandwidth
      radio.setPALevel(RF24_PA_MAX); // Maximum Range
    
      radio.setPayloadSize(sizeof(RGBState)); // Size of the Data we will Transmit
      radio.setAutoAck(true); // Allows us to check if the Transmission Succeeded
    
      // We technically don't need to open a Reading Pipe for a Transmit-Only Device!
      radio.openReadingPipe(1, CHANNEL_READ); // For Incoming Data
      // We need to open the Writing Pipe in order to Transmit data
      radio.openWritingPipe(CHANNEL_WRITE); // For Outgoing Data
    }
    
    inline void sendState() {
      if (!radio.write(&currentState, sizeof(RGBState))) {
        Serial.println("Transmission failed!"); // We output errors to Serial.
      }
    }
    
    void loop() {
      if (!radioReady) {
        digitalWrite(PIN_LED_RED, HIGH); // Turn the Red LED on to show failure
        return; // We cannot continue if the Radio isn't working!
      }
    
      // We need a LOCAL State so we can Compare later!
      RGBState newState;
      
      // Let's populate the LOCAL State
      newState.Red = digitalRead(PIN_BTN_RED);
      newState.Green = digitalRead(PIN_BTN_GREEN);
      newState.Blue = digitalRead(PIN_BTN_BLUE);
    
      // Now we see if anything changed
      if (
        newState.Red != currentState.Red ||
        newState.Green != currentState.Green ||
        newState.Blue != currentState.Blue
      ) {
        // Update the Current State
        currentState = newState;
        // Show the State on the Controller's LEDs
        digitalWrite(PIN_LED_RED, currentState.Red);
        digitalWrite(PIN_LED_GREEN, currentState.Green);
        digitalWrite(PIN_LED_BLUE, currentState.Blue);
    
        sendState(); // Send the State to all Receivers
      }
    }

    The example code (above) is fully-annotated, so I won’t conflate this article by repeating what has already been said. Simply read through the above code… it is pretty simple to understand.

    RGB Display (Receiver) Code

    #include <Arduino.h>
    
    #include <SPI.h>
    #include <RF24.h>
    
    #define PIN_LED_RED     2   // D2
    #define PIN_LED_GREEN   3   // D3
    #define PIN_LED_BLUE    4   // D4
    
    #define PIN_RF_CE       9   // D9 = Chip Enable (CE) for nRF24L01+
    #define PIN_RF_CS       10  // D10 = Chip Select (CS) for nRF24L01+
    
    #define CHANNEL_READ    123456789 // Opposite of the Remote code
    #define CHANNEL_WRITE   987654321 // Opposite of the Remote code
    
    /*
      RGBState
      We will Transmit and Receive this State data.
    */
    struct RGBState {
      bool Red;   // True if the Red button is Down
      bool Green; // True if the Green button is Down
      bool Blue;  // True if the Blue button is Down
    };
    
    RGBState currentState;
    RF24 radio(PIN_RF_CE, PIN_RF_CS);
    bool radioReady = false;
    
    void setup() {
      // Initialise Serial first
      Serial.begin(115200);
    
      // Pin modes
      pinMode(PIN_LED_RED, OUTPUT);
      pinMode(PIN_LED_GREEN, OUTPUT);
      pinMode(PIN_LED_BLUE, OUTPUT);
    
      // Configure the Radio
      int failCount = 0;
    
      while (!radioReady && failCount < 10) {
        radioReady = radio.begin();
        if (!radioReady) {
          failCount++;
          Serial.print("Radio Initialisation Failed ");
          Serial.print(failCount);
          Serial.println(" times.");
        }
      }
    
      if (!radioReady) {
        Serial.println("Radio could not be initialised!");
        return;
      }
    
      radio.setDataRate(RF24_2MBPS); // Maximum Bandwidth
      radio.setPALevel(RF24_PA_MAX); // Maximum Range
    
      radio.setPayloadSize(sizeof(RGBState)); // Size of the Data we will Transmit
      radio.setAutoAck(true); // Allows us to check if the Transmission Succeeded
    
      // We need to open the Reading Pipe in order to Receive data
      radio.openReadingPipe(1, CHANNEL_READ); // For Incoming Data
      // We don't need to open the Writing Pipe on a Receive-only Device
      radio.openWritingPipe(CHANNEL_WRITE); // For Outgoing Data
    
      radio.startListening(); // This starts the radio listening for incoming messages
    }
    
    void loop() {
      if (!radioReady) {
        digitalWrite(PIN_LED_RED, HIGH); // Turn the Red LED on to show failure
        return; // We cannot continue if the Radio isn't working!
      }
    
      if (!radio.available()) { return; } // No need to continue
    
      RGBState inboundState;
      radio.read(&inboundState, sizeof(RGBState));
    
      digitalWrite(PIN_LED_RED, inboundState.Red);
      digitalWrite(PIN_LED_GREEN, inboundState.Green);
      digitalWrite(PIN_LED_BLUE, inboundState.Blue);
    }

    Again, the above code is heavily-annotated to describe in sequence what is happening. You simply need to read through it carefully.

    What I will point out is that the main differences are at the end of the setup() and loop() methods, which deal with the specifics of receiving rather than transmitting.

    Grab the Sample Code on GitHub

    Flowduino has a GitHub account specifically to provide complete sample projects accompanying the articles on this website.

    The complete Sample Projects for this article can be found here.

    Running the Demo

    RF-Nano RGB Remote/Display Demo in use
    RF-Nano RGB Remote/Display Demo in use

    So, we now have a Remote (Transmitter) and an RGB Display (Receiver). Whenever you press one of the buttons representing a colour (Red, Green, and Blue – respectively) the lights will simultaneously illuminate on both devices.

    If you press multiple buttons, you will combine colours… which is pretty fun.

    What’s important to note is that we are transmitting multiple values in a structured object. We can transmit basically any kind of data we want this way.

    The Price

    The RF-Nano is what I would call “competitively-priced”. While there are certainly cheaper Arduino Nano R3 clones on the market, these don’t feature the integrated radio. Once the cost of the radio module is factored in, the RF-Nano ultimately comes in at approximately the same overall price.

    When you consider the reduced complexity, potential time-savings when it comes to fault-finding and troubleshooting, the price becomes a bargain.

    In Conclusion…

    The RF-Nano is an extremely useful development board, which eliminates the potential fault-finding effort we have to endure when a device-to-device project starts experiencing communication problems.

    Since the radio is integrated directly into the development board, we can rule out radio hardware issues entirely, and focus on the software possibilities.

    The overall quality is impressively high, and easy to use.

    If you’re using an Arduino with the ATmega328 microcontroller, and you want to add a low-latency, high-frequency radio… swap out your dev board for the RF-Nano. It’s pin-compatible; albeit you can’t attach anything to the SPI pins, as explained previously – but an external radio would have to occupy these pins anyway.

    The Photographs in this Article…

    RF-Nano Arrangement Shot by Flowduino
    Gratuitous photo by Flowduino, showing 4 RF-Nano development boards “artfully” arranged.

    We just want to take a moment to point out that every single photograph of the RF-Nano used in this article has been taken by the Simon, of Flowduino, whom also authored this article.

    The images were all taken, and – where applicable – edited, on a mobile phone.

    We hope you like them… and if you do decide to use them on your own publications (online or physical) we would very-much appreciate it if you would provide accreditation. Something like “Photo by Flowduino.com” would be perfect. Permission for use elsewhere is explicitly granted under that sole condition.

    Simon Stuart
    Simon Stuarthttp://flowduino.com
    Simon has been creating digital things for the last 30 years. Whether it be video games, complex systems for the aerospace and automotive industries, workflow management systems for data-intensive and manufacturing industries, or specialised electronics devices... Simon has tinkered with it, personally and professionally.
    RELATED ARTICLES

    LEAVE A REPLY

    Please enter your comment!
    Please enter your name here

    Discuss/Discord

    Want to discuss this article in more detail? Why not join our community, including our authors, on Discord? You will find a community of like-minded Makers, where you can ask questions, make suggestions, and show off your own projects.

    Overall, the RF-Nano is a well-built, pin-compatible (with the Nano R3), easy-to-use, and extremely useful Development Board. I highly recommend this development board for any projects where you would normally use a Nano R3, but which require low-latency, high-frequency communication with the potential for long-range (with an SMA antenna connected). Just be aware of the error in the documentation mentioned in this "Reviewtorial," lest you waste countless hours trying to figure out why the radio isn't sending or receiving anything.Arduino Nano R3 + nRF24L01 = RF-Nano