Desarrolla tu aplicación SCADA basada en Node-RED
Curso de 8 Capítulos de Contenido General [IS.AC002.GC]
#0 Cómo instalar Node-RED
Node-RED es una herramienta de programación de código abierto, basado en NodeJS, para desarrollar aplicaciones con el fin de interactuar con diferentes hardware. Además Node-RED proporciona un editor de navegador que facilita la programación y configuración de tus aplicaciones.
Ve más información de Node-RED en la web oficial:
El primer paso de este curso será cómo instalar la aplicación Node-RED en tu ordenador. Antes de instalar Node-RED, debemos instalar NodeJS.
Cómo instalar NodeJS
Solo ve a la web oficial de NodeJS y instala el código fuente de NodeJS o un instalador previo a la compilación para tu sistema operativo: Download NodeJS
Si quieres instalarlo desde la línea de comando Linux:
Añade el depósito:
sudo apt install curl
-
Para la última versión, escribe:
curl -sL https://deb.nodesource.com/setup_10.x | sudo bash -
-
Para la versión LTS, escribe:
curl -sL https://deb.nodesource.com/setup_8.x | sudo bash -
Finalmente, para instalar NodeJS escribe:
sudo apt install nodejs
Cómo instalar Node-RED
Después de instalar el NodeJS, nuestro equipo está listo para instalar Node-RED. La mejor manera de instalar Node-RED es utilizar el gestor de paquetes de Node, npm, que ya viene con Node.js.
-
WINDOWS OS
Escribe el comando prompt:
npm install -g --unsafe-perm node-red
-
Post de MQTT
Escribe la línea de comando:
sudo npm install -g --unsafe perm node-red
Echa un vistazo a la guía oficial de instalación para saber más detalles:
Una vez tengas instalado Node-RED, podrás continuar con el siguiente capítulo del curso.
Do you want to continue the course?...for free!!!
If you are interested in
You will receive a weekly chapter to develop a NodeRed's Scada Application.
Industrial Applications can run under Open Source Platforms
Cliente MQTT para PLC basado en Arduino como módulo de E/S
En el capítulo #1 se explica cómo configurar tu PLC industrial basado en Arduino como un módulo de E/S MQTT.
Antes de empezar este capítulo sería interesante echar un vistazo a cómo funciona el protocolo MQTT, qué es un JSON y sus aplicaciones.
Después, echa un vistazo rápido a las siguientes bibliotecas:
-
MQTT: MQTT Post
-
ArduinoJson: ArduinoJson library
La arquitectura del código está hecha para un M-Duino21+, pero está diseñada para ser extendida para otros M-Duinos. Hay cuatro matrices diferentes que incluyen todas las E/S disponibles en M-Duino 21+.
Estas matrices son: digitalOutputs , analogOutputs, digitalInputs, analogInputs
Así, cada matriz se estructurará de la siguiente manera:
digitalOutputs[NUM_ZONES][NUM_DIGITAL_OUTPUTS_PER_ZONE] = {{Q0_0, Q0_1... }, {Q1_0, Q1_1...}, {Q2_0, Q2_1...}
Este último ejemplo será para un M-Duino 58+, cada zona está subdividida con su nomenclatura de pines relacionados.
Después de haber explicado cómo acceder al mapeo de entradas estamos listos para explicar el resto del código.
En la función setup() sólo estamos haciendo la inicialización de la Ethernet y del cliente MQTT llamado MQTT, relacionando la función callback cuando recibimos un mensaje a través de MQTT y todos los valores de entrada reales. Después de eso, sólo tenemos tres funciones principales en la función loop(). Estamos llamando a la función reconnect() si no hay conexión con el servidor, si hay una conexión sólo estamos usando la función mqtt.loop() que mantendrá la conectividad con nuestro servidor. Además de estas dos funciones, tenemos la función updateInputs() que actualizará el valor real de las entradas.
Profundicemos en las funciones para tener una mejor comprensión de lo que hacen:
-
GESTIÓN DE SALIDAS:
En primer lugar, tenemos la función reconnect() que se suscribirá a los temas "Q" y "A". Un tema está dedicado a las salidas analógicas y el otro está dedicado a las salidas digitales. Si no se conecta al servidor, entonces se detendrá la conexión del cliente para no causar ningún problema con los puertos del servidor.
Second, to understand how the outputs control works we need to look into the callback function. In this program the callback function is receiveMqttMessage()
. The receiveMqttMessage() function will run every time that we receive a message. So, when we receive a message the callback function will decide if the topic is for analog or digital pins and then will call the right function for it,
The setAnalogOutput() function work exactly the in the same way that setDigitalOutput().
ANALOG_OUTPUTS_OFFSET es usado para compensar el valor de los pines analógicos. Los pines analógicos se colocan e n las posiciones 5, 6 ,7. Por eso el ANALOG_OUTPUTS_OFFSET es 5.
-
GESTIÓN DE ENTRADAS:
In the main loop, we have the function updateInputs()that will update all the values of the inputs. This function will be comparing all the previous values to the current ones and if the new value has changed, then the program will publish the new value through MQTT. The digital values will be compering if there is a 0 or a 1, the function that makes this work is updateDigitalInput().
En el sitio analógico el programa comparará si el valor analógico ha cambiado más de 5 puntos si el valor actual es mayor o menor que el anterior, entonces publicará el nuevo valor. La función que hace este trabajo es updateAnalogInput().
/*
Copyright (c) 2019 Boot&Work Corp., S.L. All rights reserved
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// included library depending of M-Duino version
#ifdef MDUINO_PLUS
#include <Ethernet2.h>
#else
#include <Ethernet.h>
#endif
// libraries needed for MQTT communication
#include <ArduinoJson.h>
#include <PubSubClient.h>
#define MQTT_ID "demo"
#define NUM_ZONES 1
#define NUM_DIGITAL_OUTPUTS_PER_ZONE 5
#define DIGITAL_OUTPUTS_OFFSET 0
const int digitalOutputs[NUM_ZONES][NUM_DIGITAL_OUTPUTS_PER_ZONE] = {
{Q0_0, Q0_1, Q0_2, Q0_3, Q0_4},
};
#define NUM_ANALOG_OUTPUTS_PER_ZONE 3
#define ANALOG_OUTPUTS_OFFSET 5
const int analogOutputs[NUM_ZONES][NUM_DIGITAL_OUTPUTS_PER_ZONE] = {
{A0_5, A0_6, A0_7},
};
#define NUM_DIGITAL_INPUTS_PER_ZONE 7
#define DIGITAL_INPUTS_OFFSET 0
const int digitalInputs[NUM_ZONES][NUM_DIGITAL_INPUTS_PER_ZONE] = {
{I0_0, I0_1, I0_2, I0_3, I0_4, I0_5, I0_6},
};
#define NUM_ANALOG_INPUTS_PER_ZONE 6
#define ANALOG_INPUTS_OFFSET 7
#define ANALOG_INPUTS_THRESHOLD 5 // Filtering threshold
const int analogInputs[NUM_ZONES][NUM_ANALOG_INPUTS_PER_ZONE] = {
{I0_7, I0_8, I0_9, I0_10, I0_11, I0_12},
};
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xAE };
IPAddress broker(10, 0, 3, 21);
unsigned port = 1883;
// Initialize client
EthernetClient client;
PubSubClient mqtt(client);
int digitalInputsValues[NUM_ZONES][NUM_DIGITAL_INPUTS_PER_ZONE];
int analogInputsValues[NUM_ZONES][NUM_ANALOG_INPUTS_PER_ZONE];
////////////////////////////////////////////////////////////////////////////////////////////////////
void setup(){
Ethernet.begin(mac);
mqtt.setServer(broker, port);
mqtt.setCallback(receiveMqttMessage);
// Init variables
for (int i = 0; i < NUM_ZONES; ++i) {
for (int j = 0; j < NUM_DIGITAL_INPUTS_PER_ZONE; ++j) {
digitalInputsValues[i][j] = digitalRead(digitalInputs[i][j]);
}
for (int j = 0; j < NUM_ANALOG_INPUTS_PER_ZONE; ++j) {
analogInputsValues[i][j] = analogRead(analogInputs[i][j]);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {
if (!mqtt.connected()) {
reconnect();
} else {
mqtt.loop();
}
updateInputs();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void updateInputs() {
for (int i = 0; i < NUM_ZONES; ++i) {
for (int j = 0; j < NUM_DIGITAL_INPUTS_PER_ZONE; ++j) {
updateDigitalInput(i, j);
}
for (int j = 0; j < NUM_ANALOG_INPUTS_PER_ZONE; ++j) {
updateAnalogInput(i, j);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void updateDigitalInput(int zone, int index) {
int value = digitalRead(digitalInputs[zone][index]);
if (value != digitalInputsValues[zone][index]) {
digitalInputsValues[zone][index] = value;
publishInput(zone, index + DIGITAL_INPUTS_OFFSET, value);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void updateAnalogInput(int zone, int index) {
int value = analogRead(analogInputs[zone][index]);
int minValue = value > ANALOG_INPUTS_THRESHOLD ? value - ANALOG_INPUTS_THRESHOLD : 0;
int maxValue = value < 1023 - ANALOG_INPUTS_THRESHOLD ? value + ANALOG_INPUTS_THRESHOLD : 1023;
if ((analogInputsValues[zone][index] < minValue) || (analogInputsValues[zone][index] > maxValue)) {
analogInputsValues[zone][index] = value;
publishInput(zone, index + ANALOG_INPUTS_OFFSET, value);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void setDigitalOutput(char *payload, unsigned int len) {
DynamicJsonBuffer json(JSON_OBJECT_SIZE(3));
JsonObject &root = json.parseObject(payload, len);
if (root.success()) {
int zone = root["zone"];
if (zone >= NUM_ZONES) {
// Invalid zone
return;
}
int index = root["index"];
index -= DIGITAL_OUTPUTS_OFFSET;
if (index >= NUM_DIGITAL_OUTPUTS_PER_ZONE) {
// Invalid digital output
return;
}
int value = root["value"];
digitalWrite(digitalOutputs[zone][index], value);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void setAnalogOutput(char *payload, unsigned int len) {
DynamicJsonBuffer json(JSON_OBJECT_SIZE(3));
JsonObject &root = json.parseObject(payload, len);
if (root.success()) {
int zone = root["zone"];
if (zone >= NUM_ZONES) {
// Invalid zone
return;
}
int index = root["index"];
index -= ANALOG_OUTPUTS_OFFSET;
if (index >= NUM_ANALOG_OUTPUTS_PER_ZONE) {
// Invalid analog output
return;
}
int value = root["value"];
analogWrite(analogOutputs[zone][index], value);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void reconnect() {
if (mqtt.connect(MQTT_ID)) {
mqtt.subscribe("Q");
mqtt.subscribe("A");
} else {
// MQTT connect fail
client.stop();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void receiveMqttMessage(char* topic, byte* payload, unsigned int len) {
if (strcmp(topic, "Q") == 0) {
// Set digital output
setDigitalOutput((char*) payload, len);
} else if (strcmp(topic, "A") == 0) {
// Set analog output
setAnalogOutput((char*) payload, len);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void publishInput(int zone, int index, int value) {
DynamicJsonBuffer json(JSON_OBJECT_SIZE(3));
JsonObject &root = json.createObject();
if (root.success()) {
root["zone"] = zone;
root["index"] = index;
root["value"] = value;
publish("I", root);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void publish(const char *topic, JsonObject &root) {
unsigned len = root.measureLength();
if (len > 0) {
char *payload = new char[len + 1];
if (payload) {
root.printTo(payload, len + 1);
publish(topic, payload);
delete[] payload;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void publish(const char *topic, const char *payload) {
if (mqtt.connected()) {
mqtt.publish(topic, payload);
}
}
Cómo visualizar las entradas
En los capítulos anteriores, hemos visto cómo instalar Node-RED en nuestro ordenador y un código útil de Arduino utilizando unidades Industrial Shields. Este código estará relacionado durante todo el curso e iremos interactuando con él para depurar nuestros propios programas.
El primer paso es instalar la versión estable de Node-RED-Dashboards API. Recuerda que Node-RED-Dashboard requiere que Node-RED esté instalado. Escribiendo en el terminal iremos a nuestro directorio ~/.node-red using:
cd .node-red/
Luego teclearemos el comando para instalar Node-RED-Dashboard:
npm install node-red-dashboard
Si quieres más información sobre Node-RED-Dashboard echa un vistazo al siguiente enlace:Node-RED-Dashboard
Ejecutando NodeRED
Una vez instalado Node-RED-Dashboard podemos proceder a poner en marcha el primer programa.
First of all, we will learn how to execute the Node-RED and how to change our programs. If you have installed Node-RED you can use the node-red command:
node-red
Después de ejecutar el comando node-red, podemos ir al the localhost:1880/
Escribe localhost:1880/ en tu navegador. Verás la interfaz de usuario de Node-RED donde podremos desarrollar, depurar y modificar nuestros programas.
Ahora podemos ver los diferentes bloques de Node-RED en el sitio correcto. Si haces clic en un módulo verás que en la parte izquierda se muestra una documentación de ayuda que te será muy útil para entender el bloque.
Creating our first program
Finally, our first
Elige un blog de entrada MQTT.
Este módulo será nuestro mensaje de entrada MQTT desde el broker MQTT. Haz un doble click y cambia el topic por "I". Este cambio relacionará el blog de entrada con todos los mensajes que recibamos a través del tema "I". En realidad, el bloque se ha suscrito a "I".
Además, debemos cambiar el servidor. Verás que no tienes ningún servidor creado. Cree un nuevo servidor, nómbralo y configúralo de la siguiente manera. Estos serán la dirección IP y el puerto donde se encuentra el broker MQTT. Fíjate en la siguiente imagen. Hemos llamado al servidor "Localhost broker".
Nuestra suscripción MQTT está configurada correctamente.
De este bloque recibiremos una cadena JSON con la siguiente estructura:
"{"zone" = 0, "index" = 0 , "value" = 0 }"
Trabajar con la cadena no es práctico. Por esta razón, tenemos que convertir esta cadena en un objeto JavaScript. Node-RED tiene un bloque dedicado a hacer este trabajo. Este bloque se llama Json y lo podemos encontrar en los bloques de funciones. Haz doble clic en este bloque. Cambia la acción a "Convertir siempre a objeto JavaScript". Ahora a la salida de este mensaje tendremos el objeto JavaScript y será muy fácil organizar nuestro programa.
Después de convertir el mensaje del tema MQTT "I" a JavaScript estamos listos para analizar el objeto y dividirlo en diferentes zonas e índices. En este curso, sólo veremos un ejemplo de M-Duino 21+. Así que tendremos la siguiente estructura de entrada.
ZONE 0
-
INDEX 0 = I0_0
-
INDEX 1 = I0_1
-
INDEX 2 = I0_2
-
INDEX 3 = I0_3
-
INDEX 4 = I0_4
-
INDEX 5 = I0_5
-
INDEX 6 = I0_6
-
INDEX 7 = I0_7
-
INDEX 8 = I0_8
-
INDEX 9 = I0_9
-
INDEX 10 = I0_10
-
INDEX 11 = I0_11
-
INDEX 12 = I0_12
ZONE 1
ZONA 1 (M-Duino 42+, M-Duino 38R, M-Duino 38AR+...):
ZONE 2
ZONA 2 (M-Duino 57R+, M-Duino 58+...)
Echa un vistazo a las entradas del M-Duino 21+, y verás que de I0_7 a I0_12 son entradas analógicas . De hecho, los valores se representarán de forma diferente porque uno es digital y el otro será Analógico (de 0 a 1024). Pero lo veremos más adelante.
La primera división al objeto será la zona.
Node-RED tiene un bloque llamado switch que nos será muy útil para dividir la información y dirigirla hacia donde queramos. Así, para filtrar la zona añadiremos el switch y con un doble clic cambiaremos la configuración. Añadiremos dos nuevas condiciones y las configuraremos en la siguiente imagen. Añadiendo payload.zone indicaremos con parte del objeto que el switch deberá mirar para clasificar el mensaje.
Tras el primer cambio de zona, procederemos a hacer lo mismo con el índice.
Antes de colocar los bloques finales para mostrar los valores en el tablero, necesitamos crear algunos grupos.
En el lado derecho, tenemos tres pestañas diferentes. Uno para ver los mensajes de depuración, otro para información y otro para la distribución de nuestro tablero.
En el lado Layout, hemos distribuido el espacio en tres grupos, entradas digitales y dos para entradas analógicas. Cuando creamos los bloques de tablero adecuados, decidiremos dónde colocar estos bloques en nuestro tablero. Aparte de eso, hay otras configuraciones dentro y pestañas de temas que le darán un aspecto personalizado a nuestro tablero.
Entonces, luego de haber analizado y dividido el mensaje completo, ahora solo nos falta mostrar en nuestro tablero el valor real y actualizado. De hecho, solo necesitamos conectar el bloque de interruptores para entradas digitales y el bloque de indicadores para las entradas analógicas, puedes encontrar estos bloques en el grupo del tablero en el lado izquierdo.
Node-RED también tiene opciones como un gráfico para señales analógicas.
Echa un vistazo a las siguientes imágenes para tener una mejor comprensión.
Te habrás dado cuenta de que en la última imagen del bloque hemos colocado el bloque msg. Este blog es el bloque de depuración. El bloque de depuración es muy útil para depurar nuestro programa y hacer un seguimiento de cada función para ver cómo fluye el mensaje en nuestro flujo de trabajo.
Finalmente, tenemos nuestra lógica programada y solo necesitamos personalizar nuestro tablero. Configura el diseño, el sitio y el tema para darle el aspecto que quieras a tu tablero. Como ejemplo, mira las siguientes imágenes.
Finalmente, el trabajo está hecho. Con el fin de probar nuestro nuevo programa, tendremos al broker mosquito MQTT en nuestro ordenador y conectaremos el M-Duino a la misma red. A continuación, se muestra una imagen de cómo se mostrará el tablero.
Además, tienes un ejemplo de video de cómo verás el tablero en tiempo real mientras cambian los valores de entrada.
Cómo interactuar con las salidas
En el capítulo #3 se muestra cómo interactuar con nuestras salidas de un PLC Industrial Shields. Antes de comenzar, este capítulo asegura que has logrado los pasos de los capítulos anteriores. Durante el curso iremos aumentando la aplicación Node-RED, por lo que en este ya tenemos las entradas en el dashboard.
Requirements
-
Node-RED y NodeJS instalados y en ejecución
-
Node-RED-Dashboard instalado
-
Broker Mosquitto MQTT instalado y en ejecución
-
PLC con el código del capítulo#1 ejecutándose
Una vez cumplidos los requisitos, comenzaremos a explicar cómo funcionará la arquitectura de la salida.
Outputs Architecture
Como se ha mencionado anteriormente, este curso está diseñado para ser utilizado con una familia de PLC M-Duino. Como ejemplo, usamos un M-Duino 21+.
En nuestro M-Duino 21+ tenemos 8 salidas digitales (de Q0_0 a Q0_7), aparte de eso sabemos que tres de estas salidas digitales pueden funcionar como salidas analógicas. Entonces, en nuestro ejemplo, tendrá 5 salidas digitales (de Q0_0 a Q0_4) y 3 salidas analógicas (de A0_5 a A0_7). Para extender la aplicación Node-RED a más salidas, el procedimiento es exactamente el mismo.
Adding a new tab
Para tener una mejor organización de nuestro dashboard será colocar correctamente una nueva pestaña donde podamos controlar nuestras salidas y no mezclarlas con nuestras entradas. Obviamente este es un ejemplo, en tu aplicación puedes colocar los diferentes componentes en función de tus necesidades.
Entonces, en la primera parte de este capítulo, veremos cómo agregar una nueva pestaña.
Ves a la interfaz de usuario de Node-RED a través de tu navegador. En la parte superior derecha, selecciona el menú y haz clic en flow -> Add a new flow. Luego ves a renombrar un Outputs flow.
Después de eso, se creará una pestaña automática. En nuestro Diseño, podremos nombrar y agregar los nuevos grupos. Nombra tu nueva pestaña Outputs y agrega dos nuevos grupos, uno llamado Salidas Analógicas y el otro llamado Salidas Digitales.
Ahora estamos listos para agregar los elementos del tablero. Agrega un interruptor y un control deslizante. Haz clic en ellos y nómbralos. Selecciona también el grupo que se colocará, selecciona Salidas Analógicas para el control deslizante y Salidas digitales para el interruptor.
* No olvides agregar el tema en la parte inferior del nodo Editar interruptor. 'A' para Salidas Analógicas y 'Q' para Salidas Digitales.
Sending the MQTT Message
Desde aquí hemos colocado los elementos en nuestro tablero. Entonces, ahora es el momento de proceder a conectar estos elementos al bróker MQTT, es decir, enviar un mensaje con el tema A y Q.
Primero, es necesario relacionar el interruptor y el control deslizante con el objeto JS correcto. Selecciona la función Change y configúrala de la siguiente manera:
* Para cada entrada diferente, debemos establecer el valor del índice. En este caso, es un "0" porque estamos configurando el Q0_0.
La función Change agregará contenido nuevo, ahora tenemos un índice aparte de la carga útil donde tenemos el valor del interruptor o el control deslizante analógico. Además del índice, necesitamos agregar la zona. Entonces, agrega otra función de cambio y agrega el parámetro de zona. Echa un vistazo a la siguiente imagen:
* Para cada zona diferente, debemos establecer el valor adecuado de la zona. En este caso, es un "0" porque estamos configurando la zona 0 (entre Q0_0 y A0_7).
Ahora tenemos el objeto JS configurado correctamente. Ahora solo debemos crear el JSON y enviarlo al bróker MQTT. Toma la función json y edítala como "Convertir siempre a cadena JSON". Luego agrega la función de salida MQTT y selecciona el corredor. En nuestro caso, nuestro broker se está ejecutando en nuestro ordenador, por lo que seleccionamos "Broker de localhost".
* Recuerda que hemos agregado el tema a la función de cambio. También podemos definir la función de salida MQTT, pero luego tendremos que crear dos bloques diferentes.
Echa un vistazo al ejemplo de cómo configurar la función json y la función de salida MQTT.
Finalmente, agrega todas las E / S que quieres usar y coloca el interruptor y los controles deslizantes donde quieras.
Nuestro programa Node-RED debería verse como
Cómo configurar tu PLC Industrial Shields a través de MQTT
En el capítulo #4 se explica cómo configurar nuestro PLC. Los PLC de Industrial Shields tienen una EEPROM donde puedes establecer y guardar algunos valores importantes que son muy relevantes para tu aplicación.
En este capítulo, seguiremos una aplicación imaginaria. Imagina un; HVAC (Calefacción, Ventilación y Aire Acondicionado) una aplicación donde tienes un valor de humedad y temperatura que son tu punto de referencia para el sistema. Estos valores deberán estar en tu EEPROM para no perderla durante un corte de energía y cuando el PLC reinicie nuevamente solo tome el último valor de la EEPROM para continuar con su funcionalidad.
Entonces, en el post, aprenderemos cómo usar nuestra EEPROM, cómo enviar desde el Node-RED valores bien interpretados por el PLC y cómo obtener valores correctamente interpretados desde el PLC a la aplicación Node-RED.
Requirements
Los requisitos para este capítulo son:
-
Mosquitto u otro corredor de MQTT en ejecución
-
Node-RED instalado y en ejecución
-
PLC de la familia M-Duino
-
Red Ethernet
-
Arduino IDE instalado con la Librería MQTT adecuada
*Sigue los capítulos anteriores para ver estos pasos.
Empecemos por la parte práctica. Al principio, comenzaremos con la aplicación Node-RED. Nuestra aplicación Node-RED consistirá en un Editar Nodo Numérico donde el usuario podrá introducir el valor deseado de nuestro sistema. Por lo tanto, agrega dos Editar nodo numérico desde las funciones del tablero.
Nuestro rango de temperatura estará entre 15-35ºC (paso 0.1) y nuestra humedad estará entre 0-100% (paso 1).
Una vez colocados los dos Nodos de edición numéricos, configúralos para que se muestren en la tercera pestaña de nuestro panel de control como una pestaña de configuración.
Ahora nuestro dashboard se ve bien. Solo atendamos a la lógica de la aplicación. Queremos enviar este valor directamente a nuestro corredor MQTT. Antes de hacer eso tendremos que realizar algunos cambios en esta variable para que sea más fácil entender los datos en nuestro controlador. Todas las variables de Java Scrips son de 64 bits, esto es un problema para nosotros porque Arduino Mega o Leonardo usado en Industrial Shields PLC solo usan como máximo aceptado de 32 bits.
Para cambiar este valor, tendremos que crear una nueva función en nuestro flujo de trabajo Node-RED.
Agrega una función vacía y configurada de la siguiente manera:
La función Buffer.allocUnsafe () devuelve un nuevo buffer no inicializado del tamaño especificado. En nuestro caso queremos un buffer de 4 bytes, echa un vistazo a este enlace para saber más sobre las Librerías NodeJS. NodeJS buffer information .
Para darle más resolución a nuestro sistema hemos decidido multiplicar el valor de temperatura por 10 con el fin de tratar con números centenarios en nuestro PLC o controlador. Así que agrega una función corta como esa:
Después de eso, nuestro valor está listo para ser enviado. Agrega la función de envío MQTT con el tema deseado. Por el momento, nuestro flujo de trabajo se verá así:
La mitad del Node-Red está lista. Como ya sabes, los nodos Node-RED se activan cuando el nodo sufre un cambio, entonces imagina que por alguna razón tienes un corte de energía en tu sistema, el PLC estará funcionando como se esperaba porque tiene el valor configurado en su EEPROM, pero el sistema lo hará. No sé en qué valor está funcionando el PLC. Para solucionar este problema enviaremos a través del mismo tema el valor configurado guardado en la EEPROM del PLC cuando el PLC reinicie su funcionalidad.
Cómo relacionamos esto con nuestra edición de nodo numérico. Es igual que antes pero al revés.
Agrega una función de recepción MQTT, suscríbete al mismo tema y luego agrega una función editable adicional. Configura la función de la siguiente manera:
Como puedes ver, solo usamos una función aquí. Echa un vistazo a los enlaces anteriores de NodeJS para ver cómo funciona esta función. Básicamente, la función leerá un valor de 32 bits en little endian y lo cambiará por un valor de Java Script. El valor se trata adecuadamente para nuestra aplicación Node-RED. Simplemente agrega una nueva función para dividir el valor de temperatura entre 10.
Nuestro workflow se verá así:
Software de PLC
Siguiendo la aplicación Node-RED, necesitaremos un controlador que pueda interactuar con él. En esta segunda parte del capítulo, se muestra un código de ejemplo. El código es un ejemplo simple de cómo usar nuestra EEPROM, estructuras y protocolo de comunicación MQTT.
En nuestro PLC basado en Arduino, tendremos una estructura compuesta por dos int32_t, uno para temperatura y otro para humedad. Estos valores serán valores del sistema para establecer el comportamiento del sistema.
//Configuration structure
typedef struct {
int32_t temperature;
int32_t humidity;
} actualConfig_t;
actualConfig_t actualConfig;
Para obtener y poner valores en la EEPROM, usaremos la biblioteca EEPROM.h. El código usa dos funciones principales de esta biblioteca, una es EEPROM.get (eaddress, variable); para obtener valores y EEPROM.put (dirección, variable; a valores seguros.
La otra parte del código, que es muy importante, es cómo leer los valores del MQTT. Para hacer eso usamos la función setCallBack.
mqtt.setCallback(receiveMqttMsg);TEsta función será llamada cada vez que recibamos un mensaje MQTT.
void receiveMqttMsg(char* topic, uint8_t* payload, unsigned int len){
if (strcmp(topic, "configTemp") == 0){
//Set config to variable
memcpy(&actualConfig.temperature, payload, len);
//Print to debug
Serial.print("Temperature set to: ");
Serial.println(actualConfig.temperature);
//Set config to EEPROM
EEPROM.put(eeAdressTEMP, actualConfig.temperature);
}
if (strcmp(topic, "configHum") == 0){
//Set config to EEPROM
memcpy(&actualConfig.humidity, payload, len);
//Print to debug
Serial.print("Humidity set to: ");
Serial.println(actualConfig.humidity);
//Set config to EPROM
EEPROM.put(eeAdressHUM, actualConfig.humidity);
}
}
Una vez que los datos están en nuestra RAM es muy fácil almacenar información en la EEPROM simplemente usando la función EEPROM.put().
Aparte del código se crea una función de depuración que puedes utilizar para conocer el estado de las variables durante el flujo de trabajo.
A continuación se muestra el código completo:
////////////////////////////////////////////////////////////////////////////////////////////////////
//Configuration structure
typedef struct {
int32_t temperature;
int32_t humidity;
} actualConfig_t;
#define MQTT_ID "demo"
//including library for MQTT
#include <PubSubClient.h>
//including EEPROM library
#include <EEPROM.h>
//including Ethernet library depending of M-Duino version
#ifdef MDUINO_PLUS
#include <Ethernet2.h>
#else
#include <Ethernet.h>
#endif
actualConfig_t actualConfig;
const byte eeAdressTEMP = 0;
const byte eeAdressHUM = 4;
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xAE };
IPAddress ip(10, 10, 11, 2);
IPAddress broker(10, 10, 11, 1);
unsigned port = 1883;
EthernetClient ethClient;
PubSubClient mqtt(ethClient);
void setup(){
Serial.begin(9600);
//Set up Ethernet
Ethernet.begin(mac, ip);
Serial.print("Local IP: ");
Serial.println(Ethernet.localIP());
//Set up MQTT
mqtt.setServer(broker, port);
mqtt.setCallback(receiveMqttMsg);
updateFromEEPROM();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void loop(){
if (!mqtt.connected()){
reconnect();
if (mqtt.connected()){
mqtt.publish("actualConfigTemp", (uint8_t*) &actualConfig.temperature);
mqtt.publish("actualConfigHum", (uint8_t*) &actualConfig.humidity);
}
}else{
mqtt.loop();
}
//Rest of the logic
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void reconnect(){
if (mqtt.connect(MQTT_ID)){
mqtt.subscribe("configTemp");
mqtt.subscribe("configHum");
}else{
//connection fail
ethClient.stop();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void receiveMqttMsg(char* topic, uint8_t* payload, unsigned int len){
if (strcmp(topic, "configTemp") == 0){
//Set config to variable
memcpy(&actualConfig.temperature, payload, len);
//Print to debug
Serial.print("Temperature set to: ");
Serial.println(actualConfig.temperature);
//Set config to EEPROM
EEPROM.put(eeAdressTEMP, actualConfig.temperature);
}
if (strcmp(topic, "configHum") == 0){
//Set config to EEPROM
memcpy(&actualConfig.humidity, payload, len);
//Print to debug
Serial.print("Humidity set to: ");
Serial.println(actualConfig.humidity);
//Set config to EPROM
EEPROM.put(eeAdressHUM, actualConfig.humidity);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void updateFromEEPROM(){
EEPROM.get(eeAdressTEMP, actualConfig.temperature);
EEPROM.get(eeAdressHUM, actualConfig.humidity);
}
void debug(){
Serial.println("------------------Debug:-------------------");
Serial.print("Temp Value:");
Serial.println(actualConfig.temperature);
Serial.print("Hum Value:");
Serial.println(actualConfig.humidity);
}
A continuación se adjunta un video donde se muestra la funcionalidad del sistema. Por un lado tenemos el tablero de NodeRED y por el otro lado, tenemos el monitor serial de Arduino IDE para ver cómo recibimos los datos. Recuerda que NodeRED y Mosquitto se están ejecutando y M-Duino PLC está conectado a la misma red del broker MQTT y el servicio NodeRED:
#5 Communications: How to read variables from a Slave device through Modbus TCP/I
Introducción
El contenido del capítulo 5 se centra en controlar un dispositivo esclavo a través de Modbus TCP / IP. Este dispositivo está conectado a la misma red a través de Ethernet. En este caso, el dispositivo es un M-Duino 21+ que usa el código esclavo que se publica en our blog.
Este es el primer requisito para completar y probar la nueva lección, además de tener instalado NodeRED.
Modbus es un estándar muy útil que nos permite comunicar varios dispositivos de diferentes fabricantes en una misma red. Además, Modbus TCP / IP se transfiere a través de Ethernet, que es uno de los protocolos más fiables del mercado.
En este caso, mostraremos cómo configurar NodeRED para controlar uno de los esclavos de nuestro sistema.
Los maestros Modbus normalmente tienen 8 funciones principales:
readCoils(); readDiscreteInputs(); readHoldingRegisters(); readInputRegisters(); writeSingleCoil(); writeSingleRegister(); writeMultipleCoils(); writeMultipleRegisters();
Instalación y Actualización
Entonces, en primer lugar, necesitaremos instalar la biblioteca adecuada para tener estas funciones disponibles en nuestra aplicación NodeRED.
Escribe tu terminal o símbolo del sistema después de colocar tu terminal en el directorio raíz de tu NodeRED, normalmente usa el .node-red /:
cd .node-red/npm install node-red-contrib-modbustcp
Ahora verás que en tu aplicación NodeRED hay dos nuevas funciones Modbus disponibles.
Developing the Application
A menudo, en nuestras instalaciones, tenemos las mismas dos funcionalidades principales que controlar. Entradas y salidas. Como hemos hecho en los capítulos anteriores, hemos dividido el contenido.
Inputs
/////////////////Developing////
Outputs
Nodo cliente Modbus TCP. Se conecta a un servidor Modbus TCP para escribir msg.payload en una bobina o registro.
Los códigos de función admitidos actualmente incluyen:
-
FC 5: Escribe una única Bobina
-
FC 6: Escribe un único registro de retención
-
FC 15: Escribe múltiples Bobinas
-
FC 16: Escribe múltiples registros de retención
Al elegir el dataType y la Address podremos acceder a nuestras salidas esclavas y controlarlas.
Después de instalar la biblioteca NodeRED para usar el protocolo de comunicación Modbus TCP / IP, podemos usar sus nodos. Pero antes de utilizar el bloque principal que organizará la comunicación tendremos que configurar algunos parámetros. Tendremos que especificar el Type (tipo) y la Address (dirección). El Type será la función Modbus y la Address será la posición de la matriz Node-RED donde se colocan nuestras E/S deseadas.
Selecciona un interruptor y el nombre que quieras. No hay configuración adicional para este nodo.
Luego selecciona el bloque de cambio colocado en los bloques de funciones. Escribe la dirección en la función para dar una dirección al objeto Java Script.
Después de eso, nuestro interruptor enviará un mensaje (1/0) y, además, tendrá una dirección que apunta a la salida correcta.
Haz lo mismo para el dataType, agrega una función de cambio, escribe dataType y haz lo mismo para todos los interruptores, FC5.
Finally, select the modbustcp block into the outputs nodes. Give a name to the block. Into the configurations, Type and Address are toked for the node if you are not defined before for other nodes. In our case, we will define before having just one connection opened with our PLC.
Por lo tanto, selecciona cualquier tipo y dirección. Luego configura tu Modbus IP Server para el nodo, en nuestro ejemplo, hemos usado 10.10.10.4 IP.
If we do the same process for Analog outputs, adding sliders instead of switches, we got the entire control of the outputs. For the second change node, we must select dataType as FC6.
A continuación se muestra el flujo de salida completo y la interfaz de usuario.
#6 Install NodeRed on Raspberry Pi or Industrial Panel PC based on Linux
Introduction
El contenido del capítulo # 6 es cómo instalar nuestra aplicación NodeRED en una de nuestras Touchberry Pi u otra PC Panels basada en Linux. Este tipo de aplicaciones son muy utilizadas por nuestros clientes porque es una forma fácil y robusta de tener un panel táctil SCADA al lado de la instalación. Otro punto en el que nos centraremos durante el capítulo es cómo agregar la capacidad de inicio automático, por lo que una vez que nos encontramos en el panel el sistema nos mostrará directamente la aplicación NodeRED.
Installation and Upgrade
Dependiendo de la versión de Panel PC tendremos instaladas distintas versiones de Debian o Ubuntu. Actualiza o instala la última versión de NodeRED en tu sistema escribiendo en la terminal:
b
ash <(curl -sL https://raw.githubusercontent.com/node-red/raspbian-deb-package/master/resources/update-nodejs-and-nodered)
Actualiza npm antes de instalar cualquier paquete usando los siguientes comandos:
cd ~/.node-red npm outdated npm update
Ejecutando NodeRED
Para iniciar NodeRED, abre una nueva ventana de terminal y escribe:
node-red-start
* Cerrar la terminal o ctrl-c no detiene la ejecución de NodeRED. Seguirá ejecutándose en segundo plano.
Para detener la ejecución, escribe:
node-red-stop
Inicio automático al arrancar
Probablemente en tu aplicación querrás que el NodeRED se inicie cuando enciendas tu Touchberry Pi. Para ello volveremos a abrir la terminal y teclearemos:
sudo systemctl enable nodered.service
y para deshabilitar el tipo:
sudo systemctl disable nodered.service
Estos son los conceptos básicos para instalar NodeRED en nuestra Touchberry Pi u otr Panel PC Industrial Shields basada en Linux.
Si necesitas información adicional al respecto, sigue el siguiente link.
Administrador de alarmas por correo electrónico
El contenido del capítulo # 7 está enfocado en generar alarmas con NodeRED. Básicamente, veremos cómo crear un módulo para enviara un correo electrónico si algún parámetro supera el valor deseado.
El requisito previo de este capítulo es tener un servidor de correo electrónico con credenciales válidas.
Instalación y Actualización
Ejecuta el siguiente comando en tu directorio de usuario de NodeRED, normalmente como ya sabemos ~ / .node-red:
cd .node-red sudo npm i node-red-node-email
Una vez que el blog esté instalado, echa un vistazo a los bloques sociales en la aplicación NodeRED. Verás un bloque de salida llamado correo electrónico. Llévalo a tu flujo, también agrega un bloque de inyección.
En nuestro bloque de inyección lo configuraremos con un tema y una carga útil. El tema será nuestro asunto de correo electrónico y la carga útil será el contenido de nuestro correo electrónico. A continuación se muestra un ejemplo:
Después de eso, solo tenemos que conectarlo a nuestro bloque de salida de correo electrónico y configurarlo. Debes tener un correo electrónico válido y la seguridad bien configurada. Configura tu bloqueo de la siguiente manera, recuerda que To (Para) es el ID de correo electrónico al que deseas enviar el correo electrónico y el UserID (Identificación) de usuario y la Password (Contraseña) son para el correo electrónico desde donde deseas enviar el correo electrónico:
Cuando hagas clic en el blog, el correo electrónico debe enviarse al destino. Si deseas automatizar eso, solo necesitas crear una función que controle el vaciado de los datos del correo electrónico.
Si ves el siguiente error:
Configure your email to accept Control access to less secure apps these must unblock Node-RED to use your email. In this example, we have used Gmail. To change these parameters, you just need to go to your secure configuration on your Gmail account. With other email service providers, contact their technical team to know how to unblock the control access.
Usando SQLite DataBase con NodeRED
Introducción
SQLite es una biblioteca de lenguaje basada en C que implementa el motor de base de datos SQL. Es pequeño, rápido, autónomo, de alta confiabilidad y con todas las funciones. Aparte de eso, se basa en código abierto y es la base de datos más utilizada del mundo.
En este capítulo, centraremos nuestros esfuerzos en explicar cómo implementar una base de datos usando SQLite con NodeRED.
Instalación
Escribe tu terminal:
sudo npm install -g [email protected]hash -r
cd ~/.node-red
npm install node-red-node-sqlite
Una vez que tenemos la biblioteca instalada en nuestro NodeRED, podemos usar sus bloques en ella. Otra herramienta útil que recomendamos es DB Browser para SQLite. Existen distribuciones para los sistemas operativos más populares.
Ejemplo
their website.
CREATE TABLE IF NOT EXISTS log( time INT PRIMARY KEY, value INT)
Ahora podemos hacer clic en implementar y luego hacer clic en Create DB Table (Crear tabla de base de datos), ejecutaremos el código SQLite a través del bloque y nuestra base de datos se creará correctamente.
Una vez que hemos creado la base de datos es el momento de introducir nuestros primeros caracteres y leerlos. Imagina que tenemos un PLC datalogger que está enviando a nuestra aplicación NodeRED el valor analógico de algún sensor, y nuestro proyecto es conocer sus valores a lo largo del tiempo. Luego tenemos que crear una base de datos donde la hora y los valores deben estar en la misma fila de nuestra base de datos.
Para ello, hemos creado una interfaz de usuario como ejemplo con un control deslizante, un botón y un texto. El control deslizante será para simular el valor analógico externo, el texto se utilizará para mostrar el último valor en nuestra base de datos y el botón será para actualizar el último valor que se mostrará en el texto.
¿Como hacer eso? Es muy sencillo.
En primer lugar, introduce el control deslizante y configúralos como quieras. Es importante verificar la salida del caso "solo en el lanzamiento", si no en cada cambio, guardaremos un valor en nuestra base de datos. Después de configurar nuestro control deslizante debemos introducir una función entre el control deslizante y el bloque SQLite.
Sobre esta función colocaremos el siguiente código siguiendo las bases SQLite:
return {"topic": "INSERT INTO log (time, value) VALUES ( " + Date.now() +", "+ msg.payload +")"}
Este fragmento de código se lanzará cada vez que cambiemos el valor en el control deslizante al bloque SQLite. Con esto, insertaremos un valor y una marca de tiempo en nuestra tabla.
A continuación se muestran algunas imágenes de este proceso:
El último paso será mostrar el último valor de nuestra base de datos. Usamos el mismo método, simplemente agregando un código diferente en nuestro bloque de funciones:
var time = msg.payload
var newMsg = {
"topic": "SELECT * FROM log ORDER BY time DESC LIMIT 1"
}
return newMsg;
A continuación se muestra la configuración de cada bloque en caso de que exista alguna duda:
Finalmente, agrega tus parámetros personalizados al diseño.
Espero que este capítulo te haya ayudado a tener una nueva característica de NodeRED y que ahora puedas implementar bases de datos en tu proyecto.
A continuación se muestra nuestro diseño de ejemplo: