Lección 4. Configurar el PLC por MQTT y EEPROM
#4 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's de Industrial Shields tienen una EEPROM donde se pueden configurar y guardar algunos valores importantes que son muy relevantes para su aplicación.
En este capítulo vamos a seguir una aplicación imaginaria. Imagina una aplicación HVAC (Calefacción, Ventilación y Aire Acondicionado) 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 perderlos durante un corte de energía y cuando el PLC se reinicie de nuevo simplemente tomar el último valor de la EEPROM para continuar con su funcionalidad.
Así que, en el post aprenderemos a utilizar nuestra EEPROM, a enviar desde el Node-RED valores bien interpretados por el PLC y a obtener valores bien interpretados desde el PLC a la aplicación Node-RED.
Requisitos
Los requisitos para este capítulo son:
- Mosquitto u otro broker MQTT en funcionamiento
- Node-RED instalado y funcionando
- M-Duino family PLC
- Conexión Ethernet
- Arduino IDE instalado con la Librería MQTT adecuada
*Sigue los capítulos anteriores para ver estos pasos.
Empecemos con la parte práctica. Comenzaremos con la aplicación Node-RED. Nuestra aplicación Node-RED tendrá un nodo Edit Numeric donde el usuario podrá introducir el valor deseado para nuestro sistema. Así que añade dos nodos numeric desde las funciones del dashboard.
Nuestro rango de temperatura estará entre 15-35 ºC (paso 0,1) y la humedad entre 0-100 % (paso 1).

Una vez colocados los dos nodos Edit Numeric, configúralos para que se muestren en la tercera pestaña de nuestro dashboard, como pestaña de configuración.

Nuestro dashboard ya tiene buen aspecto. Vamos ahora con la lógica de la aplicación. Queremos enviar este valor directamente a nuestro broker MQTT. Antes, tendremos que hacer algunos cambios en esta variable para que el controlador interprete los datos con facilidad. Todas las variables numéricas de JavaScript son de 64 bits. Esto es un problema porque Arduino Mega o Leonardo, que usan los PLC de Industrial Shields, solo aceptan un máximo de 32 bits.
Para cambiar este valor, tendremos que crear una nueva función en nuestro flujo de trabajo Node-RED.
Añade una función vacía y configúrala como se indica:

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 de NodeJS Información del buffer de NodeJS .
Una vez inicializado el buffer, podemos proceder a transformar nuestro valor a un int32_t little endian como puede leer nuestro controlador sin problema. Para ello, vamos a utilizar la función writeInt32LE (value, offset). Lee más sobre ella:
Para dar más resolución a nuestro sistema, hemos decidido multiplicar el valor de la temperatura por 10 para trabajar con valores enteros en nuestro PLC o controlador. Así que añade una función corta como esta:

Después, nuestro valor está listo para enviarse. Añade la función de envío MQTT con el topic deseado. Por ahora, nuestro flujo debería verse así:


Ya tenemos la mitad del Node-RED hecha. Como ya sabes, los nodos de Node-RED actúan cuando el nodo sufre un cambio. Ahora imagina que, por algún motivo, hay un corte de alimentación en tu sistema. El PLC seguirá funcionando como se espera porque tiene el valor configurado en su EEPROM, pero el sistema no sabrá con qué valor está trabajando el PLC. Para resolverlo, enviaremos por el mismo topic el valor configurado guardado en la EEPROM del PLC cuando el PLC vuelva a arrancar.
¿Cómo lo relacionamos con nuestro nodo Edit Numeric? Es lo mismo que antes, pero en sentido inverso:
- Añade una función MQTT
- Suscríbete al mismo topic
- Añade una función editable
Configura la función de la siguiente manera:

Como puedes ver, aquí sólo utilizamos una función. 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á a un valor JavaScript. El valor es tratado adecuadamente para nuestra aplicación Node-RED. Sólo hay que añadir una nueva función para dividir el valor de la temperatura por 10.
Nuestro flujo de trabajo será así:


Software del PLC
Tras la aplicación Node-RED necesitaremos un controlador que pueda interactuar con ella. En esta segunda parte del capítulo se muestra un código de ejemplo. El código es un ejemplo sencillo de cómo utilizar nuestra EEPROM, las estructuras y el protocolo de comunicación MQTT.
En nuestro PLC basado en Arduino tendremos una estructura formada por dos int32_t, uno para la temperatura y otro para la humedad. Estos valores serán los valores del sistema para configurar el comportamiento del mismo.
//Estructura de configuración
typedef struct {
int32_t temperature;
int32_t humidity;
} actualConfig_t;
actualConfig_t actualConfig;
Para obtener y poner valores en la EEPROM utilizaremos la Librería EEPROM.h. El código utiliza dos funciones principales de esta librería, una es EEPROM.get (dirección, variable); para obtener valores y EEPROM.put (dirección, variable) para asegurar valores.
La otra parte del código, que es muy importante, es cómo leer los valores del MQTT. Para ello, utilizamos la función setCallBack.
mqtt.setCallback(receiveMqttMsg);
Esta 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){
//Establecer configuración a variable
memcpy(&actualConfig.temperature, payload, len);
//Imprimir para depuración
Serial.print("Temperature set to: ");
Serial.println(actualConfig.temperature);
//Establecer configuración en EEPROM
EEPROM.put(eeAdressTEMP, actualConfig.temperature);
}
if (strcmp(topic, "configHum") == 0){
//Establecer configuración en EEPROM
memcpy(&actualConfig.humidity, payload, len);
//Imprimir para depuración
Serial.print("Humidity set to: ");
Serial.println(actualConfig.humidity);
//Establecer configuración en EPROM
EEPROM.put(eeAdressHUM, actualConfig.humidity);
}
}
Esta función comparará el tema para saber si el valor es una temperatura o una humedad usando strcmp(tema, "configTemp") == 0 .
Después, usaremos memcpy(&address, data, datalen) para guardar los datos de la función callBack de MQTT en nuestra variable de trabajo. Si no hubiéramos modificado los datos en la aplicación Node-RED, este proceso sería mucho más complicado. Ahora tenemos un dato int32_t en little endian muy fácil de tratar.
Una vez que los datos están en nuestra RAM, es muy fácil almacenar la información en la EEPROM simplemente utilizando la función EEPROM.put().
Aparte del código, se crea una función de depuración que se puede utilizar para conocer el estado de nuestras 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 vídeo, donde se muestra el funcionamiento del sistema. Por un lado, tenemos el dashboard de Node-RED y por otro lado, tenemos el monitor serie de Arduino IDE para ver cómo recibimos los datos. Recordemos que Node-RED y Mosquitto están funcionando y el PLC M-Duino está conectado a la misma red del broker MQTT y del servicio Node-RED:
No se permite comentar en este curso.