DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
What's in store for DevOps in 2023? Hear from the experts in our "DZone 2023 Preview: DevOps Edition" on Fri, Jan 27!
Save your seat
  1. DZone
  2. Coding
  3. JavaScript
  4. RF Communication Using nrf24l01 Modules and Node.js

RF Communication Using nrf24l01 Modules and Node.js

When it comes to communicating with your boards, radio is an option. Setting up RF communication between Arduinos and Raspberry Pis just takes a handy Node.js addon.

Sony Arouje user avatar by
Sony Arouje
·
Feb. 07, 17 · Tutorial
Like (1)
Save
Tweet
Share
6.42K Views

Join the DZone community and get the full member experience.

Join For Free

Recently, I started experimenting with radio communication with low cost nrf24L01 modules. These modules are very cheap compared to XBee modules, which I used earlier. With these nrf24 modules, we can enable wireless communication between Arduinos and Raspberry Pis very effectively and economically. For my experiment, I used two nrf24 modules, one connected to an Arduino Uno and another to a Raspberry Pi 1. Here are the pin connection details

Seq

NRF24L01

RPi

Arduino Uno

1

GND

25 (GND)

GND

2

VCC

17 (3.3v)

3.3v

3

CE

15

7

4

CSN

24

8

5

SCK

23

13

6

MOSI

19

11

7

MISO

21

12

8

IRQ

–

–


To the communication, I used the RF24Network library, which is very good and has good documentation. Also, it comes with examples for both Arduino and RPi. I didn’t write any code — I just used the examples and was able to see the communication working. Initially, I had some trouble, but by the end, everything worked well. I could see the data coming from the Arduino to my Pi.

My intention was to use these modules in my RPi and write code in Node.js. Unfortunately, there is no Node.js support for this library. So last night, I decided to write a Node.js addon for this C/C++ library. I didn’t have any experience in writing a Node.js addon, I just spent an hour understanding the Nan and creating very simple addons. Then I started writing the addon for RF24Network. This was the hard part — definitely harder than a simple hello world addon.

Node-gyp kept on failing when it tried to compile the RFNetwork modules. In my searches, I realized that node-gyp uses the make utility, and I needed to add the C/C++ files from that library. In the end, I could compile the node addon. See the binding.gyp file

{
    "targets": [
        {
            "target_name": "nrf24Node",
            "sources": [ 
                "nrf24Node.cc",
                "RF24/RF24.cpp", 
                "RF24/utility/RPi/spi.cpp",
                "RF24/utility/RPi/bcm2835.c",
                "RF24/utility/RPi/interrupt.cpp",
                "RF24Network/RF24Network.cpp",
                "RF24Network/Sync.cpp"
            ],
            "include_dirs": [
                "<!(node -e \"require('nan')\")",
                "RF24Network",
                "RF24"
            ],
            "link_settings": {
                "libraries": [
                    "-RF24",
                    "-RFNetwork"
                ]
            }   
        }
    ]
}

I should say that I am just a beginner at node-gyp and this binding.gyp might need some improvements. Anyway, with this GYP file, the compilation succeeded.

Next was creating the addon file. Here, I had to learn more about the data types of Nan and callbacks. I started with simple functions and compiled again, then moved onto the next one. I took more time in understanding callbacks, which allows the addon to call JavaScript callback functions. I also spent a lot of time understanding threading and creating a module for continuous listening of incoming messages and triggering the callback function — so that Node.js can process those incoming messages. I use libuv for threading. It seems easier to understand than Async worker modules in Nan.

I spent that whole night learning and writing and refactoring the addon. I finished the module by early morning. By that time, I could write a Node.js app and could listen to incoming messages.

Here is the sample code in Node.js to listen to and acknowledge the message back to the sender.

var rf24 = require('./build/Release/nrf24Node.node');
rf24.begin(90,00);
rf24.printDetails();
rf24.write(1,"Ack");

rf24.readAsync(function(from, data){
    console.log(from);
    console.log(data);
    rf24.write(from,"Ack");
});


process.on('SIGINT', exitHandler);

function exitHandler() {
    process.exit();
    rf24.close();
}

Here is the complete addon. The code is uploaded to GitHub with the steps to compile and use it in your own Node.js applications.

#include <nan.h>
#include <v8.h>
#include <RF24.h>
#include <RF24Network.h>
#include <iostream>
#include <ctime>
#include <stdio.h>
#include <time.h>
#include <string>
using namespace Nan;
using namespace v8;

RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_8MHZ);
RF24Network network(radio);

Nan::Callback *cbPeriodic;
uv_async_t* async;

struct payload_t {                  // Structure of our payload
  char msg[24];
};

struct payload_pi {                  
  uint16_t fromNode;
  char msg[24];
};

//--------------------------------------------------------------------------
//Below functions are just replica of RF24Network functions.
//No need to use these functions in you app.
NAN_METHOD(BeginRadio) {
  radio.begin();
}

NAN_METHOD(BeginNetwork){
  uint16_t channel = info[0]->Uint32Value();
  uint16_t thisNode = info[0]->Uint32Value();
  network.begin(channel,thisNode);
}

NAN_METHOD(Update) {
  network.update();
}

NAN_METHOD(Available) {
  v8::Local<v8::Boolean> status = Nan::New(network.available());
  info.GetReturnValue().Set(status);
}

NAN_METHOD(Read) {
  payload_t payload;
  RF24NetworkHeader header;
  network.read(header,&payload,sizeof(payload));
  info.GetReturnValue().Set(Nan::New(payload.msg).ToLocalChecked());
}
//--------------------------------------------------------------------------------

NAN_METHOD(Begin){
  if (info.Length() < 2)
      return Nan::ThrowTypeError("Should pass Channel and Node id");

  uint16_t channel = info[0]->Uint32Value();
  uint16_t thisNode = info[1]->Uint32Value();

    radio.begin();
    delay(5);
    network.begin(channel, thisNode);
}

NAN_METHOD(Write){
  if (info.Length() < 2)
      return Nan::ThrowTypeError("Should pass Receiver Node Id and Message");  

  uint16_t otherNode = info[0]->Uint32Value();
  v8::String::Utf8Value message(info[1]->ToString());
  std::string msg = std::string(*message);
  payload_t payload;
  strncpy(payload.msg, msg.c_str(),24);

  RF24NetworkHeader header(otherNode);
  bool ok = network.write(header,&payload, sizeof(payload));
  info.GetReturnValue().Set(ok);
}


void keepListen(void *arg) {
    while(1)
    {
        network.update();
        while (network.available()) { 
              RF24NetworkHeader header; 
               payload_t payload;
              network.read(header,&payload,sizeof(payload));

        payload_pi localPayload;
        localPayload.fromNode = header.from_node;
        strncpy(localPayload.msg, payload.msg, 24);
        async->data = (void *) &localPayload;
        uv_async_send(async);
        }
        delay(2000);
    }
}

void doCallback(uv_async_t *handle){
  payload_pi* p = (struct payload_pi*)handle->data;
  v8::Handle<v8::Value> argv[2] = {
      Nan::New(p->fromNode),
      Nan::New(p->msg).ToLocalChecked()
    };
  cbPeriodic->Call(2, argv);
}  

NAN_METHOD(ReadAsync){
  if (info.Length() <= 0)
    return Nan::ThrowTypeError("Should pass a callback function");
  if (info.Length() > 0 && !info[0]->IsFunction())
      return Nan::ThrowTypeError("Provided callback must be a function");

  cbPeriodic = new Nan::Callback(info[0].As<Function>());
  async = (uv_async_t*)malloc(sizeof(uv_async_t));
  uv_async_init(uv_default_loop(), async, doCallback);
  uv_thread_t id;
  uv_thread_create(&id, keepListen, NULL);
  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}

NAN_METHOD(PrintDetails) {
  radio.printDetails();
}

NAN_METHOD(Close){
  uv_close((uv_handle_t*) &async, NULL);
}


NAN_MODULE_INIT(Init){
    Nan::Set(target, New<String>("beginRadio").ToLocalChecked(),
        GetFunction(New<FunctionTemplate>(BeginRadio)).ToLocalChecked());

    Nan::Set(target, New<String>("beginNetwork").ToLocalChecked(),
        GetFunction(New<FunctionTemplate>(BeginNetwork)).ToLocalChecked());

    Nan::Set(target, New<String>("update").ToLocalChecked(),
        GetFunction(New<FunctionTemplate>(Update)).ToLocalChecked());

    Nan::Set(target, New<String>("printDetails").ToLocalChecked(),
        GetFunction(New<FunctionTemplate>(PrintDetails)).ToLocalChecked());

    Nan::Set(target, New<String>("available").ToLocalChecked(),
        GetFunction(New<FunctionTemplate>(Available)).ToLocalChecked());

    Nan::Set(target, New<String>("read").ToLocalChecked(),
        GetFunction(New<FunctionTemplate>(Read)).ToLocalChecked());         

    Nan::Set(target, New<String>("readAsync").ToLocalChecked(),
        GetFunction(New<FunctionTemplate>(ReadAsync)).ToLocalChecked());

    Nan::Set(target, New<String>("write").ToLocalChecked(),
        GetFunction(New<FunctionTemplate>(Write)).ToLocalChecked());        

    Nan::Set(target, New<String>("close").ToLocalChecked(),
        GetFunction(New<FunctionTemplate>(Close)).ToLocalChecked()); 

    Nan::Set(target, New<String>("begin").ToLocalChecked(),
        GetFunction(New<FunctionTemplate>(Begin)).ToLocalChecked());                           
}

NODE_MODULE(nrf24Node, Init)

All the credit goes to the developers of RF24 and RF24Network library, I just created an addon for the great library. Along the way I learned a lot and could finish the nodejs addon.

Node.js

Published at DZone with permission of Sony Arouje, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • What Was the Question Again, ChatGPT?
  • 7 Awesome Libraries for Java Unit and Integration Testing
  • API Design Patterns Review
  • Microservices Discovery With Eureka

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: