IoT industrial

Gateway Modbus RTU para MQTT com RS-485: Fiação, Configurações e Código

Conecte medidores de energia e sensores Modbus RTU em RS-485 a um gateway que publica as leituras via MQTT. Aborda fiação, terminação, configurações seriais, mapas de registradores e um poller completo em Python.

Barramento RS-485 de medidores de energia ligado a um pequeno gateway Linux que envia mensagens de Modbus RTU para MQTT até a nuvem
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

Um gateway Modbus RTU para MQTT permite coletar leituras de medidores de energia, medidores de vazão, inversores e sensores em RS-485 e publicá-las em um broker onde dashboards, ferramentas de analytics e até um gêmeo digital da planta podem usá-las. Este tutorial aborda a camada física que causa a maioria dos problemas (fiação, terminação, configurações seriais), como ler o mapa de registradores de um fabricante e um gateway completo em Python que faz polling de vários dispositivos em um barramento e publica os valores via MQTT. Um Raspberry Pi ou qualquer máquina Linux com um adaptador USB para RS-485 é suficiente para acompanhar.

O que diferencia o Modbus RTU do Modbus TCP

O modelo de dados Modbus é o mesmo: coils, discrete inputs, holding registers e input registers. O que muda é o transporte. O Modbus RTU roda sobre uma linha serial, quase sempre RS-485, como um barramento multiponto. Um mestre faz polling de até 247 dispositivos escravos, cada um com um endereço de unidade exclusivo. Os frames são binários com CRC, e os limites dos frames são definidos por intervalos de silêncio na linha.

Isso tem consequências práticas:

  • Apenas um mestre por barramento. Se um sistema de gestão predial (BMS) ou uma IHM já faz polling dos medidores, seu gateway não pode simplesmente entrar nos mesmos fios. Você precisa de um barramento separado, de um dispositivo com uma segunda porta ou de um gateway posicionado na frente de ambos.
  • O barramento é lento e compartilhado. A 9600 baud, cada requisição e resposta leva dezenas de milissegundos, e as requisições a dispositivos diferentes são serializadas. O planejamento do polling importa.
  • Problemas elétricos parecem problemas de software. Terminação ausente ou um par invertido geram timeouts e erros de CRC que nenhuma quantidade de código vai resolver.

Se seus dispositivos estão em Ethernet, use nosso tutorial de Modbus TCP para MQTT.

Hardware necessário

  1. Um computador gateway: um Raspberry Pi, um PC industrial ou qualquer máquina Linux.
  2. Um adaptador USB para RS-485, de preferência isolado e com resistores de terminação e de polarização selecionáveis. O isolamento protege o gateway de diferenças de potencial de terra entre painéis.
  3. Cabo de par trançado para o barramento, de preferência blindado, com um terceiro condutor para o comum de sinal.
  4. O manual Modbus de cada dispositivo. Você precisa do mapa de registradores e das configurações seriais padrão.

Fiação RS-485 bem feita

O RS-485 é robusto quando ligado como previsto e frustrante quando não. Siga estas regras:

  1. Em cadeia (daisy chain), não em estrela. Passe o barramento de dispositivo em dispositivo, em linha. Derivações curtas são toleráveis; ramificações longas causam reflexões.
  2. Termine as duas pontas. Coloque um resistor de 120 ohms entre o par A/B nas duas extremidades físicas do barramento, e em nenhum outro lugar. Muitos adaptadores e medidores têm uma chave ou jumper de terminação.
  3. Polarize uma única vez. Os resistores de polarização (fail-safe) mantêm a linha em um estado definido quando ninguém está transmitindo. Habilite-os em um único ponto, normalmente no adaptador do mestre.
  4. Conecte o comum. Apesar do nome "dois fios", conecte o terra de sinal (muitas vezes identificado como GND, COM ou C) entre os dispositivos, para que os receptores compartilhem uma referência.
  5. Atenção à identificação A/B. Os fabricantes discordam sobre se A é positivo ou negativo, e alguns usam D+ e D-. Se nada responder, inverter A e B no adaptador é um primeiro teste legítimo.
  6. Mantenha-o longe dos cabos de potência. Passe o barramento separado dos cabos de motores e inversores de frequência e aterre a blindagem em uma das pontas.

Configurações seriais: baud rate, paridade e stop bits

Todos os dispositivos de um barramento precisam usar as mesmas configurações, e cada dispositivo precisa de um endereço exclusivo.

Configuração Valores comuns Observações
Baud rate 9600, 19200, 38400 9600 e 19200 são os padrões mais comuns; cabos mais longos favorecem taxas menores
Data bits 8 O Modbus RTU sempre usa 8 data bits
Paridade Par, Nenhuma, Ímpar A especificação Modbus usa paridade par por padrão, mas muitos dispositivos saem de fábrica sem paridade
Stop bits 1 ou 2 Sem paridade, a especificação pede 2 stop bits, embora muitos dispositivos aceitem 1
Endereço da unidade 1 a 247 Definido no painel frontal do dispositivo ou por software; deve ser único no barramento

Anote as configurações como um código curto, como 9600 8E1 ou 19200 8N2, e ajuste todos os dispositivos para que correspondam antes de conectar qualquer coisa.

Lendo um mapa de registradores

O manual de um dispositivo lista os registradores em uma tabela com endereço, descrição, tipo de dado, unidade e, às vezes, um fator de escala. O exemplo abaixo é um medidor de energia trifásico hipotético usado neste tutorial; medidores reais são diferentes, então sempre pegue os endereços do seu próprio manual.

Descrição Função Endereço (base 0) Tipo Unidade
Tensão L1-N 04 input 0 float32 V
Corrente L1 04 input 6 float32 A
Potência ativa total 04 input 52 float32 W
Frequência 04 input 70 float32 Hz
Energia ativa importada 04 input 72 float32 kWh

Três coisas a verificar em todo manual: se os endereços são baseados em zero ou escritos no estilo 30001 (subtraia o deslocamento), se os valores de 32 bits usam a palavra mais significativa primeiro ou a menos significativa primeiro e se os registradores inteiros precisam de um fator de escala.

Pré-requisitos no gateway

sudo usermod -aG dialout $USER     # permite acesso à porta serial; depois saia e entre novamente
ls -l /dev/serial/by-id/           # caminho estável do dispositivo para o seu adaptador
python3 -m venv ~/rtu-gw && source ~/rtu-gw/bin/activate
pip install "pymodbus[serial]>=3.8,<3.9" "paho-mqtt>=2.0"

Use o caminho /dev/serial/by-id/... em vez de /dev/ttyUSB0, porque a numeração dos dispositivos USB pode mudar após uma reinicialização.

O código do gateway Modbus RTU para MQTT

O gateway faz polling de cada medidor por vez, lê cada bloco de registradores em uma única requisição sempre que possível e publica uma mensagem JSON por medidor por ciclo. Agrupar os valores de um medidor em uma única mensagem mantém baixa a taxa de mensagens, que é o que você quer em um barramento lento e em um uplink tarifado por uso.

# rtu_mqtt_gateway.py - faz polling de medidores Modbus RTU via RS-485 e publica no 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")

# Em produção, substitua a porta pelo seu caminho /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"     # use seu próprio id aleatório
MQTT_HOST, MQTT_PORT = "broker.connectmq.com", 1883   # produção: 8883 com TLS
MQTT_USER, MQTT_PASS = None, None                     # produção: "YOUR_USERNAME", "YOUR_PASSWORD"
CLIENT_ID = f"rtu-gw-{uuid.uuid4().hex[:8]}"          # produção: "YOUR_CLIENT_ID"

# Medidores no barramento: endereço Modbus da unidade -> nome no 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):
    """Lê os input registers float32 do medidor hipotético em duas requisições de bloco."""
    block_a = client.read_input_registers(0, count=8, slave=unit)        # tensão, corrente
    block_b = client.read_input_registers(52, count=24, slave=unit)      # potência .. energia
    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:   # publica qualidade ruim uma vez, após 3 falhas
                    mq.publish(topic, json.dumps({"quality": "bad"}), qos=1, retain=True)
            time.sleep(0.05)                     # pequeno intervalo entre dispositivos no barramento
        time.sleep(max(0, POLL_SECONDS - (time.monotonic() - cycle_start)))


if __name__ == "__main__":
    main()

Repare nos deslocamentos em read_meter: o bloco B começa no registrador 52, então a frequência no registrador 70 fica no deslocamento 18 e a energia importada no 72 fica no deslocamento 20. Errar esses deslocamentos é o bug mais comum quando se agrupam leituras, então imprima os registradores brutos uma vez durante o comissionamento.

Execute e verifique

Rode o gateway e depois acompanhe a saída de qualquer 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"}

O servidor MQTT público pode ser lido por qualquer pessoa, então use-o apenas para testes e protótipos, nunca para dados de produção. Para uma planta real, crie um broker privado e mude para a porta 8883 com suas credenciais.

Solução de problemas no Modbus RTU

Sintoma Causa provável O que tentar
Nenhum dispositivo responde A/B invertidos, baud rate ou paridade errados, porta errada Inverta A/B, confirme 8E1 vs 8N1, verifique /dev/serial/by-id
Alguns dispositivos respondem, outros dão timeout Endereço de unidade duplicado ou errado, fiação interrompida Desconecte todos menos um dispositivo e teste cada um
Erros de CRC intermitentes Terminação ausente, topologia em estrela, ruído de inversores Termine as duas pontas, refaça o trajeto do cabo, use par trançado blindado
Funciona a 9600, falha em taxas maiores Cabo longo ou fiação ruim Fique na taxa menor ou corrija o cabeamento
Código de exceção 2 (illegal data address) Endereço além do mapa do dispositivo ou bloco que atravessa uma lacuna Divida o bloco em leituras menores
Timeouts aleatórios enquanto outro sistema está rodando Dois mestres no mesmo barramento Dê ao gateway seu próprio barramento ou porta

Escalando

Uma única porta RS-485 serve bem para alguns medidores. Quando você tiver dezenas, considere um adaptador por painel ou conversores Modbus RTU para TCP, que permitem a um gateway alcançar vários barramentos via Ethernet, e então reaproveite a ponte TCP. Publicar na mudança em vez de a cada ciclo, como descrito no nosso guia de report by exception, também mantém o uplink silencioso. Para plantas remotas com dados móveis, como estações de bombeamento de saneamento no interior, veja MQTT sobre rede celular.

Um broker MQTT gerenciado como o ConnectMQ elimina a necessidade de operar um servidor para esses gateways. O plano Serverless gratuito suporta até 25 conexões, o suficiente para um piloto com vários gateways, e o plano Serverless pago escala até 1.000. As regras de alerta por limite do portal podem monitorar valores como power_total e registrar um alerta quando um limite for ultrapassado. Para a arquitetura por trás de tudo isso, leia o guia de IoT industrial com MQTT.

Comece com o ConnectMQ

Coloque seus medidores RS-485 online hoje. Cadastre-se em app.connectmq.com, escolha o plano Serverless gratuito (sem cartão de crédito), crie um broker e copie o usuário, a senha e o client ID gerados para o script do gateway. As leituras dos seus medidores aparecerão no explorador de topics ao vivo do ConnectMQ assim que o primeiro polling for concluído.

Perguntas frequentes

Quantos dispositivos Modbus RTU um barramento RS-485 suporta?

Os endereços Modbus permitem até 247 dispositivos, mas o limite elétrico depende da carga dos transceptores; o RS-485 padrão suporta 32 cargas unitárias, e muitos transceptores modernos representam cargas fracionárias. Na prática, o tempo de polling normalmente se torna o limite antes.

Posso ler dispositivos Modbus RTU que um BMS já consulta?

Não no mesmo barramento, porque o Modbus RTU permite apenas um mestre. Use uma segunda porta do dispositivo, uma saída de medição separada ou peça ao integrador do BMS que disponibilize os dados de outra forma.

Qual baud rate devo usar no Modbus RTU?

Use o que todos os dispositivos do barramento suportam, normalmente 9600 ou 19200. Taxas menores toleram melhor cabos mais longos e ambientes com mais ruído.

Um gateway Modbus RTU deve publicar a cada polling?

Não necessariamente. Agrupar os valores de um dispositivo em uma mensagem por ciclo, ou publicar apenas em mudanças significativas, mantém o volume de mensagens baixo sem perder informação.

Artigos relacionados

Cliente MQTT no CODESYS: Publicando Variáveis do CLP em um Broker

Configure a biblioteca IIoT MQTT Client do CODESYS para publicar variáveis do CLP em JSON, verifique-as no servidor MQTT público broker.connectmq.com e depois migre para um broker privado com credenciais e TLS.

8 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.