Building Connected Things with Node.js, Johnny-Five, and Microsoft Azure

Sending Device-to-Cloud (D2C) Messages

In this lab, you will capture data from the ThingLabs Thingy and send messages from the device to the Cloud using the Azure IoT SDK for Node.js.

Table of Contents

Bill of Materials

For this lab you will need:

  1. An Azure IoT Hub (created in the preceeding lab).
  2. The ThingLabs Thingy™ (created in an earlier lab).

Building the Application

In this lab you will write a Node.js application that runs on a hub (your development machine) and collects data from a development board and sends it up to your Azure IoT Hub.

Define an Install the Application Metadata and Dependencies

Defining and installing the application metadata and dependencies works similarly to the preceeding labs.

{
  "name": "thingy",
  "repository": {
    "type": "git",
    "url": "https://github.com/ThingLabsIo/IoTLabs/tree/master/Edison/AzureIoT"
  },
  "version": "0.0.2",
  "description": "Sample app that connects a device to Azure using Node.js",
  "license": "MIT",
  "dependencies": {
    "johnny-five": "latest",
    "edison-io": "latest",
    "azure-iot-device": "latest",
    "azure-iot-device-amqp-ws": "latest"
  }
}

Full Code Example:

'use strict';
// Define the objects you will be working with
var five = require('johnny-five');
var Edison = require('edison-io');
var device = require('azure-iot-device');

// Define the client object that communicates with Azure IoT Hubs
var Client = require('azure-iot-device').Client;

// Define the message object that will define the message format going into Azure IoT Hubs
var Message = require('azure-iot-device').Message;

// Define the protocol that will be used to send messages to Azure IoT Hub
// For this lab we will use AMQP over Web Sockets. The usage of Web Sockets allows using
// AMQP also in environments where standard AMQP ports do not work (for example,
// because of network restrictions).
var Protocol = require('azure-iot-device-amqp-ws').AmqpWs;

// The device-specific connection string to your Azure IoT Hub
var connectionString = process.env.IOTHUB_DEVICE_CONN || 'YOUR IOT HUB DEVICE-SPECIFIC CONNECTION STRING HERE';

// Create the client instanxe that will manage the connection to your IoT Hub
// The client is created in the context of an Azure IoT device.
var client = Client.fromConnectionString(connectionString, Protocol);

// Extract the Azure IoT Hub device ID from the connection string
var deviceId = device.ConnectionString.parse(connectionString).DeviceId;

// location is simply a string that you can filter on later
var location = process.env.DEVICE_LOCATION || 'GIVE A NAME TO THE LOCATION OF THE THING';

// Define the sensors you will use
var button, led, lcd, temperature

// Define some variable for holding sensor values
var tempC, tempF, r, g, b = 0;

// Define the board, which is an abstraction of the Intel Edison
var board = new five.Board({
  io: new Edison()
});

// *********************************************
// Send a messae to Azure IoT Hub.
// Always send the same message format (to 
// ensure the StreamAnalytics job doesn't fail)
// includng deviceId, location and the sensor 
// type/value combination.
// *********************************************
function sendMessage(src, val){
    // Define the message body
    var payload = JSON.stringify({
        deviceId: deviceId,
        location: location,
        sensorType: src,
        sensorValue: val
    });
    
    // Create the message based on the payload JSON
    var message = new Message(payload);
    
    // For debugging purposes, write out the message payload to the console
    console.log('Sending message: ' + message.getData());
    
    // Send the message to Azure IoT Hub
    client.sendEvent(message, printResultFor('send'));
    
    console.log('- - - -');
}

// *********************************************
// Helper function to print results in the console
// *********************************************
function printResultFor(op) {
  return function printResult(err, res) {
    if (err) console.log(op + ' error: ' + err.toString());
    if (res) console.log(op + ' status: ' + res.constructor.name);
  };
}

// *********************************************
// Open the connection to Azure IoT Hub.
// When the connection respondes (either open or 
// error) the anonymous function is executed.
// *********************************************
var connectCallback = function (err) {
    console.log('Open Azure IoT connection...');
    
    // *********************************************
    // If there is a connection error, display it 
    // in the console.
    // *********************************************
    if(err) {
        console.error('...could not connect: ' + err);
        
    // *********************************************
    // If there is no error, send and receive
    // messages, and process completed messages.
    // *********************************************
    } else {
        console.log('...client connected');
        
        // *********************************************
        // Create a message and send it to the IoT Hub
        // every two-seconds
        // *********************************************
        var sendInterval = setInterval(function () {
            sendMessage('temperature', tempC);
        }, 2000);
        
        // *********************************************
        // Listen for incoming messages
        // *********************************************
        client.on('message', function (msg) {
            console.log('*********************************************');
            console.log('**** Message Received - Id: ' + msg.messageId + ' Body: ' + msg.data);
            console.log('*********************************************');

            var parsedData = JSON.parse(msg.data);
            // Toggle LED state based on the value received
            if (parsedData.ledState === 1) {
                led.on();
            } else {
                led.off();
            }

            // *********************************************
            // Process completed messages and remove them 
            // from the message queue.
            // *********************************************
            client.complete(msg, printResultFor('completed'));
            // reject and abandon follow the same pattern.
            // /!\ reject and abandon are not available with MQTT
        });
            
        // *********************************************
        // If the client gets an error, dsiplay it in
        // the console.
        // *********************************************
        client.on('error', function (err) {
            console.error(err.message);
        });
            
        // *********************************************
        // If the client gets disconnected, cleanup and
        // reconnect.
        // *********************************************
        client.on('disconnect', function () {
            clearInterval(sendInterval);
            client.removeAllListeners();
            client.connect(connectCallback);
        });
    }
}

// *********************************************
// The board.on() executes the anonymous
// function when the 'board' reports back that
// it is initialized and ready.
// *********************************************
board.on('ready', function() {
    console.log('Board connected...');
    
    // Plug the Temperature sensor module
    // into the Grove Shield's A0 jack
    // The controller defines the type of 
    // Temperature sensor this is.
    temperature = new five.Thermometer({
        controller: "GROVE",
        pin: "A0"
    });
    
    // Plug the LCD module into any of the
    // Grove Shield's I2C jacks.
    lcd = new five.LCD({
        controller: 'JHD1313M1'
    });
    
    // Plug the LED module into the
    // Grove Shield's D6 jack.
    led = new five.Led(6);
    
    // Plug the Button module into the
    // Grove Shield's D4 jack.
    button = new five.Button(4);
    
    // *********************************************
    // The thermometer object will invoke a callback
    // everytime it reads data as fast as every 25ms
    // or whatever the 'freq' argument is set to.
    // *********************************************
    temperature.on('data', function() {
        // Set the state of the variables based on the 
        // value read from the thermometer
        // 'this' scope is the thermometer
        tempC = this.celsius;
        tempF = this.fahrenheit;
        
        // Use a simple linear function to determine
        // the RGB color to paint the LCD screen.
        // The LCD's background will change color
        // according to the temperature.
        // Hot -> Moderate -> Cold
        // 122°F ->  77°F  -> 32°F
        // 50°C  ->  25°C  -> 0°C
        // Red ->  Violet  -> Blue
        r = linear(0x00, 0xFF, tempC, 50);
        g = linear(0x00, 0x00, tempC, 50);
        b = linear(0xFF, 0x00, tempC, 50);
        
        // Paint the LCD and print the temperture
        // (rounded up to the nearest whole integer)
        lcd.bgColor(r, g, b).cursor(0, 0).print('Fahrenheit: ' + Math.ceil(tempF));
        lcd.bgColor(r, g, b).cursor(1, 0).print('Celsius: ' + Math.ceil(tempC));
        
    });
    
    // *********************************************
    // The button.on('press') invokes the anonymous 
    // callback when the button is pressed.
    // *********************************************
    button.on('press', function() {
        led.on();
        console.log('*********************************************');
        sendMessage('led', 'on');
        console.log('*********************************************');
    });
    
    // *********************************************
    // The button.on('release') invokes the
    // anonymous callback when the button is
    // released.
    // *********************************************
    button.on('release', function() {
        led.off();
        console.log('*********************************************');
        sendMessage('led', 'off');
        console.log('*********************************************');
    });
    
    // *********************************************
    // Open the connection to Azure IoT Hubs and
    // begin sending messages.
    // *********************************************
    client.open(connectCallback);
});

// *********************************************
// Helper method for painting the LCD.
// Linear Interpolation
// (https://en.wikipedia.org/wiki/Linear_interpolation)
// *********************************************
function linear(start, end, step, steps) {
  return (end - start) * step / steps + start;
}

Run the Application

  1. Copy the application files to the Edison in the same way you have done in the preceeding labs.
  2. Install the application dependencies on the Edison (if you are using the same application directory as the preceeding labs, the dependencies should all be installed already).
  3. From the remote session (Command Prompt or Terminal), execute the following command in the application directory. If you changed the file name of the JavaScript file, use the correct name here.
node thingy.js

Once the application is running, you will see the temperature reading on the RGB LCD Display. You can press the button and see the LED light up (you will also see the PRESSED log message in the remote session). As long as the button is pressed, the LED will remain on. When you release the button, the button.on('release') callback is invoked and the LED turns off (you will also see the RELEASED log message).

Troubleshooting

The following troubleshooting tips may be helpful to you.

Azure Connection Errors

If you get connection errors when trying to connect to Azure IoT Hubs, it could be that the Edison operating system’s date is innacurate. The Edison does a date/time sync when it starts, but some network firewalls may block the Network Time Protocols (NTP). If you are seeing errors, ensure the date is correct by executing the following code:

date -s "$(curl -sD - google.com | grep '^Date:' | cut -d' ' -f3-6)Z"

Conclusion & Next Steps

Go to ‘Visualizing IoT Data’ ›