Guía completa: Modbus RTU y TCP con Python y Node-RED
Para escribir y leer un coil por Modbus RTU desde un Raspberry PLC en Node-RED, instala los nodos node-red-contrib-modbus y crea un Modbus-Client con Type Serial, el puerto RS-485 de tu PLC (/dev/ttySC0 o /dev/ttySC2), Serial type RTU y los parámetros serie del esclavo. Después usa un nodo Modbus Write con FC 5 y un nodo Modbus Read con FC 1, los dos con el Unit-Id del esclavo. El flujo de este post se ha probado con Node-RED 4.1 y node-red-contrib-modbus 5 contra un esclavo simulado.
Esta es la parte 1 de la serie Modbus y Raspberry PLC. Las partes 1 y 2 usan Node-RED, y las partes 3 y 4 usan Python. Todos los detalles, como timeouts, direccionamiento, valores de 32 bits y diagnóstico de fallos, están en la guía de Modbus para PLC con Raspberry Pi.
Qué necesitas
- Un Raspberry PLC con Node-RED. Es el maestro Modbus RTU. Si todavía no tienes Node-RED instalado, consulta cómo instalar Node-RED en un Raspberry PLC.
- Un esclavo Modbus RTU: cualquier equipo con RS-485, u otro PLC de Industrial Shields que trabaje como esclavo, por ejemplo un PLC basado en Arduino o ESP32 como en Modbus RTU con PLC basados en Arduino y ESP32.
- Cableado RS-485: A+ con A+ y B- con B-, con terminación en los extremos del bus.
- Los parámetros serie del esclavo: dirección (Unit-Id), velocidad, paridad y bits de parada. Este post usa el Unit-Id 31 a 38400 bit/s, 8N1. Cámbialos para que coincidan con tu esclavo.
Arranca Node-RED e instala los nodos Modbus
Arranca Node-RED en un terminal del PLC:
node-red-start
Abre el editor en un navegador. Desde otro ordenador, usa http://PLC_IP_ADDRESS:1880. En el propio PLC, con una pantalla conectada, usa http://127.0.0.1:1880.
Abre Menu, Manage palette (o pulsa Alt+Mayús+P), ve a la pestaña Install, busca node-red-contrib-modbus e instálalo. La versión 5.62 de estos nodos necesita Node-RED 4 o posterior y Node.js 22 o posterior, así que comprueba antes node -v.
Configura el Modbus-Client para RS-485
Arrastra un nodo Modbus Write al flujo, haz doble clic y pulsa el icono del lápiz junto a Server. Se abre el Modbus-Client: la conexión que comparten todos los nodos Modbus del flujo.
| Campo | Valor | Nota |
|---|---|---|
| Type | Serial | Modbus RTU va por el puerto serie |
| Serial port | /dev/ttySC0 o /dev/ttySC2 | v3, y v4 sin expansión: /dev/ttySC0 y /dev/ttySC1. v6, y v4 con expansión: /dev/ttySC2 y /dev/ttySC3. Compruébalo con ls /dev/ttySC* |
| Serial type | RTU | |
| Baud rate | 38400 | Igual que en el esclavo |
| Bits de datos, paridad, bits de parada | 8, None, 1 | Igual que en el esclavo |
| Unit-Id | 1 | Unit-Id por defecto para los nodos que dejan vacío su propio Unit-Id. No es la dirección del maestro: en Modbus RTU el maestro no tiene dirección |
Pon un nombre a la configuración y pulsa Update. Si el flujo usa varios puertos RS-485, crea un Modbus-Client para cada puerto y comprueba que cada nodo usa el correcto.
Escribe un coil con Modbus Write (FC 5)
Configura el nodo Modbus Write:
- Unit-Id:
31, la dirección del esclavo. - FC:
FC 5: Force Single Coil. - Address:
0. El coil 00001 del manual del equipo es la dirección 0. - Quantity:
1. - Server: el Modbus-Client del paso anterior.
Añade un nodo Inject, pon en msg.payload el booleano true y conéctalo a Modbus Write. Añade un segundo nodo Inject con el booleano false. Cada clic en un nodo Inject envía una petición de escritura: true activa el coil y false lo desactiva.

Lee el coil con Modbus Read (FC 1)
Añade un nodo Modbus Read con el mismo Modbus-Client:
- Unit-Id:
31, el mismo esclavo. - FC:
FC 1: Read Coil Status. - Address:
0. Quantity:1. - Poll Rate:
1segundo.
Conecta un nodo Debug a la primera salida de Modbus Read, que da el array de datos. La segunda salida da la respuesta Modbus en bruto. Pulsa Deploy. El panel Debug muestra un mensaje nuevo cada segundo. La respuesta lleva los coils en bytes completos, así que el array tiene 8 valores aunque leas un solo coil. El primer valor es el coil 0.
En nuestra prueba, después de un clic en el nodo Inject true, las lecturas siguientes mostraron un array que empieza por true, y después de un clic en false, un array que empieza por false.

Importa el flujo completo
Para saltarte los pasos manuales, abre Menu, Import, pega este flujo y pulsa Import. Después cambia el puerto serie y el Unit-Id por los valores de tu PLC y de tu esclavo.
[{"id":"5e1ff7e5bdc20aad","type":"tab","label":"Flow 1","disabled":false,"info":"","env":[]},{"id":"bf0737313ed47579","type":"modbus-write","z":"5e1ff7e5bdc20aad","name":"","showStatusActivities":true,"showErrors":true,"showWarnings":true,"unitid":"31","dataType":"Coil","adr":"0","quantity":"1","server":"d71fcab96da43404","emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"startDelayTime":"","x":660,"y":300,"wires":[[],[]]},{"id":"ecf9141ee03c86bc","type":"inject","z":"5e1ff7e5bdc20aad","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":510,"y":280,"wires":[["bf0737313ed47579"]]},{"id":"d3759149be3b0b7e","type":"inject","z":"5e1ff7e5bdc20aad","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":510,"y":320,"wires":[["bf0737313ed47579"]]},{"id":"8f43e9aa567f6f3c","type":"modbus-read","z":"5e1ff7e5bdc20aad","name":"","topic":"","showStatusActivities":false,"logIOActivities":false,"showErrors":false,"showWarnings":true,"unitid":"31","dataType":"Coil","adr":"0","quantity":"1","rate":"1","rateUnit":"s","delayOnStart":false,"startDelayTime":"","server":"d71fcab96da43404","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":530,"y":400,"wires":[["d2d923f66ea77a31"],[]]},{"id":"d2d923f66ea77a31","type":"debug","z":"5e1ff7e5bdc20aad","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":680,"y":400,"wires":[]},{"id":"d71fcab96da43404","type":"modbus-client","name":"ModbusRTU","clienttype":"simpleser","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"127.0.0.1","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttySC0","serialType":"RTU","serialBaudrate":"38400","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","serialAsciiResponseStartDelimiter":"0x3A","unit_id":1,"commandDelay":1,"clientTimeout":1000,"reconnectOnTimeout":true,"reconnectTimeout":2000,"parallelUnitIdsAllowed":true,"showWarnings":true,"showLogs":true}]
Cuando el esclavo no responde
| Síntoma | Qué comprobar |
|---|---|
| No llegan mensajes al panel Debug, Modbus Read muestra un timeout | Unit-Id del esclavo, velocidad, paridad y bits de parada, A+ y B- invertidos, terminación en los extremos del bus |
| El Modbus-Client no puede abrir el puerto | El nombre del puerto para tu versión de PLC, con ls /dev/ttySC* |
| Algunas lecturas fallan y otras funcionan | Dos programas en el mismo puerto. Solo un programa puede usar un puerto serie: para los scripts de Python u otros flujos que lo usen |
| Código de excepción 02 (dirección de datos no válida) | La dirección del coil no existe en el esclavo. Consulta la tabla de coils en el manual del esclavo |
| Los nodos Modbus no se instalan o no aparecen | Versiones de Node-RED y Node.js: node-red-contrib-modbus 5.62 necesita Node-RED 4 y Node.js 22 o posterior |
Para ver los errores en el panel Debug, activa Show Errors en los nodos Modbus Read y Modbus Write.
Qué hemos probado. Node-RED 4.1.17 sobre Node.js 22 (Linux) con node-red-contrib-modbus 5.62.1 y 5.23.3. La versión 5.62.1 se ejecutó con la librería modbus-serial 8.0.25 de npm en lugar de su propia versión modificada de esa librería. El flujo de este post, cambiando solo el puerto serie por un enlace serie virtual, se ejecutó contra un esclavo Modbus RTU simulado (pymodbus 3.16.1, Unit-Id 31, 38400 8N1). Los nodos Inject true y false activaron y desactivaron el coil 0, y Modbus Read mostró el nuevo valor en menos de un segundo. No hemos ejecutado esta actualización en un Raspberry PLC real: las dos capturas son de la primera versión del post.
¿Qué controlador para tu proyecto Modbus? Un Raspberry PLC ejecuta Node-RED y Python sobre Linux. Un PLC basado en Arduino o ESP32, un PLC clásico, un PC industrial o un gateway también pueden ser el maestro Modbus. La guía comparativa da los criterios para elegir.
Compara controladores industrialesAbre la guía completa de Modbus
Llévalo a la práctica
Tras los primeros pasos de Modbus en un Raspberry PLC: una red real, un simulador para romperla y un ejemplo de Modbus TCP.
Caso de estudio
Muchos termómetros. Una sola red Modbus.
Varios PLC M-Duino locales leen termómetros Modbus RTU por RS-485 y un PLC principal recoge las lecturas por Modbus TCP.
Lee el caso de estudio
Simulador
Haz funcionar una red Modbus RTU y TCP y encuentra el fallo
Un M-Duino 21 pregunta a cuatro esclavos Modbus RTU por RS-485 y sirve los datos por Modbus TCP. Apaga un esclavo o cambia la velocidad y encuentra el fallo en las tramas.
Abre el simulador
PLC Solution Library
Leer un inversor Fronius por SunSpec Modbus TCP
Una Raspberry Pi lee la potencia AC de un inversor y el consumo del Smart Meter por Modbus TCP, para calcular el excedente solar en vivo.
Ver el ejemplo
Preguntas frecuentes sobre Modbus RTU en Node-RED
¿Qué es el Unit-Id de la configuración del Modbus-Client?
Es el Unit-Id por defecto para los nodos Modbus que dejan vacío su propio Unit-Id. Un nodo con su propio Unit-Id usa ese valor. El maestro Modbus RTU no tiene dirección, así que este campo nunca identifica al Raspberry PLC.
¿Qué puerto RS-485 selecciono en un Raspberry PLC?
/dev/ttySC0 o /dev/ttySC1 en el v3, y en el v4 sin expansión. /dev/ttySC2 o /dev/ttySC3 en el v6, y en el v4 con expansión. ls /dev/ttySC* lista los puertos de tu PLC.
¿Por qué el panel Debug muestra 8 valores para un solo coil?
Porque la respuesta Modbus lleva los coils en bytes completos, y el nodo da todos los bits de esos bytes. Usa solo el primer valor.
¿Pueden un flujo de Node-RED y un script de Python usar el mismo puerto RS-485?
No a la vez. Dos programas en un mismo puerto serie se quitan bytes el uno al otro, y aparecen timeouts y errores de CRC. Para el flujo antes de ejecutar el script de la parte 3.
¿Cómo hago lo mismo por Ethernet?
Con un Modbus-Client de Type TCP y la dirección IP del esclavo. Consulta la parte 2: Modbus TCP y Node-RED.
Más sobre Modbus en el Raspberry PLC
- Guía de Modbus TCP y RTU para PLC con Raspberry Pi: ejemplos con Python y Node-RED
- Varios esclavos Modbus RTU en Node-RED
- RS-485 con Raspberry PLC: bornes y prueba en bucle
- Parte 2: Modbus TCP y Node-RED
- Parte 3: Modbus RTU y Python
- Parte 4: Modbus TCP y Python
- Trama Modbus RTU, códigos de función y CRC
- Cómo elegir un controlador industrial: PLC, controlador embebido, IPC o gateway
Elige el modelo y la combinación de E/S en la familia Raspberry PLC.