Modbus es un protocolo de comunicación maestro-esclavo utilizado en todo el mundo en la automatización industrial. Funciona sobre dos transportes: líneas serie (Modbus RTU, vía RS-485) y Ethernet (Modbus TCP). Ambos están soportados de forma nativa en los PLCs Raspberry Pi de Industrial Shields — RTU a través del puerto RS-485 y TCP a través de la interfaz Ethernet.
En esta guía encontrarás ejemplos funcionales en Python y Node-RED para ambos protocolos, probados en hardware PLC Raspberry Pi con pymodbus 3.x.
pip install --upgrade pymodbus antes de continuar.
Requisitos previos
Antes de ejecutar estos ejemplos, asegúrate de tener lo siguiente configurado:
- Cómo trabajar con RS-485 en un Raspberry PLC — necesario para Modbus RTU
- Configuración Ethernet para tu modelo de Raspberry PLC (consulta la Guía del Usuario de tu unidad)
- Cómo instalar Node-RED en un Raspberry Pi PLC — viene preinstalado en la imagen del SO de Industrial Shields
Instala pymodbus si aún no está disponible:
pip install pymodbus
Modbus RTU con Python
Esclavo (servidor)
Ejecuta este código en el Raspberry Pi PLC que actuará como esclavo. Inicia un servidor Modbus RTU en el puerto RS-485.

from pymodbus.server import StartSerialServer
from pymodbus.framer import FramerType
from pymodbus.datastore import (
ModbusServerContext,
ModbusSequentialDataBlock,
ModbusSlaveContext
)
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [0] * 10000),
co=ModbusSequentialDataBlock(0, [0] * 10000),
hr=ModbusSequentialDataBlock(0, [0] * 10000),
ir=ModbusSequentialDataBlock(0, [0] * 10000)
)
context = ModbusServerContext(slaves=store, single=True)
StartSerialServer(
context=context,
port='/dev/ttySC0', # RS-485 port on Raspberry PLC 21. Use /dev/ttySC2 for other models.
framer=FramerType.RTU,
baudrate=9600
)
Maestro (cliente)
Ejecuta este código en el segundo Raspberry Pi PLC. Se conecta al esclavo y lee y escribe registros holding.
from pymodbus.client import ModbusSerialClient
from pymodbus.framer import FramerType
client = ModbusSerialClient(
port='/dev/ttySC0',
framer=FramerType.RTU,
baudrate=9600,
timeout=3
)
if not client.connect():
print("Connection failed. Check RS-485 wiring and port.")
exit()
SLAVE_ID = 1
REGISTER_ADDRESS = 0
data_to_write = [10, 20, 30, 40, 50]
# Write registers
response = client.write_registers(REGISTER_ADDRESS, data_to_write, slave=SLAVE_ID)
if not response.isError():
print("Write successful")
else:
print("Write error:", response)
# Read registers
response = client.read_holding_registers(REGISTER_ADDRESS, count=5, slave=SLAVE_ID)
if not response.isError():
print("Read successful:", response.registers)
else:
print("Read error:", response)
client.close()
Cableado: Conecta RS-485 A+ con A+ y B- con B- entre las dos unidades. Ambos PLCs utilizan /dev/ttySC0 en este ejemplo.
Salida esperada cuando la comunicación funciona correctamente:
Write successful
Read successful: [10, 20, 30, 40, 50]
Modbus RTU con Node-RED
Este ejemplo utiliza Node-RED como maestro y el esclavo Python del apartado anterior como servidor.
- Instala el paquete
node-red-contrib-modbusdesde el gestor de paletas de Node-RED. - Construye el siguiente flujo: Inject → Function → Modbus Write y Modbus Read → Debug
- Configura el nodo Function para incrementar un contador en cada activación:
if (context.hasOwnProperty('num')) {
context.num = context.num + 1;
} else {
context.num = 0;
}
msg.payload = context.num;
return msg;
- Configura el nodo Modbus Write:
- Tipo de servidor:
Serial - Puerto serie:
/dev/ttySC0 - Tipo serie:
RTU - Velocidad en baudios:
9600 - Código de función:
FC 6: Preset Single Register - Dirección de registro:
0
- Tipo de servidor:
- Configura el nodo Modbus Read con los mismos ajustes de servidor:
- Código de función:
FC 3: Read Holding Registers - Dirección:
0, Cantidad:1 - Intervalo de lectura:
5000 ms
- Código de función:
El flujo escribirá y leerá el registro 0 cada 5 segundos. Utiliza el esclavo Python de la sección anterior como servidor.
Modbus TCP con Python
Prueba con un solo dispositivo
No necesitas dos PLCs Raspberry Pi para probar Modbus TCP. Puedes ejecutar tanto el esclavo como el maestro en la misma unidad usando localhost como dirección.
Abre dos sesiones de terminal en el mismo PLC. Ejecuta el esclavo en la primera y el maestro en la segunda.
Esclavo (servidor)
from pymodbus.server import StartTcpServer
from pymodbus.datastore import (
ModbusServerContext,
ModbusSequentialDataBlock,
ModbusSlaveContext
)
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [0] * 10000),
co=ModbusSequentialDataBlock(0, [0] * 10000),
hr=ModbusSequentialDataBlock(0, [0] * 10000),
ir=ModbusSequentialDataBlock(0, [0] * 10000)
)
context = ModbusServerContext(slaves=store, single=True)
# Listens on all interfaces, port 502
# For single-device testing, use address=("localhost", 502)
StartTcpServer(context=context, address=("0.0.0.0", 502))
Maestro (cliente)
from pymodbus.client import ModbusTcpClient
# Replace with the slave IP, or use "localhost" for single-device testing
client = ModbusTcpClient(host="192.168.1.100", port=502, timeout=3)
if not client.connect():
print("Connection failed. Check IP address and network configuration.")
exit()
SLAVE_ID = 1
REGISTER_ADDRESS = 0
data_to_write = [10, 20, 30, 40, 50]
# Write registers
response = client.write_registers(REGISTER_ADDRESS, data_to_write, slave=SLAVE_ID)
if not response.isError():
print("Write successful")
else:
print("Write error:", response)
# Read registers
response = client.read_holding_registers(REGISTER_ADDRESS, count=5, slave=SLAVE_ID)
if not response.isError():
print("Read successful:", response.registers)
else:
print("Read error:", response)
client.close()
Nota sobre los puertos Ethernet: Los PLCs Raspberry Pi tienen dos interfaces Ethernet (eth0 y eth1). Para conexiones entre dispositivos, utiliza el conector Ethernet superior en ambas unidades, o ajusta la configuración IP según tu cableado.

Modbus TCP con Node-RED
Este flujo utiliza Node-RED como maestro TCP. Puedes usar el esclavo Python anterior como servidor.
- Instala
node-red-contrib-modbussi aún no está instalado. - Construye el mismo flujo que en el ejemplo RTU: Inject → Function → Modbus Write y Modbus Read → Debug
- Configura el nodo Modbus Write:
- Tipo de servidor:
TCP - Host: dirección IP del esclavo Modbus TCP (o
localhostpara pruebas en un solo dispositivo) - Puerto:
502 - Código de función:
FC 6: Preset Single Register - Dirección de registro:
0
- Tipo de servidor:
- Configura el nodo Modbus Read con los mismos ajustes de servidor TCP:
- Código de función:
FC 3: Read Holding Registers - Dirección:
0, Cantidad:1 - Intervalo de lectura:
5000 ms
- Código de función:
El flujo lee y escribe el registro 0 cada 5 segundos desde el esclavo TCP.
Gestión de errores y robustez de la conexión
En entornos de producción, las conexiones se pierden y los dispositivos se reinician. Los ejemplos anteriores incluyen un parámetro básico timeout y una comprobación explícita de la conexión. Para aplicaciones críticas, añade un bucle de reintento:
import time
from pymodbus.client import ModbusTcpClient
MAX_RETRIES = 5
RETRY_DELAY = 2 # seconds
client = ModbusTcpClient(host="192.168.1.100", port=502, timeout=3)
for attempt in range(MAX_RETRIES):
if client.connect():
print("Connected")
break
print(f"Connection attempt {attempt + 1} failed. Retrying in {RETRY_DELAY}s...")
time.sleep(RETRY_DELAY)
else:
print("Could not connect after maximum retries.")
exit()
# ... your read/write logic ...
client.close()
Posts relacionados
- Introducción a Modbus y Raspberry PLC — Parte 1
- Introducción a Modbus y Raspberry PLC — Parte 2
- Introducción a Modbus y Raspberry PLC — Parte 3
- Introducción a Modbus y Raspberry PLC — Parte 4
- Cómo usar pymodbus con Raspberry Pi PLC
- Uso de múltiples esclavos Modbus RTU en Node-RED
→ Explora la gama completa de PLCs Raspberry Pi en industrialshields.com