Ir al contenido

Introducción a Modbus y Raspberry PLC. Parte 2

Escribe y lee un coil por Modbus TCP en Node-RED con node-red-contrib-modbus
21 de febrero de 2024 por
Introducción a Modbus y Raspberry PLC. Parte 2
Boot & Work Corp. S.L, Bernat Brunet Pedra

Guía completa: Modbus RTU y TCP con Python y Node-RED

Para escribir y leer un coil por Modbus TCP desde un Raspberry PLC en Node-RED, instala los nodos node-red-contrib-modbus y crea un Modbus-Client con Type TCP, la dirección IP del esclavo y el puerto 502. Después usa un nodo Modbus Write con FC 5 y un nodo Modbus Read con FC 1. Pon a los dos nodos el mismo Unit-Id, el que espera el equipo (a menudo 1 o 255). 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 2 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.

ParteProtocoloHerramienta
Parte 1Modbus RTUNode-RED
Parte 2 (este post)Modbus TCPNode-RED
Parte 3Modbus RTUPython
Parte 4Modbus TCPPython

Qué necesitas

  • Un Raspberry PLC con Node-RED. Es el cliente (maestro) Modbus TCP. Si todavía no tienes Node-RED instalado, consulta cómo instalar Node-RED en un Raspberry PLC.
  • Un servidor (esclavo) Modbus TCP: un contador, un variador, otro PLC o un gateway. Para probar sin equipo, ejecuta en el mismo PLC el servidor Modbus TCP en Python de la guía de Modbus y usa la dirección 127.0.0.1 y el puerto 5020.
  • Una conexión Ethernet entre el PLC y el esclavo, los dos en la misma subred. Por defecto, eth0 del Raspberry PLC tiene la dirección 10.10.10.20. Para cambiarla, consulta cómo configurar una IP fija en un Raspberry PLC.
  • La dirección IP, el puerto TCP y el Unit-Id del esclavo. Este post usa un esclavo en 10.10.10.4, puerto 502, Unit-Id 1.

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. Si has hecho la parte 1, los nodos ya están instalados.

Configura el Modbus-Client para TCP

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.

CampoValorNota
TypeTCPModbus TCP va por Ethernet
Host10.10.10.4Dirección IP del esclavo
Port502El puerto Modbus TCP de la mayoría de equipos. Usa 5020 para el servidor de prueba en Python de la guía
TCP TypeDEFAULT
Unit-Id1Unit-Id por defecto para los nodos que dejan vacío su propio Unit-Id

Pon un nombre a la configuración y pulsa Update. Para cada esclavo con otra dirección IP, crea un Modbus-Client más.

El Unit-Id en Modbus TCP

En Modbus TCP, la dirección IP selecciona el equipo. El Unit-Id sigue viajando en la cabecera de cada petición, y selecciona un equipo detrás de un gateway, por ejemplo un esclavo RTU detrás de un gateway Modbus TCP a RTU. Muchos equipos lo ignoran, y otros solo responden a 1 o a 255. Usa el valor del manual del equipo, y el mismo valor en todos los nodos.

La primera versión de este flujo tenía Unit-Id 1 en Modbus Write y 31 en Modbus Read. Contra nuestro esclavo simulado, que comprueba el Unit-Id, la escritura funcionó, la lectura recibió una respuesta de excepción y el panel Debug no mostró nada. El flujo de este post usa el Unit-Id 1 en los dos nodos.

Escribe un coil con Modbus Write (FC 5)

Configura el nodo Modbus Write:

  • Unit-Id: 1.
  • 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 por Ethernet: true activa el coil y false lo desactiva.

Flujo de Node-RED: un nodo Inject con el valor true conectado a un nodo Modbus Write con el estado active
Un nodo Inject con el valor true conectado a Modbus Write.

Lee el coil con Modbus Read (FC 1)

Añade un nodo Modbus Read con el mismo Modbus-Client:

  • Unit-Id: 1, el mismo que en Modbus Write.
  • FC: FC 1: Read Coil Status.
  • Address: 0. Quantity: 1.
  • Poll Rate: 1 segundo.

Conecta un nodo Debug a la primera salida de Modbus Read, que da el array de datos, y pulsa Deploy. El panel Debug muestra un mensaje nuevo cada segundo. El array tiene 8 valores, porque la respuesta lleva los coils en bytes completos. 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.

Flujo completo de Node-RED: nodos Inject true y false conectados a Modbus Write, y Modbus Read conectado a un nodo debug
El flujo completo: dos nodos Inject (true y false) alimentan Modbus Write, y Modbus Read consulta el coil cada segundo y lo envía a un nodo Debug. El flujo es el mismo que en la parte 1, solo cambia el Modbus-Client.

Importa el flujo completo

Para saltarte los pasos manuales, abre Menu, Import, pega este flujo y pulsa Import. Después cambia la dirección IP, el puerto y el Unit-Id por los valores de tu esclavo.

[{"id":"3c784f724b6bb9da","type":"modbus-write","z":"66f823dbe170e15a","name":"","showStatusActivities":true,"showErrors":true,"showWarnings":true,"unitid":"1","dataType":"Coil","adr":"0","quantity":"1","server":"a0cecbb6e2cd25e8","emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"startDelayTime":"","x":560,"y":220,"wires":[[],[]]},{"id":"3459adaa212c8dd3","type":"inject","z":"66f823dbe170e15a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":390,"y":200,"wires":[["3c784f724b6bb9da"]]},{"id":"526661ccfde56f87","type":"inject","z":"66f823dbe170e15a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":390,"y":240,"wires":[["3c784f724b6bb9da"]]},{"id":"f5c9e191b12f95d7","type":"modbus-read","z":"66f823dbe170e15a","name":"","topic":"","showStatusActivities":false,"logIOActivities":false,"showErrors":false,"showWarnings":true,"unitid":"1","dataType":"Coil","adr":"0","quantity":"1","rate":"1","rateUnit":"s","delayOnStart":false,"startDelayTime":"","server":"a0cecbb6e2cd25e8","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":430,"y":320,"wires":[["3ff9cb94cb91b7e8"],[]]},{"id":"3ff9cb94cb91b7e8","type":"debug","z":"66f823dbe170e15a","name":"debug 1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":580,"y":320,"wires":[]},{"id":"a0cecbb6e2cd25e8","type":"modbus-client","name":"","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"10.10.10.4","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","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íntomaQué comprobar
Conexión rechazadaEl servidor no está en marcha, el puerto no es el correcto o un cortafuegos lo bloquea
Sin conexión, timeoutDirección IP del esclavo, cable, subred. Dos Raspberry PLC nuevos en la misma red tienen los dos 10.10.10.20 en eth0: cambia uno de ellos. Compruébalo con ip a y ping
La escritura funciona pero Debug no muestra nadaEl Unit-Id de Modbus Read. Usa el mismo Unit-Id que en Modbus Write, el del manual del equipo
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
Código de excepción 0B (el equipo destino del gateway no responde)Un gateway Modbus TCP a RTU no ha recibido respuesta del esclavo RTU con ese Unit-Id

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 host y el puerto por 127.0.0.1 y un puerto libre, se ejecutó contra un esclavo Modbus TCP simulado (pymodbus 3.16.1, Unit-Id 1). Los nodos Inject true y false activaron y desactivaron el coil 0, y Modbus Read mostró el nuevo valor en menos de un segundo. La primera versión del flujo, con Unit-Id 31 en Modbus Read, no dio datos en Debug contra el mismo esclavo. No hemos ejecutado esta actualización en un Raspberry PLC real: las dos capturas son de la primera versión del post.

Encuentra el fallo en una red Modbus RTU y TCP. En el simulador, 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 de red ModbusAbre la guía completa de Modbus

Preguntas frecuentes sobre Modbus TCP en Node-RED

¿Necesito el Unit-Id con Modbus TCP?

La dirección IP selecciona el equipo, pero el Unit-Id sigue estando en cada petición. Muchos equipos lo ignoran, algunos solo responden a 1 o 255, y un gateway lo usa para seleccionar el esclavo RTU que tiene detrás. Usa el valor del manual del equipo en todos los nodos.

¿Qué puerto TCP usa Modbus TCP?

El puerto estándar es el 502. Algunos equipos permiten cambiarlo, y los servidores de prueba en Linux suelen usar el 5020, porque solo root puede abrir puertos por debajo de 1024.

¿Pueden varios clientes leer el mismo esclavo Modbus TCP?

Sí, a diferencia de Modbus RTU, donde hay un solo maestro por línea. Un SCADA y el flujo de Node-RED pueden leer el mismo servidor a la vez, si el equipo acepta varias conexiones.

¿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.

¿Cómo hago lo mismo en Python?

Con ModbusTcpClient de pymodbus. Consulta la parte 4: Modbus TCP y Python.

Buscar en nuestro blog

Introducción a Modbus y Raspberry PLC. Parte 2
Boot & Work Corp. S.L, Bernat Brunet Pedra 21 de febrero de 2024
Compartir esta publicación

¿Estás buscando tu Controlador Lógico Programable ideal?

Echa un vistazo a esta comparativa de producto de varios controladores industriales basados en Arduino.

Comparamos entradas, salidas, comunicaciones y otras especificaciones con las de los equipos de otras marcas destacadas.

Comparativa de PLCs