Envio de mensajes al IoT Hub con NodeJs

En nuestros días el término de IOT (internet de las cosas) está teniendo mayor relevancia de lo esperado y se está convirtiendo en parte fundamental para el desarrollo de futuras tecnologías que puedan aportar o dar valor agregado a las cosas que nos hacen fácil la vida. El internet de las cosas nos ayudará a avanzar en la capacidad de recopilar, analizar y distribuir datos que se pueden a futuro convertir en información, conocimiento y porque no en sabiduría. Al día de hoy, ya se están llevando a cabo proyectos relacionados con el IoT que prometen entre otras cosas mejorar la distribución de los recursos del mundo y ayudarnos a ser más productivos.

Uno de los usos de sensores se lleva a cabo a través de Raspberry PI, esta es una placa computadora (SBC) de bajo costo, se podría decir que es un computador de tamaño reducido, del orden de una tarjeta de crédito, Este proyecto fue ideado en 2006 pero no fue lanzado al mercado Hasta febrero de 2012. Ha sido desarrollado por un grupo de la Universidad de Cambridge y su misión era fomentar la enseñanza a los niños. De hecho, en enero de este año Google donó más de 15.000 Raspberry Pi para colegios en Reino Unido. La Raspberry Pi, es una excelente herramienta para aprender electrónica y programación. Los primeros diseños de Raspberry Pi se basaban en el microcontrolador Atmel ATmega644. Sus esquemas y el diseño del circuito impreso están disponibles para su descarga pública.
Debido a su funcionamiento y respuesta que ha obtenido por parte del público, la organización Raspberry PI ha decidido crear un kit oficial que contendrá todo lo necesario para que cualquier usuario pueda trabajar con Raspberry Pi desde el primer momento.

Uno de los pilares más importantes y en que la Raspberry Pi está siendo parte fundamental es en el desarrollo y diseños de IoT o para objetos que usen IoT. El internet de las cosas es el concepto que está moviendo la evolución de la tecnología en el mundo, ahora ya no es tan imposible pensar que con tan solo un celular se puedan manipular las puertas, las ventanas, y las luces del hogar y que con solo un dispositivo como una raspberry que es de fácil manejo y acceso, se vuelve más atractivo para el consumidor.

La aplicación de la IoT se encuentra en varios sectores, la construcción, la agricultura, la salud, el transporte entre otros, entonces ¿por qué IoT y node js?
Dispositivos como sensores tienen tendencia a generar gran volumen de datos y así generar un gran número de solicitudes, el node js suministra estas peticiones a través de flujos.
Si de adquirir los datos en tiempo real se trata node js es conocido por su velocidad, eficiencia y escalabilidad como el participante clave para la aplicación de datos en tiempo real.
Node js viene con poder de JavaScript que es bastante sencillo de aprender y entender.
Básicamente node es oportuno cuando necesitas realizar muchas cosas al mismo tiempo como operaciones I/O a la vez (acceso a base de datos, ficheros entre otros).
Permite utilizar JavaScript tanto en el cliente como en el servidor.

A continuación indicaremos el funcionamiento de un proyecto con sensores de temperatura y humedad relativa donde capturamos sus datos en tiempo real, los enviamos al IoT hub de azure y por medio de un servicio de Windows guardamos en base de datos estas tomas.
Utilizamos una raspberry pi 3.
La referencia de los sensores utilizados es:
Bmp180.
Dht22.

Archivos en Visual Studio Code. JavaScript del lado del servidor.

Módulos instalados:
Estos son los módulos de NodeJs, ya sean propios o instalados, se encuentran los módulos utilizados para la comunicación con el IoT Hub de Azure.
Cylon es utilizada para temperatura.
El i2c es para el sensor BMP180.
Express es para el servidor.
Graceful-fs es para escritura en archivos planos.
Socket.io canal de comunicación entre cliente servidor para envío de datos asincrónicamente.

Sensor BMP180

El EventEmitter es una clase maestra, cuando se desarrollan los módulos propios estos deben heredar, así se pueden emitir eventos.

function BMP180(){  
    EventEmitter.call(this);

InitRobot es iniciar el robot, se utiliza ya que cylon es una librería de robotica y cuenta con un robot de temperatura y asi seria la estructura del robot.
Sigue la configuración que se utiliza rasberry, y un sensor BMP180, esto es lo que pide el robot.

function initRobotBMP(){

    if(!readyRobotBMP180){
        readyRobotBMP180 = false;
        robot = Cylon.robot({
        connections: {
            raspi: { adaptor: 'raspi' }
        },

        devices: {
            bmp180: { driver: 'bmp180' }
        },

        work: function(device) {
            bmp180device = device;
            readyRobotBMP180 = true;
            console.log("Inicializado el robot de tempertura");
        }
        }).start(); 
    }
}

Lectura del sensor de temperatura BMP180:
La variable self toma el contexto de esa misma clase a través de la instancia this, quiere decir que en ese método perteneciente a esta va a emitir eventos, ejm para emitir temperatura se llamó a readytemp, y la variable temperatura seria el argumento.

BMP180.prototype.readDevice = function (){  
    var self = this;
    if(bmp180device){
        bmp180device.bmp180.start(function (){
            bmp180device.bmp180.getTemperature(function(err, val) {
                if (err) {
                    self.emit("errorTemp",err);
                    return;
                }
                var temperatura = val.temp;

                self.emit("readyTemp",temperatura);

            });
        });         
    }else{
        self.emit("errRob","No inicializado el robot de tempertura");
    }
}

Estado del robot de temperatura con el sensor BMP180:

BMP180.prototype.readyRobotBMP180 = function(){  
    return readyRobotBMP180;
}
module.exports.BMP180 = BMP180;  

Sensor DHT22:

Se realiza el constructor y la herencia con el EventEmitter.

function DHT(){  
    EventEmitter.call(this);
}
//Herencia para eventos
util.inherits(DHT, EventEmitter);  

Se configura el sensor DHT y el pin de lectura de la temperatura y la humedad relativa.

DHT.prototype.configurar = function(pin, tipo){  
    tipoSensor = tipo;
    pinLectura = pin;
    console.log("Inicializado sensor DHT"+tipoSensor+" en PIN "+pinLectura);
}

Lectura del sensor DHT datos de temperatura y humedad relativa.

DHT.prototype.read = function(){  
    var self = this;
    sensorDHT.read(tipoSensor, pinLectura, function(err, temperature, humidity) {
        if (!err) {
            self.emit("readyTempAm",temperature.toFixed(1));
            self.emit("readyHumAm",humidity.toFixed(1));
        }else{
          self.emit("errTHAm",err);
        }

    });
}

module.exports.DHT = DHT;  

Azure IoT Hub:

Módulos internos, el require (‘azure-iot-device-mqtt’) es usado para el envío de mensajes a Azure, y la instancia del cliente es clientFromConnectionString.
El require (‘azure-iot-common’) se usa para arman los mensajes.

var clientFromConnectionString = require('azure-iot-device-mqtt').clientFromConnectionString;  
var util = require("util"),EventEmitter = require("events").EventEmitter;  
var dns = require('dns');  
var Message = require('azure-iot-common').Message;  

El constructor:

function IoTHub(){  
    EventEmitter.call(this);

Creación del cliente Azure IoT Hub:
Se crea el cliente de Azure, es decir se instancia un cliente.

IoTHub.prototype.createClient  = function (connectionString,linktestI){  
    linkTestInternet = linktestI;
    client = clientFromConnectionString(connectionString);
}

Se abre una conexión, se abre el canal como tal.

IoTHub.prototype.openConnection = function(){  
    self = this;
     client.open(function(err){  
         if (err) {
            self.emit("errIoT",err);
        } else {
            self.emit("readyIoT","Client azure connected");
        }

    });
}

Envío de mensajes:
El SendEvent es el encargado del envío de mensajes.
El fulfill es para esa promesa, es una clase, en esa clase se está ejecutando un código entonces cuando ese código termina con ese fulfill retorna, sale de la promesa.

IoTHub.prototype.sendEvent = function(data){  
    self = this;
    return new Promise(function(fulfill,reject){

        var msg = new Message(data);
        client.sendEvent(msg, function (err) {
          if (err) {
            self.emit("errIoT",err);
            fulfill({valor:"Error"});       
          } else {
            self.emit("readySendIoT","Message sent");
            fulfill({valor:"ok"});      
          };
        }); 

        waitSeg(10,function(){
            fulfill({valor:"Timeout"});     
        });
    });

}

Función de timeout:

function waitSeg(timeWait,callback){  
  var N=0;
  var hilo = setInterval(function(){
    if(N>=timeWait){
      callback();
      clearInterval(hilo);
    }
    N++;
  },1000);

}

module.exports.IoTHub = IoTHub;  

Main:

Modulo principal para gestión y configuración de parámetros para lectura de temperatura y humedad relativa .Eventos relacionados.

//Modulos internos
var gpio = require("pi-gpio");  
const fs = require('graceful-fs');  
const co = require('co');  
//Modulos propios 
var controSens = require('./ControlTemperApp').init();  
var BMP180Cy = require('./BMP180Cylon').init();  
var TCA9548Am = require('./TCA9548A').init();  
var DHTm = require('./DHT').init();  
var IoTHubm = require('./IoTHub').init();  
var Servm = require("./Server").init();  
var config = require('./config.json');  

Arreglos de configuración para temperatura del sensor BMP180, y la temperatura y humedad relativa del sensor DHT.

var temperatura = config.dispositivos.temperatura.list;  
var tempAmb = config.dispositivos.tempAmb.list;  
var humAmb = config.dispositivos.humAmb.list;  

Cuando se emita el evento se captura el readyTemp, la temperatura del sensor BMP180.

BMP180Cy.on("readyTemp",function(tempera){  
    var device = temperatura[numChan*indexMux+indexChan];
    if(device){
        controSens.setTemperatura(tempera,device.nombre);
    }
    console.log("Mux:["+indexMux+"]-Canal:["+indexChan+"] -> "+tempera+" °C");
    if(numMux !== 0){next(false,readChanel);}
});

Lo mismo sucede con el sensor DHT cuando se emita el evento se captura la temperatura y la humedad.

//Event 
DHTm.on("readyTempAm",function(tempAm){  
    if(tempAmb[0]){
        controSens.setTemperaturaAmb(tempAm,tempAmb[0].nombre);
    }
    console.log("TemperaturaAm: "+tempAm + " °C");
});

//Event 
DHTm.on("readyHumAm",function(humAm){  
    if(humAmb[0]){
        controSens.setHumedadAmb(humAm,humAmb[0].nombre);
    }
    console.log("HumedadAm: "+humAm + "%");
});

Lectura del sensor DHT

function readDht(){  
    if(tempAmb[0] && humAmb[0]){
        DHTm.read();

Lectura del sensor BMP180

function readOnlyBPM(){  
    if(temperatura[0] != null){
        if(statusRobot){
            BMP180Cy.readDevice();
        }else{
            initRobot();
        }
    }
}

ejecución de cada periodo revisando el estado de los procesos.

every((periodo).seconds(),function(){

    switch(status){
        case "start":
            status = "run";
            Servm.startserver(config.puerto);
            DHTm.configurar(config.pindht,config.tipodht);
            setInterval(readDht,lectsens);
            IoTHubm.createClient(config.conectionString,config.linktestinternet);
            IoTHubm.openConnection();
            if(numMux === 0){
                initRobot();
                setInterval(readOnlyBPM,lectsens);
            }else{
                setInterval(reinti2c,bmpreint);
                initGPIO(rst,"output",function(){
                    writeGPIO(rst,1,function(){
                      initRobot();
                      if(statusRobot)
                        readChanel();
                    });
                });
            }
        break;      
        case "reint":
            status = "run";
            if(numMux !== 0){
                next(false,function(){
                   initRobot();      
                   if(statusRobot)
                     readChanel();
                });
            }
        break;      
    }   
});

Visualización de datos: modulo del servidor donde se instancia un servidor local.

//Modulos internos
var util = require("util"),EventEmitter = require("events").EventEmitter;  
var express = require('express');  
var app = express();  
var http = require('http').Server(app);  
var io = require('socket.io')(http);

//Inicializador del modulo
exports.init = function (){  
    return new Server();
}
//Constructor
function Server(){  
    EventEmitter.call(this);
}
//Herencia para eventos
util.inherits(Server, EventEmitter);  

Inicia el servidor, se coloca a escuchar el puerto e inicio de eventos para conexión de usuarios.

Server.prototype.startserver = function(port){  
  var self = this;
  http.listen(port, function(){
    console.log('listening on *: '+port);
  });
  //
  io.on('connection', function(socket){
    //console.log('a user connected');
    self.emit("userConnect","Usuario conectado");
    socket.on("messages-serv",function(data){
      //console.log("Dato desde navegador: "+data.index);
      self.emit("userChange",data);
    });
  });
}

Se tiene una configuración con Id para el envío de mensajes al IoT Hub Azure.

{
 "intervalo":30000,
 "puerto":3000,
 "nummuxes":0,
 "pinresetmux":11,
 "pindht":4,
 "tipodht":22,
 "tiemporefresh":2000,
 "linktestinternet":"www.google.com",
 "conectionString":"HostName=xxxx.azure-devices.net;DeviceId=xxxx;SharedAccessKey=xxxxx",
 "dispositivos":{
    "temperatura":{
       "list":[
            {
            "nombre":"BMP180",
            "id":41,
            "intervalo":30
            }
        ]
       },
    "tempAmb":{
          "list":[
              {
            "nombre":"AmbDHT22",
            "id":42,
            "intervalo":30
            }
        ]
     },
    "humAmb":{
          "list":[
                      {
            "nombre":"HumDHT22",
            "id":43,
            "intervalo":30
            }
        ]
     }
 }
}

Módulos del lado del cliente:
El html

<!DOCTYPE html>  
<html lang="en">  
<head>  
  <meta charset="UTF-8">
  <title>TempSense</title>
   <link rel="stylesheet" type="text/css" href="/WWW/Styles/index.css">
  <script src="/socket.io/socket.io.js"></script>
  <script src="/WWW/Scripts/index.js"></script>
</head>  
<body>  
  <div>
    <div>
      <h3 id= "hid">Temperatura</h3>
      <div class="contenedor" id="Temp"></div> 
      <div align="center">
        <button id="izquierda" type="button" onclick="back()">&lt;</button>
        <button id="derecha" type="button" onclick="next()">&gt;</button>
      </div>
    </div>
    <div>
      <h3>Temperatura Ambiente</h3>
      <div class="contenedor" id="TempA">
    </div> 
    <div>
      <h3>Humedad Ambiente</h3>
      <div class="contenedor" id="HumA">
      </div> 
    </div>
  </div>
</body>  
</html>  

Codigo javascript para gestionar el socket del lado del cliente.

//Gestor de Socket del lado del cliente
var socket = io.connect('/', { 'forceNew': true });

//Variables globales
var arrayindextemp;  
var indexcontrol;  
var lengtharray;  
//Evento de boton atras
function back(){  
  if(indexcontrol > 0){
    indexcontrol--;
    if(arrayindextemp !== undefined){
        socket.emit("messages-serv",{index:arrayindextemp.arrindex[indexcontrol].nombre});
    }
  }
}
//Evento de boton adelante
function next(){  
    if(arrayindextemp !== undefined){
        if(indexcontrol < (lengtharray-1)){
            indexcontrol++;
            socket.emit("messages-serv",{index:arrayindextemp.arrindex[indexcontrol].nombre});
        }
    }
}
//Obtiene los mensajes provenientes del servidor
socket.on('messages', function(data) {  
    //
    document.getElementById('Temp').innerHTML = data.temperatura+" °C";
    document.getElementById('hid').innerHTML = data.titulotemp;
    //
    document.getElementById('TempA').innerHTML = data.temperAmbiente+" °C";
    document.getElementById('HumA').innerHTML = data.humedadAmbiente+" %";
});
//Obtiene la configuracion inicial desde el servidor
socket.on('arrayindex-temp', function(data) {  
    var datajson = JSON.parse(data);  
    indexcontrol = 0;
    lengtharray = datajson.arrindex.length;
     arrayindextemp  = datajson;
});

Finalmente se obtienen los datos en tiempo real.
Codigo completo:
https://github.com/levalencia/IoTHubWithNodeJS