Arquitectura para el monitoreo de huertos con IoT y Azure (Parte I - Envío de mensajes a Event Hub)
Antes de iniciar este articulo, quisiera citar el post de Max Debolí Azure MVP donde explica una arquitectura propuesta para escenarios de IoT, en este caso veremos el envío de telemetría de un Huerto a soluciones de IoT de Azure. Me parece interesante y funcional para muchos de los casos que se pueden dar en una empresa que esté adoptando este tipo de tecnologías.
https://mdeboli.wordpress.com/2016/09/20/arquitectura-para-el-monitoreo-de-huertos-iot-azure/
Ahora que tenemos un mejor entendimiento del escenario, vamos a desarrollar una sería de publicaciones, donde veremos como programar paso a paso está solución, enfocando en como hacer la comunicación con Azure y luego procesar los datos.
Por ultimo les comparto un video donde explica el escenario que estamos atacando, de esta manera tendremos un mejor contexto de la solución:
- Arduino IDE
- JSON Object Library
- YL-69 Library
Una vez que tengamos todos los requisitos para nuestro ambiente de desarrollo, agregaremos el siguiente código, el cual hará la comunicación con los sensores de humedad, y generará el objeto JSON para luego enviarlos por USB:
#include <ArduinoJson.h>
class HumiditySensor
{
public:
HumiditySensor(int sensorPin);
void ConfigurePin();
void Read();
int sensorValue;
private:
int _sensorPin;
};
HumiditySensor::HumiditySensor(int sensorPin)
{
this->_sensorPin = sensorPin;
this->sensorValue = 0;
}
void HumiditySensor::ConfigurePin()
{
pinMode(this->_sensorPin, INPUT);
}
void HumiditySensor::Read()
{
this->sensorValue = analogRead(this->_sensorPin);
}HumiditySensor sensorTest1(A0);
HumiditySensor sensorTest2(A1);
HumiditySensor sensorTest3(A2);
HumiditySensor sensorTest4(A3);
HumiditySensor sensorTest5(A4);
HumiditySensor sensorTest6(A5);void OnRequestData()
{
StaticJsonBuffer<250> jsonBuffer;
JsonObject& jsonObj = jsonBuffer.createObject();
jsonObj["did"] = "ARD01";
JsonArray& sensorData = jsonObj.createNestedArray("sd");//a0 sensor
JsonObject& sensorA0Data = sensorData.createNestedObject();
sensorA0Data["pid"] = "A0";
sensorA0Data["sm"] = "yl-69";
sensorA0Data["h"] = sensorTest1.sensorValue;
jsonObj.printTo(Serial);
Serial.println();
//a1 sensor
JsonObject& sensorA1Data = sensorData.createNestedObject();
sensorA1Data["pid"] = "A1";
sensorA1Data["sm"] = "yl-69";
sensorA1Data["h"] = sensorTest2.sensorValue;
jsonObj.printTo(Serial);
Serial.println();
//a2 sensor
JsonObject& sensorA2Data = sensorData.createNestedObject();
sensorA2Data["pid"] = "A2";
sensorA2Data["sm"] = "yl-69";
sensorA2Data["h"] = sensorTest3.sensorValue;
jsonObj.printTo(Serial);
Serial.println();
//a3 sensor
JsonObject& sensorA3Data = sensorData.createNestedObject();
sensorA3Data["pid"] = "A3";
sensorA3Data["sm"] = "yl-69";
sensorA3Data["h"] = sensorTest4.sensorValue;
jsonObj.printTo(Serial);
Serial.println();
//a4 sensor
JsonObject& sensorA4Data = sensorData.createNestedObject();
sensorA4Data["pid"] = "A4";
sensorA4Data["sm"] = "yl-69";
sensorA4Data["h"] = sensorTest5.sensorValue;
jsonObj.printTo(Serial);
Serial.println();
//a5 sensor
JsonObject& sensorA5Data = sensorData.createNestedObject();
sensorA5Data["pid"] = "A5";
sensorA5Data["sm"] = "yl-69";
sensorA5Data["h"] = sensorTest6.sensorValue;
jsonObj.printTo(Serial);
Serial.println();
}void setup() {
Serial.begin(9600);
sensorTest1.ConfigurePin();
sensorTest2.ConfigurePin();
sensorTest3.ConfigurePin();
sensorTest4.ConfigurePin();
sensorTest5.ConfigurePin();
sensorTest6.ConfigurePin();
}void loop() {
//read potentionmeter value
sensorTest1.Read();
sensorTest2.Read();
sensorTest3.Read();
sensorTest4.Read();
sensorTest5.Read();
sensorTest6.Read();OnRequestData();
delay(1000);
}
- Raspbian
- Node.js (6.x) o superior
- azure-event-hubs (npm)
- eventhubs-js (npm)
"use strict"
var eventHub = require("eventhubs-js")
var EventHubClient = require('azure-event-hubs').Client;
var client;
var azureEventHub = {
_eventHub_connectionString: "your-azure-event-hub-primary-connectionString",
_notification_hub_name: "huerto-eventhub",
init: function () {// Create connection with Azure Event Hub
client = EventHubClient.fromConnectionString(this._eventHub_connectionString, this._notification_hub_name)
client.open()
.then(function () {
// Init sender with receiver group Default
return client.createReceiver('$Default', '0', { startAfterTime: Date.now() })
})
.then(function (rx) {// Listening for errors
rx.on('errorReceived', function (err) { console.log(err); });// Listening for new messages in consumer group
rx.on('message', function (message) {
var body = message.body;
console.log(body);
})
});
},
sendMessage: function (msg) {client.open()
.then(function () {
// Init sender with consumer group Default
return client.createSender('0');
})
.then(function (tx) {// Listening for errors
tx.on('errorReceived', function (err) {
console.log(err);
});// Send menssage to Azure Event Hub
tx.send({ message: msg });
});
}
};module.exports = azureEventHub;
- Raspbian
- Node.js (6.x) o superior
- serialport (npm)
- node-uuid (npm)
Para obtener la información de los Arduinos, vamos a crear un modulo, el cual tendrá la función de exponer los eventos de lectura e inicialización de cada uno de los puertos USB disponibles en la Raspberry. A su vez nos permitirá comunicarnos con Event Hub de Azure y enviar los mensajes de telemetría que nos esta llegando de los dispositivos de IoT.
Agregamos el siguiente código a nuestro programa:
serialPortReader.js
"use strict"
// Dependencies
var events = require("events"),
uuid = require("node-uuid"),
serialport = require("serialport"),
azureEventHub = require("../../AzureEventHub/azureEventHub.js"),
SerialPort = serialport.SerialPort;// Default values
var defaultOptions = {
// Default Raspberry ports info
port: "/dev/ttyACM0",
// Default optimal value for baudrate
baudrate: 9600,
// Iteration time
milliseconds: 1000
};var SerialPortReader = function (options) {
//Set default values
options = (options == undefined) ? {} : options;
this.baudrate = options.baudrate ? options.baudrate : defaultOptions.baudrate;
this.milliseconds = options.milliseconds ? options.milliseconds : defaultOptions.milliseconds;
this.serialPortList = serialport;
this.switchRead = false;//Init Connection with Event Hub
azureEventHub.init();
};SerialPortReader.prototype = new events.EventEmitter;
SerialPortReader.prototype.init = function (port) {
// Init arduino usb ports for lecture
var self = this;
self.switchRead = true;// Set arduino lecture events
self.sp = new SerialPort(port, {
baudrate: this.baudrate,
parser: serialport.parsers.readline("\r\n")
});// Read arduino data
self.read();
};SerialPortReader.prototype.read = function () {
// Subscribe to data event listener
var self = this;
self.sp.on("data", arduinoReader.onData);// Subscribe to error event listener
self.sp.on("error", function (error) {
self.emit("serialPortError", error);
});
};// Close port event on demand
SerialPortReader.prototype.close = function () {
var self = this
self.sp.close();
self.emit("close", "serial port closed")
};var arduinoReader = {
//Get data from arduino
onData: function (data) {// Try to parse arduino data
var self = this;
var dataObj = arduinoReader.tryToParseJson(data);
if (dataObj) {// Parse arduino data array and send messages to Azure Event Hub
dataObj.sd.map(function (measure) {
// Message JSON
var msg = {
// Message ID
uuid: uuid.v4(),
// Device ID from Arduino
did: dataObj.did,
// Time
tt: new Date().toISOString(),
// Sensor Pin ID
pid: measure.pid,
// Humedad
h: measure.h,
// Device name
sm : measure.sm
};//Send menssage to Event Hub
azureEventHub.sendMessage(msg);
});
};
},
tryToParseJson : function (jsonString) {
var j;
try {
j = JSON.parse(jsonString);
} catch (e) {
return undefined;
}
return j;
}
};module.exports = SerialPortReader;
Una vez que creemos el modulo de lectura, vamos a comunicarnos desde la aplicación principal al modulo para iniciar con la lectura y envío de mensajes:
app.js
"use strict"
// Dependencies
var SerialPortReader = require("./modules/serialPortReader.js");// Init with default configuration values
var arduino = new SerialPortReader();// Serial port init function
function startSerialRead(port) {
arduino.init(port.comName);
}// Init all usb ports available
arduino.serialPortList.list(function (error, ports) {
ports.forEach(startSerialRead);
});arduino.on("serialPortError", function (error) {
console.log(error);
});arduino.on("close", function (data) {
console.log(data);
});arduino.on("error", function (error) {
console.log(error);
});
Felicidades!!!
Hasta este punto hemos logrado comunicar nuestros dispositivos de IoT con Azure Event Hubs!
En resumen, conectamos sensores de humedad de tierra analógicos a cada uno de los PIN de los Arduinos, los cuales contienen un programa de C que se encarga de obtener las lecturas, para luego enviarlas por USB a una Raspberry. Gracias a la magia de Node.js, podemos obtener los datos que se envían por USB desde una Raspberry y luego enviarlas a Azure para su futuro procesamiento.
Siguientes pasos:
- Crear y configurar servicio de Stream Analytics para procesamiento de datos masivos en tiempo real
Comments
Post a Comment