IoT industrial

Gateway Modbus RTU a MQTT con RS-485: cableado, ajustes y código

Conecta medidores de energía y sensores Modbus RTU sobre RS-485 a un gateway que publica sus lecturas por MQTT. Cubre cableado, terminación, ajustes serie, mapas de registros y un poller completo en Python.

Bus RS-485 de medidores de energía cableado a un pequeño gateway Linux que envía mensajes de Modbus RTU a MQTT al cloud
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

Un gateway Modbus RTU a MQTT te permite tomar lecturas de medidores de energía, caudalímetros, variadores y sensores RS-485 y publicarlas en un broker donde los dashboards y la analítica pueden usarlas. Este tutorial cubre la capa física que causa la mayoría de los problemas (cableado, terminación, ajustes serie), cómo leer el mapa de registros de un fabricante y un gateway completo en Python que sondea varios dispositivos en un mismo bus y publica sus valores por MQTT. Para seguirlo basta con una Raspberry Pi o cualquier equipo Linux con un adaptador USB a RS-485.

Qué diferencia a Modbus RTU de Modbus TCP

El modelo de datos de Modbus es el mismo: coils, discrete inputs, holding registers e input registers. Lo que cambia es el transporte. Modbus RTU funciona sobre una línea serie, casi siempre RS-485, como bus multipunto. Un maestro sondea hasta 247 dispositivos esclavos, cada uno con una dirección de unidad única. Las tramas son binarias con un CRC, y los límites de trama se definen mediante silencios en la línea.

Esto tiene consecuencias prácticas:

  • Solo un maestro por bus. Si un sistema de gestión de edificios (BMS) o un HMI ya sondea los medidores, tu gateway no puede unirse sin más a los mismos cables. Necesitas un bus separado, un dispositivo con un segundo puerto o un gateway que se sitúe delante de ambos.
  • El bus es lento y compartido. A 9600 baudios, cada petición y respuesta tarda decenas de milisegundos, y las peticiones a distintos dispositivos se serializan. La planificación del sondeo importa.
  • Los problemas eléctricos parecen problemas de software. Una terminación ausente o un par invertido producen timeouts y errores de CRC que ningún código podrá arreglar.

Si tus dispositivos están en Ethernet, usa en su lugar nuestro tutorial Modbus TCP a MQTT.

Hardware necesario

  1. Un ordenador gateway: una Raspberry Pi, un PC industrial o cualquier equipo Linux.
  2. Un adaptador USB a RS-485, idealmente aislado y con resistencias de terminación y polarización seleccionables. El aislamiento protege el gateway de las diferencias de potencial de tierra entre armarios.
  3. Cable de par trenzado para el bus, preferiblemente apantallado, con un tercer conductor para la referencia común de señal.
  4. El manual Modbus de cada dispositivo. Necesitas su mapa de registros y sus ajustes serie por defecto.

Cableado RS-485 bien hecho

RS-485 es robusto cuando se cablea como está previsto y frustrante cuando no. Sigue estas reglas:

  1. En cadena, no en estrella. Lleva el bus de un dispositivo a otro en línea. Las derivaciones cortas son tolerables; las ramas largas provocan reflexiones.
  2. Termina ambos extremos. Coloca una resistencia de 120 ohmios entre el par A/B en los dos extremos físicos del bus, y en ningún otro sitio. Muchos adaptadores y medidores tienen un interruptor o un puente de terminación.
  3. Polariza una sola vez. Las resistencias de polarización (fail-safe) mantienen la línea en un estado definido cuando nadie transmite. Actívalas en un único punto, normalmente el adaptador maestro.
  4. Conecta la referencia común. Pese al nombre de "dos hilos", conecta la masa de señal (a menudo marcada como GND, COM o C) entre dispositivos para que los receptores compartan una referencia.
  5. Ojo con el etiquetado A/B. Los fabricantes no se ponen de acuerdo en si A es positivo o negativo, y algunos usan D+ y D-. Si nada responde, intercambiar A y B en el adaptador es una primera prueba legítima.
  6. Mantenlo alejado de los cables de potencia. Tiende el bus separado de los cables de motores y variadores, y conecta la pantalla a tierra en un solo extremo.

Ajustes serie: baudios, paridad y bits de parada

Todos los dispositivos de un bus deben usar los mismos ajustes, y cada dispositivo necesita una dirección única.

Ajuste Valores habituales Notas
Velocidad (baudios) 9600, 19200, 38400 9600 y 19200 son los valores por defecto más comunes; los cables largos favorecen velocidades más bajas
Bits de datos 8 Modbus RTU siempre usa 8 bits de datos
Paridad Par, ninguna, impar La especificación Modbus usa paridad par por defecto, pero muchos dispositivos vienen sin paridad
Bits de parada 1 o 2 Sin paridad, la especificación pide 2 bits de parada, aunque muchos dispositivos aceptan 1
Dirección de unidad 1 a 247 Se configura en el panel frontal del dispositivo o por software; debe ser única en el bus

Anota los ajustes con un código corto como 9600 8E1 o 19200 8N2 y configura todos los dispositivos para que coincidan antes de conectar nada.

Cómo leer un mapa de registros

El manual de un dispositivo lista los registros en una tabla con dirección, descripción, tipo de dato, unidad y a veces un factor de escala. El ejemplo siguiente es un medidor de energía trifásico hipotético usado para este tutorial; los medidores reales difieren, así que toma siempre las direcciones de tu propio manual.

Descripción Función Dirección (base 0) Tipo Unidad
Tensión L1-N 04 input 0 float32 V
Corriente L1 04 input 6 float32 A
Potencia activa total 04 input 52 float32 W
Frecuencia 04 input 70 float32 Hz
Energía activa importada 04 input 72 float32 kWh

Tres cosas que debes comprobar en cada manual: si las direcciones tienen base cero o se escriben al estilo 30001 (resta el desplazamiento), si los valores de 32 bits usan el orden con la palabra alta primero o con la palabra baja primero, y si los registros enteros necesitan un factor de escala.

Requisitos previos en el gateway

sudo usermod -aG dialout $USER     # permitir acceso al puerto serie; después cierra sesión y vuelve a entrar
ls -l /dev/serial/by-id/           # ruta estable del dispositivo para tu adaptador
python3 -m venv ~/rtu-gw && source ~/rtu-gw/bin/activate
pip install "pymodbus[serial]>=3.8,<3.9" "paho-mqtt>=2.0"

Usa la ruta /dev/serial/by-id/... en lugar de /dev/ttyUSB0, porque los números de los dispositivos USB pueden cambiar tras un reinicio.

El código del gateway Modbus RTU a MQTT

El gateway sondea cada medidor por turnos, lee cada bloque de registros en una sola petición cuando es posible y publica un mensaje JSON por medidor y ciclo. Agrupar los valores de un medidor en un único mensaje mantiene baja la tasa de mensajes, que es lo que quieres en un bus lento y en un enlace ascendente de pago por uso.

# rtu_mqtt_gateway.py - sondear medidores Modbus RTU por RS-485, publicar en MQTT
import json, logging, struct, time, uuid
import paho.mqtt.client as mqtt
from pymodbus.client import ModbusSerialClient

logging.basicConfig(level=logging.INFO)
log = logging.getLogger("rtu-gw")

# En producción, sustituye el puerto por tu ruta /dev/serial/by-id/...
SERIAL = dict(port="/dev/ttyUSB0", baudrate=9600, parity="E", stopbits=1, bytesize=8, timeout=0.5)
POLL_SECONDS = 10
TOPIC_ROOT = "connectmq/demo/e5a1/plant1/energy"     # usa tu propio id aleatorio
MQTT_HOST, MQTT_PORT = "broker.connectmq.com", 1883   # producción: 8883 con TLS
MQTT_USER, MQTT_PASS = None, None                     # producción: "YOUR_USERNAME", "YOUR_PASSWORD"
CLIENT_ID = f"rtu-gw-{uuid.uuid4().hex[:8]}"          # producción: "YOUR_CLIENT_ID"

# Medidores en el bus: dirección de unidad Modbus -> nombre de topic
METERS = [
    {"unit": 1, "name": "mdb1_incomer"},
    {"unit": 2, "name": "mdb1_compressors"},
]


def f32(regs, offset, big_endian_words=True):
    words = regs[offset:offset + 2] if big_endian_words else regs[offset:offset + 2][::-1]
    return struct.unpack(">f", struct.pack(">HH", *words))[0]


def read_meter(client, unit):
    """Lee los input registers float32 del medidor hipotético en dos peticiones de bloque."""
    block_a = client.read_input_registers(0, count=8, slave=unit)        # tensión, corriente
    block_b = client.read_input_registers(52, count=24, slave=unit)      # potencia .. energía
    for rr in (block_a, block_b):
        if rr.isError():
            raise IOError(str(rr))
    a, b = block_a.registers, block_b.registers
    return {
        "voltage_l1": round(f32(a, 0), 1),
        "current_l1": round(f32(a, 6), 2),
        "power_total": round(f32(b, 0), 0),
        "frequency": round(f32(b, 18), 2),
        "energy_import": round(f32(b, 20), 1),
    }


def main():
    mq = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
    if MQTT_USER:
        mq.username_pw_set(MQTT_USER, MQTT_PASS)
        mq.tls_set()
    mq.will_set(f"{TOPIC_ROOT}/gateway", "offline", qos=1, retain=True)
    mq.on_connect = lambda c, u, f, rc, p: c.publish(f"{TOPIC_ROOT}/gateway", "online", qos=1, retain=True)
    mq.connect(MQTT_HOST, MQTT_PORT, keepalive=60)
    mq.loop_start()

    bus = ModbusSerialClient(**SERIAL)
    errors = {m["unit"]: 0 for m in METERS}

    while True:
        cycle_start = time.monotonic()
        if not bus.connected and not bus.connect():
            log.error("cannot open serial port %s", SERIAL["port"])
            time.sleep(5)
            continue
        for meter in METERS:
            topic = f"{TOPIC_ROOT}/{meter['name']}"
            try:
                values = read_meter(bus, meter["unit"])
                errors[meter["unit"]] = 0
                payload = {**values, "quality": "good",
                           "ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())}
                mq.publish(topic, json.dumps(payload), qos=1, retain=True)
                log.info("%s %s", meter["name"], values)
            except Exception as exc:
                errors[meter["unit"]] += 1
                log.warning("unit %s: %s", meter["unit"], exc)
                if errors[meter["unit"]] == 3:   # publicar calidad mala una vez, tras 3 fallos
                    mq.publish(topic, json.dumps({"quality": "bad"}), qos=1, retain=True)
            time.sleep(0.05)                     # pequeña pausa entre dispositivos del bus
        time.sleep(max(0, POLL_SECONDS - (time.monotonic() - cycle_start)))


if __name__ == "__main__":
    main()

Fíjate en los desplazamientos de read_meter: el bloque B empieza en el registro 52, así que la frecuencia en el registro 70 está en el desplazamiento 18 y la energía importada en el 72 está en el desplazamiento 20. Equivocarse con estos desplazamientos es el error más común al agrupar lecturas, así que imprime los registros en bruto una vez durante la puesta en marcha.

Ejecutar y verificar

Ejecuta el gateway y observa la salida desde cualquier máquina:

python rtu_mqtt_gateway.py
mosquitto_sub -h broker.connectmq.com -p 1883 -i rtu-viewer-e5a1 -t "connectmq/demo/e5a1/#" -v
connectmq/demo/e5a1/plant1/energy/mdb1_incomer {"voltage_l1": 231.4, "current_l1": 18.72, "power_total": 12890.0, "frequency": 50.01, "energy_import": 48213.6, "quality": "good", "ts": "2026-10-16T10:05:00Z"}

El servidor MQTT público gratuito lo puede leer cualquiera, así que úsalo solo para pruebas y prototipos, nunca con datos de producción. Para una planta real, sea en Monterrey o en Bilbao, crea un broker privado y cambia al puerto 8883 con tus credenciales.

Resolución de problemas de Modbus RTU

Síntoma Causa probable Qué probar
Ningún dispositivo responde A/B invertidos, baudios o paridad incorrectos, puerto equivocado Intercambia A/B, confirma 8E1 frente a 8N1, revisa /dev/serial/by-id
Algunos dispositivos responden y otros dan timeout Dirección de unidad duplicada o incorrecta, cable cortado Desconecta todos menos uno y prueba cada dispositivo
Errores de CRC intermitentes Falta de terminación, topología en estrella, ruido de los variadores Termina ambos extremos, cambia el recorrido del cable, usa par trenzado apantallado
Funciona a 9600 y falla a velocidades mayores Cable largo o cableado deficiente Quédate en la velocidad baja o arregla el cableado
Código de excepción 2 (illegal data address) Dirección fuera del mapa del dispositivo o bloque que cruza un hueco Divide el bloque en lecturas más pequeñas
Timeouts aleatorios mientras funciona otro sistema Dos maestros en el mismo bus Da al gateway su propio bus o puerto

Cómo escalar

Un único puerto RS-485 basta para unos pocos medidores. Cuando tengas decenas, considera un adaptador por cuadro eléctrico o convertidores de Modbus RTU a TCP que permitan a un solo gateway alcanzar varios buses por Ethernet, y reutiliza entonces el puente TCP. Publicar ante cambios en lugar de en cada ciclo, como se describe en nuestra guía sobre el informe por excepción, también mantiene tranquilo el enlace ascendente. Para plantas remotas con datos móviles, consulta MQTT sobre redes celulares.

Un broker MQTT gestionado como ConnectMQ elimina la necesidad de operar un servidor para estos gateways. El plan Serverless gratuito soporta hasta 25 conexiones, suficiente para un piloto con varios gateways, y el plan Serverless de pago escala hasta 1.000. Las reglas de alerta por umbral del portal pueden vigilar valores como power_total y registrar una alerta cuando se supera un límite, y los mismos topics pueden alimentar un dashboard o un gemelo digital 3D que muestre el consumo sobre cada cuadro eléctrico. Para la arquitectura que hay detrás de todo esto, lee la guía de IoT industrial con MQTT.

Empieza con ConnectMQ

Pon hoy mismo en línea tus medidores RS-485. Regístrate en app.connectmq.com, elige el plan Serverless gratuito (sin tarjeta de crédito), crea un broker y copia en el script del gateway el nombre de usuario, la contraseña y el client ID generados. Las lecturas de tus medidores aparecerán en el explorador de topics en vivo de ConnectMQ en cuanto termine el primer sondeo.

Preguntas frecuentes

¿Cuántos dispositivos Modbus RTU admite un bus RS-485?

Las direcciones Modbus permiten hasta 247 dispositivos, pero el límite eléctrico depende de la carga de los transceptores; el RS-485 estándar admite 32 cargas unitarias, y muchos transceptores modernos presentan cargas fraccionarias. En la práctica, el tiempo de sondeo suele convertirse antes en el límite.

¿Puedo leer dispositivos Modbus RTU que ya sondea un BMS?

No en el mismo bus, porque Modbus RTU solo permite un maestro. Usa un segundo puerto del dispositivo, una salida independiente del medidor o pide al integrador del BMS que exponga los datos de otra forma.

¿Qué velocidad en baudios debo usar para Modbus RTU?

La que soporten todos los dispositivos del bus, normalmente 9600 o 19200. Las velocidades bajas toleran mejor los cables largos y los entornos con más ruido.

¿Un gateway Modbus RTU debe publicar en cada sondeo?

No necesariamente. Agrupar los valores de un dispositivo en un mensaje por ciclo, o publicar solo ante cambios significativos, mantiene bajo el volumen de mensajes sin perder información.

Artículos relacionados

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.