Friday, June 22, 2018

Getting Started with MQTT and Mosca in Node.JS

A modo de hacer una introducción a lo que es MQTT y MOSCA, voy a construir una aplicación sencilla que consiste en apagar o encender la luz de mi oficina.

Aqui les dejo un poco de teoría para comprender mejor:

MQTT, por sus siglas significa Message Queue Telemetry Transport. Es un protocolo usado para la comunicación Machine to Machine (M2M) donde dispositivos se comunican entre sí utilizando un patrón publisher/subscriber. Los clientes se conectan a un broker o server, estos clientes subscriben diferentes topics y cada cliente a su vez es capaz de publicar messages. Y el broker es el responsable de enviar esos messages a los clientes que previamente se subscribieron.

En todo el párrafo anterior hice mención a los 4 conceptos más importantes que debes tomar en cuenta para aprender MQTT:

*Publisher/Subscriber
*Messages
*Topics
*Broker

Ah, lo olvidaba, es el protocolo favorito e ideal para soluciones de Internet of Things (IoT, Internet de las Cosas) porque permite conectar un dispositivo a Internet.

Bien, ahora pasemos al siguiente concepto, MOSCA. Desde luego, no es la mosca que conoces, ese insecto cochino que anda posandose sobre los alimentos.

Recuerdas lo que era un broker? Ok, Mosca es un NodeJS MQTT Broker. Está escrito en Javascript asi que solo necesitamos NodeJS para correrlo. Entonces, conmigo vamos a crear un broker privado usando Node.JS

Assumption:
Voy a suponer que ya tienes Node y NPM installado en tu máquina. Y sino, google it how to do it, it's easy!

So, let's start!

// Iniciando el proyecto node
$mkdir mqtt-mosca-app
$cd mqtt-mosca-app
$npm init --y
// Instalando dependencias
$npm i mqtt mosca --save
// espera que termine la instalación y abre el directorio con tu IDE, en mi caso Visual Studio Code
$code .

Creamos 3 archivos:

broker.js
controller.js
lamp.js

// Código para broker.js

var mosca = require('mosca');
var settings = {
port:1883
}

var server = new mosca.Server(settings);

server.on('ready', function(){
console.log("ready");
});

Requerimos el module mosca y seteamos el puerto, por defecto es el 1883. Luego se lo pasamos al servidor o broker. Activamos el broker con el evento "ready" seguido de un callback que nos imprimirá en consola que el broker está listo.


// Código para lamp.js
Necesitamos incluir el mqtt npm library y conectarnos con nuestro broker a través de la función connect, como argumento le pasamos la URL de nuestro broker, en este caso pasa usar nuestro broker necesitas poner tu IP.

Los dos estados que vamos a manejar serán: turn-on y turn-off de apagar y encender las luces. Por defecto, vamos a empezar con las luces apagadas... mmm me gusta eso XD

Luego, vamos a invocar el método on(), este método toma dos parámetros: el evento connect y un callback para subscribir a un topic.

Lamp se va a subscribir a dos topics, de encender y de apagar: lamp/turn-on y lamp-turn-off, también vamos a publicar que lamp está conectado y enviaremos el estado por defecto, te acuerdas? es el turn-off

const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://172.21.19.19');

// states: turn-on, turn-off
var state = 'turn-off';

client.on('connect', () => {
client.subscribe('lamp/turn-on');
client.subscribe('lamp/turn-off');

// inform controller that lamp is connected
client.publish('lamp/connected', 'true');
sendStateUpdate();
});

function sendStateUpdate() {
console.log('sending state = %s', state);
client.publish('lamp/state', state);
};


Entonces, cuando nos digan que nos encendamos, tenemos que preguntar si no estamos encendidos y si es cierto, cambiamos nuestro estado. De manera parecida, cuando nos digan que nos apaguemos. Y publicamos nuestro estado actualizado. El controlador recibirá esta publicación.

client.on('message', (topic, message) => {
console.log('received message %s = %s', topic, message);
switch (topic) {
case 'lamp/turn-on':
return handleTurnOnRequest(message);
case 'lamp/turn-off':
return handleTurnOffRequest(message);
}
})

function handleTurnOnRequest(message) {
if (state !== 'turn-on') {
console.log('turning on the lamp...');
//simulate turn on the lamp after 5 seconds
setTimeout(() => {
state = 'turn-on';
sendStateUpdate();
}, 5000);
}
}

function handleTurnOffRequest(message) {
if (state !== 'turn-off') {
console.log('turning off the lamp...');
//simulate turn off the lamp after 5 seconds
setTimeout(() => {
state = 'turn-off';
sendStateUpdate();
}, 5000);
}
}


// Código para Controller.js

Al igual que en lamp.js, necesitamos requerir el modulo mqtt y conectarnos a nuestro broker privado.

Para saber si la lámpara está encendida o apagada, nos creamos una variable asi como también para saber si el equipo está conectado.

Luego, invocando el método on(), y con el evento connect nos subscribimos en este caso a dos topics: lamp/connected y lamp/state ya que como contrlador, necesitamos saber si la lámpara está conectada y su estado.

const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://172.21.19.19');

var lampState = '';
var connected = false;

client.on('connect', () => {
client.subscribe('lamp/connected');
client.subscribe('lamp/state');
});

Ahora con el evento message, empezamos a recibir los mensajes publicados a los topics que nos subscribimos. Entonces decimos que cuando la lámpara esté conectada, cambiamos el valor de nuestra variable que creamos al inicio a true o a false dependiendo del caso. Y por otro lado, cuando nos llegue el mensaje del estado de la lámpara, cambiamos el valor de nuestra variable ya sea a turn-on o turn-off dependiendo del caso.

//You are supposed to receive some messages published to the topics
that you have subscribed to.
client.on('message', (topic, message) => {
switch (topic) {
case 'lamp/connected':
return handleLampConnected(message);
case 'lamp/state':
return handleLampState(message);
}
console.log('No handler for topic %s', topic)
});

function handleLampConnected (message) {
console.log('lamp connected status = %s', message);
//The message usually arrives as buffer, so I had to
convert it to string data type.
connected = (message.toString() === 'true');
};

function handleLampState (message) {
lampState = message;
console.log('lamp state update to: %s', message);
};

Y eso es todo, ahora vamos a simular ser una webapp o una mobile app que va a pedir que se encienda la luz de mi oficina y se apague. Para encender, nos aseguramos que las luces no estén encendidas y luego publicamos el topic lamp/turn-on, nuestra lámpara nos escuchará (porque ya está subscrita) y hará lo suyo. Para apagarla, lo mismo, nos aseguramos que no estén apagadas y publicamos el topic lamp/turn-off

Con el ejemplo, las luces se apagarán despues de 20 segundos con el setTimeout que estamos programando.

// simulating an outside input (a web app, mobile app, etc)
function turnOnLamp() {
// can only turn on the lamp if we are connected to
mqtt and lamp is not already turn on
if (connected && lampState !== 'turn-on') {
// ask the lamp to turn on
client.publish('lamp/turn-on', 'true');
}
}

function turnOffLamp() {
// can only turn off the lamp if we are connected to mqtt and lamp is
not already turn off
if (connected && lampState !== 'turn-off') {
//ask the lamp to turn off
client.publish('lamp/turn-off', 'true');
}
}

// for demo purposes only
//simulate turning on the lamp
setTimeout(() => {
console.log('***turn on the lamp***');
turnOnLamp();
}, 5000)

//simulate turning off the lamp
setTimeout(() => {
console.log('***turn off the lamp***');
turnOffLamp();
}, 20000)


Para probar, corre primero broker.js, luego controller.js y en seguida lamp.js
Te debería salir así:


Friday, June 15, 2018

How to make ng serve listen other than localhost

Actually, your angular app is running in:

http://localhost:4200/

Now, you can make your angular app run in:

http://172.21.19.19:8080/

It is simply like this:

ng serve --port 8080 --host 0.0.0.0 --disableHostCheck true

That's all!

Sunday, June 10, 2018

How to convert .angular-cli.json to angular.json (Angular 6 Migration)

Error: Local workspace file ('angular.json') could not be found.

Well, after running the following commands for migrating an angular app 5 to angular 6, I realized that the angular-cli.json hadn't been converted as you can see in the screenshot bellow:

npm install -g @angular/cli
npm install @angular/cli
ng update @angular/cli
ng update @angular/core
ng update @angular/material (I didn't execute this because I am not using Angular Material)




So, executing this command, I fixed this error:

ng update @angular/cli --from=1.7.4 --migrate-only


That's it! Now, you can go ahead with *ng serve*
Happy Coding!