Saturday, April 30, 2016

Set-up a NodeJS environment on Intel Edison: all-the-pieces

Working with NodeJS is fun and effective, but… Nodejs new white pantone

Note: this is rather a long post !!!

Coding in JavaScript is easy, and you have many modules that you can use.

For example, I have soon discovered that there is a module for MQTT. With this module you can easily publish MQTT messages to topics and subscribe.

You have also two good libraries, allowing you from a NodeJS program to interface with HW and to read from sensors: MRAA and UPM.

 But to work in NodeJS on Intel Edison is NOT always easy and fun.

 What is the main reason? Well I think it all points to the fact that the Linux distribution (well, it is not actually a distribution) is Yocto.

The NodeJS version shipped with Intel Edison, even if you download the latest available version (very. 3) is 0.10

This is an old version, with many important bugs.

For example, I have discovered soon that using MQTT if you send frequently messages from the board to the MQTT broker (let’s say every 10 sec.) very soon the programs stops working after 10 min.

Some test have showed me that the problem can be solved upgrading Node. It is ok with 0.12 version.

But the upgrade of the Node environment is not easy.

When I talk about environment I mean:

  • NodeJS version
  • The XDK agent (to be able to upload new Node programs directly from Intel XDK to the board, and launch them from the XDK)
  • MRAA with Node bindings (add-ons)
  • UPM with Node Bindings

Actually, now there is not a simple way, and it take a lot of manual work. And as I see, there is not a single, clear document describing how to do it.

In this blog post, I want to document the steps I have undertaken to upgrade NodeJS on Edison Board with an environment fully working.

High level steps:

  1. Flash the board with the latest Yocto version
  2. Configure Edison
  3. Upgrade MRAA and UPM libraries
  4. Upgrade the XDK agent
  5. Download and build the NodeJS (4.2)
  6. Set-up MRAA
  7. Set-up UPM
  8. Install MQTT
  9. Test all together
Doing it for the first time requires half a day !
 

Flash the board with latest Yocto version.

The only way that always works for me is to use the flashall.sh script, from the command line.

Sometimes, I need to reboot my MAC, before to start the flashing procedure. This is needed to avoid that the flashing procedure timeout while waiting for the FTDI device to re-appear.

You download and unzip the distribution (Yocto Release 3.0), taken from here, 

https://downloadmirror.intel.com/25871/eng/iot-devkit-prof-dev-image-edison-20160315.zip

then launch from the command line flashily.sh and connect through USB cables when requested.

It should take about 10 min

Configure Edison

At this point, you need to have the Intel XDK installed on your MAC or Laptop.

Launch the XDK and connect to the Edison using the serial connection (you have still the board connected with the two cables).

From the command line interface, launch

configure_edison —password

configure_edison —name

configure_edison —wifi

to setup a password, the hostname and to connect to the WI-FI network.

Upgrade MRAA and UPM libraries

From XDK, launch, upgrade libraries. 

Upgrade lib

After, you can verify with opkg info 

opkg info mraa

Package: mraa

Version: 1.0.0

Provides: mraa-dev, mraa-dbg, mraa-doc

Replaces: mraa-dev, mraa-dbg, mraa-doc, libmraa, libmraa-dev, libmraa-doc

Conflicts: mraa-dev, mraa-dbg, mraa-doc

Status: install user installed

Architecture: i586

Installed-Time: 1462025597

opkg info upm

Package: upm

Version: 0.6.2

Depends: mraa (>= 0.9.1)

Provides: upm-dev, upm-dbg, upm-doc

Replaces: upm-dev, upm-dbg, upm-doc

Conflicts: upm-dev, upm-dbg, upm-doc

Status: install user installed

Architecture: i586

Installed-Time: 1462025678

Upgrade the XDK agent

Again, from the XDK IDE (see preceding image).

Download and build the NodeJS (4.2)

First of all, remember that the space is limited. Therefore, you need to download the NodeJS distribution and start the build in a directory where you have enough space.

I have decided to create a downloads directory under /home/root

There, you can put the tree obtained from the tarball, downloaded from the NodeJS site.

But, I have hit one (of the many) annoying problem: if you simply download (with wget) the tar.gz file and explode it with tar xvf, when you launch configure on Edison you get a very strange error: the configure script (that is a Python script) complains that it cannot find the nodedownload module.

Well, I have found that the problem is in the tar utility, that doesn’t extract all the files (strange, but this is the reason). Actually, when I have unzipped the tar.gz file on my MAC I have found that the file nodedownload.py is there.

Solution: unpack the tarball file on your MAC, and upload all the resulting tree of directories and files on the Edison.

It will take some time, but I have no idea of a better solution.

then, update the XDK agent, from the XDK

The link where I took the tarball is:

http://nodejs.org/dist/v4.2.0/node-v4.2.0.tar.gz

Then, go inside the node-v4.2.0 directory and execute

./configure

make

It will take about three hours !

make install

Then, you have

root@thunder10:/# /usr/local/bin/node -v

v4.2.0

Update the XDK agent

Then, update the XDK agent, from the XDK IDE.

Upgrade lib 

after, you should be able to upload a NodeJS application directly from XDK, connected to the board through WIFI, to the directory /node_app_list on the Edison board.

Setup mraa

Then, in the directory /node_app_list run

npm install mraa

After this, mraa is correctly linked to Node. In fact, the following blink code works

var mraa = require('mraa'); //require mraa
console.log('MRAA Version: ' + mraa.getVersion()); //write the mraa version to the Intel XDK console

var myOnboardLed = new mraa.Gpio(13); //LED hooked up to digital pin 13 
myOnboardLed.dir(mraa.DIR_OUT); //set the gpio direction to output
var ledState = true; //Boolean to hold the state of Led

periodicActivity(); //call the periodicActivity function

function periodicActivity()
{
myOnboardLed.write(ledState?1:0); 
ledState = !ledState; //invert the ledState
setTimeout(periodicActivity,200); 
}

Setup of UPM.

Under /home/root/downloads clone upm git repository

git clone https://github.com/intel-iot-devkit/upm.git

cd upm

mkdir build

cd build

 

cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr

After, to compile only upm_grove (and produce the right jsupm_grove module)

cd src/grove

make

make install

To test, I have used the Grove Light sensor, attached to A1. This is the code I have used to test:

var groveSensor = require('jsupm_grove');

// Create the light sensor object using AIO pin 0
var light = new groveSensor.GroveLight(1);

// Read the input and print both the raw value and a rough lux value,
// waiting one second between readings
function readLightSensorValue() {
console.log(light.name() + " raw value is " + light.raw_value() +
", which is roughly " + light.value() + " lux");
}
setInterval(readLightSensorValue, 1000);

and this is the output from the program:

Light Sensor raw value is 184, which is roughly 2 lux

Light Sensor raw value is 190, which is roughly 2 lux

Light Sensor raw value is 357, which is roughly 5 lux

Light Sensor raw value is 361, which is roughly 5 lux

It works fine under Node 4.2 !!!

Setup MQTT

Under /node_app_slot execute

npm install mqtt

and this is the code of the program that I have used to test that UPM and MQTT work under NodeJS 4.2

var mqtt = require('mqtt');

var groveSensor = require('jsupm_grove');

var MSG_TOPIC_NAME = 'sensors/SN3/msg';
var CONTROL_TOPIC_NAME = 'sensors/control/SN3';

var SENSOR_ID = 'SN3';
var BROKER_URL = 'mqtt://broker_host';
var SLEEP_TIME = 3000; // 3 seconds
var PIN_TEMP = 0;

// Create the temperature sensor object using AIO pin 0
var temp = new groveSensor.GroveTemp(PIN_TEMP);

// connect to the Broker
var mqtt_client = mqtt.connect(BROKER_URL, {'keepalive' : 60});

//
// define events and callback
//
mqtt_client.on('close', handle_mqtt_close);
mqtt_client.on('connect', handle_mqtt_connect);
mqtt_client.on('reconnect', handle_mqtt_reconnect);
mqtt_client.on('error', handle_mqtt_err);
mqtt_client.on('message', handle_messsage);

// the variable that will contain the object for the MQTT msg
// This is the format of the msg: {"id":"SN3","temp":"26"}
//
var msg = {};
var count = 0;

function handle_mqtt_connect()
{
console.log("MQTT Connect...");

mqtt_client.subscribe('CONTROL_TOPIC_NAME', handle_mqtt_subscribe);
}

function handle_mqtt_subscribe(err, granted)
{
console.log("MQTT Subscribe...");

if (err)
{
console.log(err);
}
}

function handle_mqtt_reconnect(err)
{
console.log("MQTT Reconnect...");

if (err)
{
console.log(err);
}
mqtt_client.subscribe('CONTROL_TOPIC_NAME', handle_mqtt_subscribe);
}

function handle_mqtt_err(err)
{
console.log("MQTT Error...");

if (err)
{
console.log(err);
}
}

function handle_mqtt_close()
{
console.log("MQTT Close...");
}

function after_publish()
{
// for now nothing...
}

function handle_messsage(topic, message, packet)
{
console.log('Message received!');

console.log('msg = ' + message.toString());
}

function readSensors()
{
count = count + 1;
console.log('***********************');
console.log("Iteration n. %d", count);

var rounded_temp = temp.value();

// build the object message
// that will be sent as a JSON message
msg.id = SENSOR_ID;
msg.temp = rounded_temp.toString();

// send with MQTT QOS = 1
if (rounded_temp)
mqtt_client.publish(MSG_TOPIC_NAME, JSON.stringify(msg), {'qos' : 1}, after_publish);

setTimeout(readSensors, SLEEP_TIME); 
}

readSensors();

MQTT messages are sent to a Mosquitto broker installed on a Raspberry PI 2, connected to the WIFI network.

To verify that messages are correctly sent and received from the Broker, I have used the Eclipse Paho GUI Client:

 MQTTresult 

 

No comments:

Post a Comment