Los flujos de Node-RED Modbus son una de las formas más rápidas de sacar datos de medidores, variadores y PLCs que solo hablan Modbus y llevarlos a MQTT, donde los dashboards, los historiadores y las herramientas en la nube pueden usarlos. En este tutorial consultarás holding registers y coils con node-red-contrib-modbus, decodificarás correctamente enteros escalados y floats de 32 bits y publicarás JSON limpio en un broker solo cuando cambien los valores. También verás cómo gestionar con seguridad los comandos de escritura y cómo probarlo todo sin hardware real.
Por qué hacer de puente entre Modbus y MQTT con Node-RED
Modbus es un protocolo de petición/respuesta de 1979: un cliente (maestro) pide a un servidor (esclavo) un bloque de registros y recibe números en bruto de 16 bits. No tiene unidades, ni nombres, ni marcas de tiempo, ni envío push. Cada consumidor que quiere los datos tiene que consultar el dispositivo por su cuenta, y muchos dispositivos solo admiten unas pocas conexiones simultáneas.
MQTT le da la vuelta a esto. Un gateway consulta el dispositivo, da significado a los números y los publica una sola vez en un broker. Cualquier número de suscriptores recibe entonces valores con nombre y escalados en el momento en que cambian. Node-RED es un gateway natural porque consultar, decodificar, filtrar y publicar son un nodo cada uno, y la lógica permanece visible para el equipo de mantenimiento. Para la visión arquitectónica completa, la guía de Modbus TCP a MQTT compara Node-RED con otras opciones de gateway.
Instala node-red-contrib-modbus
Instala el paquete desde Manage palette → Install o desde la línea de comandos:
cd ~/.node-red
npm install node-red-contrib-modbus
# reinicia Node-RED después
Proporciona, entre otros, estos nodos:
| Nodo | Propósito |
|---|---|
| Modbus Read | Consulta periódicamente un bloque fijo de registros |
| Modbus Getter | Lee bajo demanda cuando llega un mensaje |
| Modbus Flex Getter | Lee lo que especifique el mensaje entrante |
| Modbus Write | Escribe coils o registros |
| Modbus Server | Un servidor Modbus TCP sencillo para pruebas |
| Modbus Client (config) | Conexión TCP o serie compartida por los nodos |
Entiende primero tu mapa de registros
Todo proyecto Modbus empieza por el mapa de registros del dispositivo. Para este tutorial, imagina un controlador de bomba con este mapa:
| Dato | Tipo | Dirección (base 0) | Notación clásica | Escalado |
|---|---|---|---|---|
| Temperatura | Holding register, 16 bits con signo | 0 | 40001 | valor / 10, °C |
| Presión | Holding register, 16 bits sin signo | 1 | 40002 | valor / 100, bar |
| Caudal | Holding registers, float de 32 bits, big-endian | 2-3 | 40003-40004 | ninguno, m³/h |
| Bomba en marcha | Coil | 0 | 00001 | booleano |
El error más habitual en Node-RED Modbus es la dirección. Las hojas de datos suelen usar la notación "40001", que empieza en uno e incluye un prefijo de tipo. La dirección del protocolo, y la que introduces en Node-RED, empieza en cero y no lleva prefijo: 40001 se convierte en la dirección de holding register 0. Si tus valores parecen desplazados un registro, este es el motivo.
Prueba sin hardware: un servidor Modbus local
Puedes seguir todo el tutorial sin un dispositivo ejecutando un servidor Modbus dentro de Node-RED.
- Arrastra un nodo Modbus Server a una pestaña aparte. Configúralo para escuchar en
0.0.0.0, puerto10502(no privilegiado, así que no necesitas root). Haz deploy. - Crea un nodo de configuración Modbus Client: tipo TCP, host
127.0.0.1, puerto10502, unit ID1. - Añade un nodo inject que se repita cada 2 segundos, un nodo function que genere valores de registro simulados y un nodo Modbus Write que use ese cliente con el código de función 16 (preset multiple registers), dirección
0y cantidad4.
La función del simulador:
// Nodo function "simulate pump registers" -> Modbus Write (FC16, dirección 0, cantidad 4)
const temperature = 40 + Math.random() * 5; // °C
const pressure = 3.2 + Math.random() * 0.3; // bar
const flowRate = 12 + Math.random() * 2; // m³/h
const buf = Buffer.alloc(4);
buf.writeFloatBE(flowRate, 0);
msg.payload = [
Math.round(temperature * 10), // registro 0
Math.round(pressure * 100), // registro 1
buf.readUInt16BE(0), // registro 2: palabra alta del float
buf.readUInt16BE(2) // registro 3: palabra baja del float
];
return msg;
Añade un segundo nodo de escritura para la coil: código de función 5 (force single coil), dirección 0, alimentado con true desde un nodo inject. Tu controlador de bomba simulado ya está funcionando.
Construye el flujo de consulta de Node-RED Modbus
Paso 1: consulta los registros
Añade un nodo Modbus Read con el mismo cliente:
- FC: 3, Read Holding Registers
- Address:
0, Quantity:4 - Poll rate: 2 segundos
Su primera salida emite msg.payload como un array de cuatro números en bruto, por ejemplo [423, 334, 16708, 26214]. La segunda salida lleva el buffer en bruto y los detalles de la respuesta, útiles para depurar el orden de bytes.
Para la coil, añade un segundo nodo Modbus Read con FC 1 (Read Coil Status), dirección 0, cantidad 1 y una consulta más lenta, cada 5 segundos.
Paso 2: decodifica los registros en bruto a valores de ingeniería
// Nodo function "decode pump registers"
const r = msg.payload;
if (!Array.isArray(r) || r.length < 4) {
node.warn("Unexpected register block");
return null;
}
// Conversión a 16 bits con signo para la temperatura
const int16 = v => (v > 32767 ? v - 65536 : v);
// Float de 32 bits a partir de dos registros, orden de palabras big-endian
const buf = Buffer.alloc(4);
buf.writeUInt16BE(r[2], 0);
buf.writeUInt16BE(r[3], 2);
const flowRate = buf.readFloatBE(0);
const values = {
temperature: Math.round(int16(r[0])) / 10,
pressure: Math.round(r[1]) / 100,
flow: Math.round(flowRate * 100) / 100
};
// Guardar para combinar con la lectura de la coil
flow.set("pump1_values", values);
msg.topic = "connectmq/demo/a7f3k2/plant1/utilities/pump1/telemetry";
msg.payload = {
...values,
running: flow.get("pump1_running") === true
};
return msg;
Conecta la lectura de la coil a una pequeña función que guarde el estado: flow.set("pump1_running", msg.payload[0] === true); return null;. Muchos dispositivos usan floats con "palabras intercambiadas", en los que la palabra baja va primero. Si tu valor de caudal parece absurdo, como 1.2e-38 o -4.5e+12, intercambia r[2] y r[3] y vuelve a probar.
Paso 3: publica solo cuando algo cambia
Consultar cada 2 segundos y publicar cada resultado desperdicia ancho de banda y llena a los suscriptores de datos duplicados. Inserta un nodo filter (antes llamado RBE) configurado como "block unless value changes" sobre msg.payload, o usa una versión con banda muerta sobre valores individuales si fluctúan. El filtro compara el objeto completo, por eso la función de decodificación no añade una marca de tiempo. Después del filtro, añade un nodo change que establezca msg.payload.ts con una marca de tiempo. Informar por excepción es un patrón central de MQTT, pero sigue valiendo la pena añadir un heartbeat periódico para los valores que cambian despacio, para que los suscriptores puedan distinguir un dispositivo tranquilo de uno muerto.
Paso 4: publica en MQTT
Añade un nodo mqtt out con el topic vacío para que se use msg.topic. Para las pruebas, configura el broker como broker.connectmq.com, puerto 1883, con un client ID único: es el servidor MQTT público gratuito. Sustituye a7f3k2 por tu propia cadena. Después observa la salida:
mosquitto_sub -h broker.connectmq.com -p 1883 -i sub-modbus-a7f3k2 -v \
-t 'connectmq/demo/a7f3k2/plant1/#'
Deberías ver mensajes como {"temperature":42.3,"pressure":3.34,"flow":12.81,"running":true,"ts":...}. No envíes datos reales de planta al servidor público: cualquiera puede suscribirse a él.
Escribir en Modbus desde MQTT, con seguridad
Leer es de bajo riesgo; escribir en una bomba no lo es. Si aceptas comandos desde MQTT, pon barreras de protección en el flujo:
- Suscríbete a un topic de comandos dedicado, como
.../pump1/cmd, nunca a un comodín. - Valida el payload contra una lista blanca de acciones permitidas y rangos de valores en un nodo function.
- Asocia cada acción a un registro o una coil fijos. Nunca dejes que el mensaje elija la dirección.
- Publica el resultado en un topic
.../pump1/cmd/ackpara que el emisor sepa si la escritura se realizó. - Usa un broker privado con autenticación para cualquier topic que pueda cambiar el estado de un equipo.
// Nodo function "validate pump command" -> Modbus Write (FC5, coil 0)
const cmd = String(msg.payload).trim().toUpperCase();
if (cmd !== "START" && cmd !== "STOP") {
node.warn("Rejected command: " + cmd);
return null;
}
msg.payload = cmd === "START";
return msg;
Modbus RTU sobre RS-485
Para dispositivos serie, cambia solo la configuración del cliente: tipo Serial, un puerto como /dev/ttyUSB0, velocidad en baudios, paridad y bits de parada que coincidan con el dispositivo (9600 8N1 es habitual) y el tipo serie RTU. Cada dispositivo del bus tiene su propio unit ID. Mantén frecuencias de consulta conservadoras, porque un bus RS-485 atiende a todos los dispositivos por turnos, y un timeout en uno retrasa a los demás. La guía de Modbus RTU a MQTT trata el cableado y la terminación.
Solución de problemas en flujos de Node-RED Modbus
| Síntoma | Causa probable | Solución |
|---|---|---|
| Errores de timeout | IP, puerto o unit ID incorrectos; firewall | Comprueba primero con una herramienta Modbus de escritorio |
| Illegal data address (excepción 2) | Dirección desplazada en uno o código de función incorrecto | Usa direcciones en base 0; holding registers frente a input registers |
| Valores desplazados un registro | Notación 40001 introducida literalmente | Resta el desplazamiento |
| Valores float absurdos | El orden de palabras es distinto | Intercambia los dos registros |
| Los números negativos aparecen como 65.000 o más | Valor con signo leído como sin signo | Aplica la conversión int16 |
| El dispositivo deja de responder bajo carga | Demasiados clientes o consultas demasiado rápidas | Un solo gateway, consultas más lentas, lecturas agrupadas en bloques |
Del gateway a producción
Una vez que los valores fluyen, el gateway solo es tan útil como el broker en el que publica. En un sitio con varios gateways Node-RED, un cliente SCADA y un historiador, todos deberían compartir un único broker autenticado accesible tanto desde la red de planta como desde la de oficina. Un broker MQTT en la nube es una forma sencilla de conseguirlo, porque cada gateway se conecta hacia fuera por el puerto 8883 y no se abren puertos de entrada hacia la red OT. ConnectMQ también reconoce topics al estilo ISA-95 como plant1/utilities/pump1/telemetry y muestra los dispositivos por esos niveles en su portal. Si prefieres no gestionar ninguna infraestructura de broker, un broker MQTT gestionado te quita de encima el parcheado y la monitorización. Con los valores de bombas y variadores ya en MQTT, también puedes animar un gemelo digital 3D de la instalación, por ejemplo de una planta de agua en Querétaro o en Valencia.
Otros protocolos industriales siguen el mismo patrón: consulta Node-RED de OPC UA a MQTT para controladores modernos y Node-RED con Siemens S7 para PLCs S7. Para configurar el broker en Node-RED, el tutorial de Node-RED con MQTT tiene lo básico.
Conecta tu gateway Modbus a ConnectMQ
Regístrate en app.connectmq.com con el plan gratuito Serverless, sin tarjeta de crédito. Crea un broker, pega el username, la password y el client ID generados en el nodo de configuración de Node-RED, activa TLS en el puerto 8883 y tus valores Modbus se publicarán de forma privada y cifrada. Usa el explorador de topics en vivo de ConnectMQ para verificar cada registro que hayas decodificado.
Preguntas frecuentes
¿Admite node-red-contrib-modbus tanto Modbus TCP como RTU?
Sí. El nodo de configuración Modbus Client admite conexiones TCP y serie, incluido RTU mediante adaptadores RS-485. Los nodos de lectura y escritura funcionan igual en ambos casos.
¿Por qué mi dirección Modbus está desplazada en uno en Node-RED?
Las hojas de datos suelen usar la numeración estilo 40001, que empieza en uno, mientras que Node-RED usa direcciones de protocolo que empiezan en cero. El holding register 40001 es la dirección 0 en el nodo Modbus Read.
¿Cómo leo un float de 32 bits desde Modbus en Node-RED?
Lee dos registros consecutivos, escríbelos en un buffer de 4 bytes y llama a readFloatBE. Si el resultado parece incorrecto, intercambia las dos palabras, porque algunos dispositivos usan el orden de palabras opuesto.
¿Con qué frecuencia debe Node-RED consultar un dispositivo Modbus?
Consulta tan despacio como permita tu caso de uso, a menudo cada 1 a 10 segundos para monitorización. Agrupa los registros en bloques y publica por excepción para que tanto el dispositivo como el broker soporten poca carga.



