Industrial IoT

Gateway von Modbus RTU zu MQTT mit RS-485: Verdrahtung, Einstellungen und Code

Binden Sie Energiezähler und Sensoren mit Modbus RTU über RS-485 an ein Gateway an, das ihre Messwerte per MQTT veröffentlicht. Behandelt Verdrahtung, Terminierung, serielle Einstellungen, Registerbelegungen und einen vollständigen Python-Poller.

RS-485-Bus mit Energiezählern, verdrahtet mit einem kleinen Linux-Gateway, das Modbus-RTU-Daten als MQTT-Nachrichten in die Cloud sendet
Probieren Sie es auf Ihrem eigenen MQTT-Broker ausHolen Sie sich im Free-Tarif einen privaten, verwalteten MQTT-Broker und starten Sie mit fertigen Codebeispielen.
Kostenlos testen Codebeispiele Keine Kreditkarte erforderlich

Mit einem Gateway von Modbus RTU zu MQTT lesen Sie Messwerte von RS-485-Energiezählern, Durchflussmessern, Antrieben und Sensoren aus und veröffentlichen sie an einen Broker, wo Dashboards und Analytics sie nutzen können. Dieses Tutorial behandelt die physikalische Schicht, die die meisten Probleme verursacht (Verdrahtung, Terminierung, serielle Einstellungen), das Lesen einer Registerbelegung des Herstellers und ein vollständiges Python-Gateway, das mehrere Geräte an einem Bus pollt und ihre Werte per MQTT veröffentlicht. Ein Raspberry Pi oder ein beliebiger Linux-Rechner mit USB-RS-485-Adapter genügt, um mitzumachen.

Was Modbus RTU von Modbus TCP unterscheidet

Das Modbus-Datenmodell ist dasselbe: Coils, Discrete Inputs, Holding Register und Input Register. Was sich ändert, ist der Transport. Modbus RTU läuft über eine serielle Leitung, fast immer RS-485, als Multi-Drop-Bus. Ein Master pollt bis zu 247 Slave-Geräte, jedes mit einer eindeutigen Geräteadresse. Frames sind binär mit CRC, und Frame-Grenzen werden durch Ruhepausen auf der Leitung definiert.

Das hat praktische Folgen:

  • Nur ein Master pro Bus. Pollt bereits eine Gebäudeleittechnik oder ein HMI die Zähler, kann sich Ihr Gateway nicht einfach an dieselben Leitungen hängen. Sie brauchen einen separaten Bus, ein Gerät mit zweiter Schnittstelle oder ein Gateway, das vor beiden sitzt.
  • Der Bus ist langsam und geteilt. Bei 9600 Baud dauern Anfrage und Antwort jeweils einige zehn Millisekunden, und Anfragen an verschiedene Geräte laufen nacheinander. Die Polling-Planung zählt.
  • Elektrische Probleme sehen aus wie Softwareprobleme. Fehlende Terminierung oder ein vertauschtes Adernpaar erzeugen Timeouts und CRC-Fehler, die kein Code der Welt behebt.

Wenn Ihre Geräte am Ethernet hängen, verwenden Sie stattdessen unser Tutorial Modbus TCP zu MQTT.

Benötigte Hardware

  1. Ein Gateway-Rechner: ein Raspberry Pi, ein Industrie-PC oder ein beliebiger Linux-Rechner.
  2. Ein USB-RS-485-Adapter, idealerweise galvanisch getrennt und mit zuschaltbaren Terminierungs- und Bias-Widerständen. Die galvanische Trennung schützt das Gateway vor Potenzialunterschieden zwischen Schaltschränken.
  3. Verdrilltes Kabel für den Bus, vorzugsweise geschirmt, mit einem dritten Leiter für die Signalmasse.
  4. Das Modbus-Handbuch jedes Geräts. Sie brauchen die Registerbelegung und die seriellen Standardeinstellungen.

RS-485 richtig verdrahten

RS-485 ist robust, wenn es wie vorgesehen verdrahtet ist, und frustrierend, wenn nicht. Befolgen Sie diese Regeln:

  1. Linie, nicht Stern. Führen Sie den Bus in einer Linie von Gerät zu Gerät. Kurze Stichleitungen sind tolerierbar; lange Abzweige verursachen Reflexionen.
  2. Beide Enden terminieren. Setzen Sie an den beiden physischen Enden des Busses einen 120-Ohm-Widerstand über das A/B-Paar, und nirgendwo sonst. Viele Adapter und Zähler haben dafür einen Schalter oder Jumper.
  3. Nur einmal biasen. Bias-Widerstände (Fail-Safe) halten die Leitung in einem definierten Zustand, wenn niemand sendet. Aktivieren Sie sie an genau einer Stelle, meist am Master-Adapter.
  4. Die Masse verbinden. Trotz der Bezeichnung „Zweidraht" verbinden Sie die Signalmasse (oft GND, COM oder C beschriftet) zwischen den Geräten, damit die Empfänger einen gemeinsamen Bezug haben.
  5. Auf die A/B-Beschriftung achten. Hersteller sind sich uneinig, ob A positiv oder negativ ist, und manche verwenden D+ und D-. Antwortet nichts, ist das Tauschen von A und B am Adapter ein legitimer erster Test.
  6. Abstand zu Stromkabeln halten. Verlegen Sie den Bus getrennt von Motor- und Frequenzumrichterkabeln und erden Sie den Schirm an einem Ende.

Serielle Einstellungen: Baudrate, Parität und Stoppbits

Alle Geräte an einem Bus müssen dieselben Einstellungen verwenden, und jedes Gerät braucht eine eindeutige Adresse.

Einstellung Übliche Werte Hinweise
Baudrate 9600, 19200, 38400 9600 und 19200 sind die häufigsten Standardwerte; lange Kabel sprechen für niedrigere Raten
Datenbits 8 Modbus RTU verwendet immer 8 Datenbits
Parität Even, None, Odd Die Modbus-Spezifikation sieht standardmäßig gerade Parität vor, viele Geräte werden aber ohne Parität ausgeliefert
Stoppbits 1 oder 2 Ohne Parität fordert die Spezifikation 2 Stoppbits, viele Geräte akzeptieren aber 1
Geräteadresse 1 bis 247 Am Bedienfeld des Geräts oder per Software eingestellt; muss am Bus eindeutig sein

Notieren Sie die Einstellungen als Kurzcode wie 9600 8E1 oder 19200 8N2 und stellen Sie alle Geräte entsprechend ein, bevor Sie etwas anschließen.

Eine Registerbelegung lesen

Ein Gerätehandbuch listet Register in einer Tabelle mit Adresse, Beschreibung, Datentyp, Einheit und manchmal einem Skalierungsfaktor. Das folgende Beispiel ist ein hypothetischer Drehstrom-Energiezähler für dieses Tutorial; echte Zähler weichen ab, entnehmen Sie Adressen daher immer Ihrem eigenen Handbuch.

Beschreibung Funktion Adresse (nullbasiert) Typ Einheit
Spannung L1-N 04 Input 0 float32 V
Strom L1 04 Input 6 float32 A
Wirkleistung gesamt 04 Input 52 float32 W
Frequenz 04 Input 70 float32 Hz
Bezogene Wirkenergie 04 Input 72 float32 kWh

Drei Dinge sollten Sie in jedem Handbuch prüfen: ob Adressen nullbasiert sind oder im Stil 30001 geschrieben werden (Versatz abziehen), ob 32-Bit-Werte das höherwertige oder das niederwertige Wort zuerst verwenden und ob Integer-Register einen Skalierungsfaktor brauchen.

Voraussetzungen auf dem Gateway

sudo usermod -aG dialout $USER     # Zugriff auf die serielle Schnittstelle erlauben, dann ab- und wieder anmelden
ls -l /dev/serial/by-id/           # stabiler Gerätepfad für Ihren Adapter
python3 -m venv ~/rtu-gw && source ~/rtu-gw/bin/activate
pip install "pymodbus[serial]>=3.8,<3.9" "paho-mqtt>=2.0"

Verwenden Sie den Pfad /dev/serial/by-id/... statt /dev/ttyUSB0, weil sich USB-Gerätenummern nach einem Neustart ändern können.

Der Code des Gateways von Modbus RTU zu MQTT

Das Gateway pollt jeden Zähler nacheinander, liest jeden Registerblock nach Möglichkeit in einer Anfrage und veröffentlicht eine JSON-Nachricht pro Zähler und Zyklus. Die Werte eines Zählers in einer Nachricht zu bündeln hält die Nachrichtenrate niedrig, was Sie auf einem langsamen Bus und einem volumenbasiert abgerechneten Uplink wollen.

# rtu_mqtt_gateway.py - Modbus-RTU-Zähler über RS-485 pollen, an MQTT veröffentlichen
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")

# In der Produktion den Port durch Ihren /dev/serial/by-id/...-Pfad ersetzen
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"     # eigene Zufalls-ID verwenden
MQTT_HOST, MQTT_PORT = "broker.connectmq.com", 1883   # Produktion: 8883 mit TLS
MQTT_USER, MQTT_PASS = None, None                     # Produktion: "YOUR_USERNAME", "YOUR_PASSWORD"
CLIENT_ID = f"rtu-gw-{uuid.uuid4().hex[:8]}"          # Produktion: "YOUR_CLIENT_ID"

# Zähler am Bus: Modbus-Geräteadresse -> Topic-Name
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):
    """Liest die float32-Input-Register des hypothetischen Zählers in zwei Blockanfragen."""
    block_a = client.read_input_registers(0, count=8, slave=unit)        # Spannung, Strom
    block_b = client.read_input_registers(52, count=24, slave=unit)      # Leistung .. Energie
    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:   # schlechte Qualität einmal melden, nach 3 Fehlversuchen
                    mq.publish(topic, json.dumps({"quality": "bad"}), qos=1, retain=True)
            time.sleep(0.05)                     # kleine Pause zwischen Geräten am Bus
        time.sleep(max(0, POLL_SECONDS - (time.monotonic() - cycle_start)))


if __name__ == "__main__":
    main()

Beachten Sie die Offsets in read_meter: Block B beginnt bei Register 52, daher liegt die Frequenz bei Register 70 auf Offset 18 und die bezogene Energie bei 72 auf Offset 20. Falsche Offsets sind der häufigste Fehler beim Bündeln von Lesevorgängen, geben Sie bei der Inbetriebnahme daher einmal die Rohregister aus.

Ausführen und prüfen

Starten Sie das Gateway und beobachten Sie die Ausgabe von einem beliebigen Rechner aus:

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"}

Der öffentliche MQTT-Broker ist für jeden lesbar und für Tests und Prototypen gedacht, verwenden Sie ihn also nur für Testdaten, nie für Produktionsdaten. Für einen echten Standort legen Sie einen privaten Broker an und wechseln mit Ihren Zugangsdaten auf Port 8883.

Fehlerbehebung bei Modbus RTU

Symptom Wahrscheinliche Ursache Was Sie versuchen sollten
Kein Gerät antwortet A/B vertauscht, falsche Baudrate oder Parität, falscher Port A/B tauschen, 8E1 vs. 8N1 prüfen, /dev/serial/by-id prüfen
Manche Geräte antworten, andere laufen in Timeouts Doppelte oder falsche Geräteadresse, Leitungsbruch Alle Geräte bis auf eines abklemmen und jedes einzeln testen
Sporadische CRC-Fehler Fehlende Terminierung, Sterntopologie, Störungen durch Antriebe Beide Enden terminieren, Kabel neu verlegen, geschirmtes verdrilltes Kabel verwenden
Funktioniert bei 9600, scheitert bei höheren Raten Langes Kabel oder schlechte Verdrahtung Bei der niedrigeren Rate bleiben oder die Verkabelung reparieren
Exception-Code 2 (Illegal Data Address) Adresse außerhalb der Gerätebelegung oder Block überspannt eine Lücke Block in kleinere Lesevorgänge aufteilen
Zufällige Timeouts, während ein anderes System läuft Zwei Master am selben Bus Dem Gateway einen eigenen Bus oder Port geben

Skalieren

Eine einzelne RS-485-Schnittstelle reicht für eine Handvoll Zähler. Bei Dutzenden sollten Sie einen Adapter pro Schaltschrank in Betracht ziehen oder Konverter von Modbus RTU zu TCP, über die ein Gateway mehrere Busse per Ethernet erreicht, und dann die TCP-Brücke wiederverwenden. Veröffentlichen bei Änderung statt in jedem Zyklus, wie in unserem Leitfaden zu Report by Exception beschrieben, hält den Uplink zusätzlich ruhig. Für entfernte Standorte mit Mobilfunkdaten lesen Sie MQTT über Mobilfunk.

Sobald mehrere Gateways in mehreren Hallen oder Werken laufen, lohnt sich eine einheitliche Topic-Struktur. Bewährt hat sich ein Unified Namespace (UNS), dessen Topic-Baum der Equipment-Hierarchie nach ISA-95 folgt: Enterprise → Site → Area → Line → Cell. Aus dem Demo-Topic plant1/energy/mdb1_incomer wird dann etwa acme/werk-augsburg/energie/hauptverteilung-1/einspeisung. Energiemonitoring, MES und Analytics abonnieren genau die Ebene, die sie brauchen, und neue Zähler fügen sich ohne Absprache in die Struktur ein. Wie Sparkplug B diese Struktur ergänzt, beschreibt der Beitrag zu Sparkplug B und Unified Namespace.

Ein verwalteter MQTT-Broker wie ConnectMQ erspart Ihnen den Betrieb eines Servers für diese Gateways. Der kostenlose Serverless-Plan unterstützt bis zu 25 Verbindungen, genug für einen Pilot mit mehreren Gateways, und der kostenpflichtige Serverless-Plan skaliert auf 1.000. Die Regeln für Schwellwert-Alarme im Portal können Werte wie power_total überwachen und einen Alarm protokollieren, wenn ein Grenzwert überschritten wird. Zur Architektur dahinter lesen Sie den Leitfaden zu Industrial IoT mit MQTT.

Starten Sie mit ConnectMQ

Bringen Sie Ihre RS-485-Zähler noch heute online. Registrieren Sie sich unter app.connectmq.com, wählen Sie den kostenlosen Serverless-Plan (ohne Kreditkarte), legen Sie einen Broker an und kopieren Sie den generierten Benutzernamen, das Passwort und die Client ID in das Gateway-Skript. Ihre Zählerwerte erscheinen im Live-Topic-Explorer von ConnectMQ, sobald die erste Abfrage abgeschlossen ist.

Häufig gestellte Fragen

Wie viele Modbus-RTU-Geräte kann ein RS-485-Bus unterstützen?

Modbus-Adressen erlauben bis zu 247 Geräte, die elektrische Grenze hängt aber von der Last der Transceiver ab; Standard-RS-485 unterstützt 32 Unit Loads, und viele moderne Transceiver stellen nur Bruchteile davon dar. In der Praxis wird meist zuerst die Polling-Zeit zur Grenze.

Kann ich Modbus-RTU-Geräte lesen, die bereits eine Gebäudeleittechnik pollt?

Nicht am selben Bus, denn Modbus RTU erlaubt nur einen Master. Nutzen Sie eine zweite Schnittstelle am Gerät, einen separaten Zählerausgang oder bitten Sie den Integrator der Gebäudeleittechnik, die Daten auf anderem Weg bereitzustellen.

Welche Baudrate sollte ich für Modbus RTU verwenden?

Die, die jedes Gerät am Bus unterstützt, typischerweise 9600 oder 19200. Niedrigere Raten vertragen längere Kabel und störungsreichere Umgebungen besser.

Sollte ein Gateway von Modbus RTU zu MQTT bei jeder Abfrage veröffentlichen?

Nicht unbedingt. Die Werte eines Geräts in einer Nachricht pro Zyklus zu bündeln oder nur bei signifikanter Änderung zu veröffentlichen, hält das Nachrichtenvolumen niedrig, ohne Informationen zu verlieren.

Verwandte Artikel

Industrial DataOps: Werksdaten kontextualisieren und steuern

Was Industrial DataOps in der Praxis bedeutet: Kontextualisierung, Normalisierung, Qualitätsprüfungen und Governance von OT-Daten und wie Sie diese Schicht auf einem MQTT-Namespace betreiben.

8 Min. Lesezeit

Alle Artikel →

Ihr privater MQTT-Broker ist nur wenige Klicks entfernt

Konto anlegen, Broker-Zugangsdaten erhalten und das erste Gerät verbinden — ohne Server, die installiert, gepatcht oder skaliert werden müssen.