Interfacing Node.js to Raspberry Pi hardware like 433Mhz transmitters with node-ffi

Now that I can control the Efergy RC mains sockets easily with a DigiX Arduino, it's time to make the same thing work on Node.js running on a Raspberry Pi. Turns out it's not quite so easy!

Now that I can talk to the Efergy remote control switches using Arduino and DigiX, it’s time to do the same thing with Raspberry Pi and Node.js.

But first, check out this gorgeous Pimoroni Pibow Timber case that the RPi now lives in. Makes me smile every time I look at it.

Pibow

Start by plugging in one of the Efergy switches and using its remote control to set its code. Use the sniffer program from the last post to find out what that code is.

Connecting the 433MHz transmitter to the RPi is dead easy. Connect GND to pin 6, VCC to PIN 1 and DATA to PIN 11. More details on the WiringPi Pinout. For our purposes: BCM GPIO 17 == WiringPi 0 == Pin 11 == GPIO #0

The simplest way of doing this is to create a command-line program in C or C++ and then shell out to it in Node. But where’s the fun in that? The core approach I decided to use was via a Node module called Node FFI which makes it relatively easy to call out to C libraries from Node. The alternative is to build a Node module in C++. But that wasn’t going to work, given that I haven’t written a line of C++ in my life, after the trauma of doing a training course in it in the late 90s and seeing the horror inflicted on my beloved C.

Unfortunately I quickly realised that node-ffi only does C whilst the RCSWitch-Pi library is C++ (but the WiringPi library under that is C again!).

An abortive attempt at using ffi-generate went nowhere as it wouldn’t even run on the RPi due to some libclang problem. Running in an Ubuntu VM worked after a lot of llvm messing but I couldn’t make any sense of the output.

Cue a lonnnngggg session Googling how to interface C to C++ and realising that I hate C++ even more now :-) Eventually I found an idiot’s guide to wrapping C++ so it can be called by C. Bit by bit I figured it all out but hit a brick wall at one point. This forced me to use the GDB debugger for the first time in 15 years. Luckily I spotted the problem using GDB in about a minute flat and got a test C program working which would call the C++ functions correctly.

Then it was back to Node.js and node-ffi for another round of head scratching and non-stop errors. Finally it all clicked and the Efergy module clicked too!

Now that I have the basics working, I will clean up this code and use it in the Node server that will run on the RPi via the usual exports/require approach. I’ll also hopefully be able to apply the same approach to any C++ or C library that accesses any RPi hardware.

The final set of steps are actually quite simple. All the effort was in the learning.

Install WiringPi

mkdir ~/gitwork
cd gitwork
git clone git://git.drogon.net/wiringPi
cd wiringPi
./build

### Install RCSwitch-Pi
cd ~/gitwork
git clone https://github.com/r10r/rcswitch-pi.git
cd rcswitch-pi
make

Build an RCSwitch-Pi library

gcc -shared -fpic RCSwitch.cpp -o libRCSwitch.so

Simple C++ test to make sure you can talk to Efergy

cd ~/gitwork/rcswitch-pi

Create a file called efergy.cpp with the following contents

#include "RCSwitch.h"
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[]) {

    /*
     output PIN is hardcoded for testing purposes
     see https://projects.drogon.net/raspberry-pi/wiringpi/pins/
     for pin mapping of the raspberry pi GPIO connector
     */
    int PIN = 0;
    int unitCode = atoi(argv[1]);

    if (wiringPiSetup () == -1) return 1;
        printf("sending unitCode[%i]\n", unitCode);
        RCSwitch mySwitch = RCSwitch();
        mySwitch.enableTransmit(PIN);

        mySwitch.send(unitCode,24);

        return 0;
}

Now compile and run it:

g++ -c -o efergy.o efergy.cpp
g++ RCSwitch.o efergy.o -o efergy -lwiringPi
sudo ./efergy 109011
sudo ./efergy 109019

If all is working ok, the switch will turn on and off.

Install Node/NPM on Raspberry Pi

    cd ~
    sudo mkdir /opt/node
    wget http://nodejs.org/dist/v0.10.23/node-v0.10.23-linux-arm-pi.tar.gz
    tar xvzf node-v0.10.23-linux-arm-pi.tar.gz
    sudo cp -r node-v0.10.2-linux-arm-pi/* /opt/node
    cd ~
    nano .bash_profile
     
    #Add these lines to the file you opened
    PATH=$PATH:/opt/node/bin
    export PATH

    #Save and exit
     
    #Test
    node -v
    npm -v

Wrapping C++ so it can be called by C

Create wrapper.h and wrapper.c

wrapper.h:

#ifndef __MYWRAPPER_H
#define __MYWRAPPER_H

#ifdef __cplusplus
extern "C" {
#endif

  typedef struct RCSwitch RCSwitch;

  RCSwitch* newRCSwitch();

  void RCSwitch_send(RCSwitch* v, unsigned long Code, unsigned int length);

  void RCSwitch_enableTransmit(RCSwitch* v, int nTransmitterPin);

  void deleteRCSwitch(RCSwitch* v);

#ifdef __cplusplus
}
#endif
#endif

wrapper.c:


    #include "RCSwitch.h"
    #include "wrapper.h"
    #include <stdio.h>

    extern "C" {
      RCSwitch* newRCSwitch() {
        //printf("Inside newRCSwitch");
        return new RCSwitch();
      }

      void RCSwitch_send(RCSwitch* v, unsigned long Code, unsigned int length) {
        //printf("Inside RCSwitch_send");
        v->send(Code, length);
      }

      void RCSwitch_enableTransmit(RCSwitch* v, int nTransmitterPin) {
        //printf("Inside RCSwitch_enableTransmit");
        v->enableTransmit(nTransmitterPin);
      }

      void deleteRCSwitch(RCSwitch* v) {
        delete v;
      }
    }

Compile everything

Use -g flag everywhere if you want to debug with GDB (gdb efergy2, break main, run 109011, next, next). Simple GDB Tutorial here and some tips on RPi Forums.

g++ -g -c wrapper.c -o wrapper.o
gcc -g -c efergy.c -o efergy.o
g++ -shared -fpic -g RCSwitch.cpp -L ./ -l wiringPi -o libRCSwitch.so
g++ -g efergy.o wrapper.o ./libRCSwitch.so -o efergy2

Run the C program

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
sudo ./efergy2 109011

Now to get everything running in Node/JavaScript

Install Node FFI

sudo npm install -g ffi

Create send.js

var ffi = require("ffi")

var libwiringPi = ffi.Library('/usr/local/lib/libwiringPi', {
    'wiringPiSetup' : [ 'int', [] ],
    'digitalWrite' : [ 'void', ['int', 'int'] ],
    'pinMode': [ 'void', ['int', 'int'] ],
    'delayMicroseconds' :  [ 'void', ['int', 'int'] ]
})

var libRCSwitch = ffi.Library('./libwrapper', {
  'newRCSwitch' : ['pointer', [] ],
  'RCSwitch_send': ['void', [ 'pointer', 'int', 'int' ] ],
  'RCSwitch_enableTransmit': ['void', ['pointer', 'int'] ]
})

if (process.argv.length < 3) {
  console.log('Arguments: Efergy Switch Code')
  process.exit()
}

var PIN = 0;
var unitCode = parseInt(process.argv[2]);
var mySwitch = libRCSwitch.newRCSwitch();

if (libwiringPi.wiringPiSetup() == -1){
    return 1;
    printf("Error initialising WiringPi");
}

if (mySwitch.isNull()) {
    console.log("Oh no! Couldn't create object!\n");
} else {
    libRCSwitch.RCSwitch_enableTransmit(mySwitch, PIN);
    libRCSwitch.RCSwitch_send(mySwitch, unitCode, 24);
}

Compile the libraries

g++ -shared -fpic -g wrapper.c -L ./ -l RCSwitch -o libwrapper.so
g++ -shared -fpic -g RCSwitch.cpp -L ./ -l wiringPi -o libRCSwitch.so

Run send.js

Note that anything which uses WiringPi has to be run sudo. A bit of a pain. Not sure I want to be running the Node process as root?

sudo env PATH=$PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./ node send.js 109011

That’s it. You can now turn on and off your Efergy socket using Node. Next up, a simple Node server to handle remote requests to do this.

Conor O'Neill

Tech guy who likes running slowly

Bandon, Cork, Ireland https://conoroneill.net