Alle Lektionen anzeigen
Teil 1
Grundlagen
Teil 2
Modbus RTU
- Lektion 5RS485 erklärt: A, B, Common und Terminierung20 Min.
- Lektion 6Baudrate und Parität: warum 8E1 Standard ist16 Min.
- Lektion 7Das Modbus RTU Frame Byte für Byte, plus CRC18 Min.
- Lektion 8Mehrere Modbus Geräte an einem RS485-Bus16 Min.
- Lektion 9Ihr erstes Modbus Gerät mit mbpoll auslesen22 Min.
- Lektion 10Modbus RTU Fehlersuche: vom Symptom zur Ursache20 Min.
Teil 3
Modbus TCP
Teil 4
Fortgeschritten
- Lektion 16Sicher auf ein Modbus Gerät schreiben18 Min.
- Lektion 17Word Order und Float: gleiche Bytes, anderer Wert22 Min.
- Lektion 18Poll-Intervall und Buslast selbst berechnen18 Min.
- Lektion 19Modbus Sicherheit: das Protokoll hilft nicht16 Min.
- Lektion 20Modbus integrieren: SPS, Home Assistant, Cloud20 Min.
- Lektion 21Modbus Inbetriebnahme: Checkliste und Spickzettel18 Min.
Modbus TCP mit einem Simulator üben
Mit einem kostenlosen Modbus Simulator üben Sie ohne Hardware. Sie starten einen Server auf Port 5020, lesen Register und lösen bewusst eine Exception aus.
Was diese Lektion behandelt
- Ein Server auf Port 5020 und der erste Lesevorgang gegen 127.0.0.1
- Eine Exception gezielt auslösen und einen 32-Bit-Wert zusammensetzen
- Ein eigener Server in fünfzehn Zeilen Python, und was er nicht zeigt
Vorher lesen: Eine Modbus Registertabelle richtig lesen, Port 502, der MBAP-Header und die Unit-ID
Ein Modbus-Simulator ist ein Programm auf Ihrem eigenen Rechner, das sich nach außen wie ein Modbus TCP-Server verhält: Es hält Register bereit, es antwortet auf FC03, und es liefert eine echte Exception, wenn Sie danach fragen. Damit üben Sie Adressierung, Funktionscodes und Datentypen, ohne auf eine Lieferung oder auf eine Freigabe in der Anlage zu warten. Nach dieser Lektion läuft bei Ihnen ein Server, Sie haben ein Register gelesen, eine Exception ausgelöst und einen eigenen Datensatz gebaut.
Warum es ohne Hardware weiter geht als gedacht
Alles, was im Protokoll steckt, üben Sie am Schreibtisch. Ein Simulator baut dieselben Frames wie ein echtes Gerät: dieselbe PDU, denselben sieben Byte langen MBAP-Header, dieselben Exception-Codes. Damit trainieren Sie genau die vier Punkte, an denen es in der Praxis am häufigsten hakt: die Zählweise der Adressen, die Wahl des Funktionscodes, das Zusammensetzen von Werten über mehr als ein Register und das Lesen einer Fehlerantwort. Wie der Header im Einzelnen aufgebaut ist, steht im Nachschlagetext zu Modbus TCP.
Was ein Simulator nicht liefert, ist die Kupferseite. Keine RS485-Leitung, keine Baudrate, keine Terminierung, keine Buslast. Ein Simulator ersetzt also das TCP-Kapitel, nicht das RTU-Kapitel. Wollen Sie danach ein echtes Netz absuchen, ist die Anleitung zum Scannen eines Modbus-Netzwerks der passende Nachschlagetext.
Simulator installieren und starten
Der Simulator gehört zu pymodbus und ist mit einem Befehl eingerichtet. pymodbus 3.15.0 steht unter der BSD-3-Lizenz, ist also frei nutzbar, auch geschäftlich, und setzt Python 3.10 oder neuer voraus.
python -m pip install "pymodbus[serial,simulator]==3.15.0"
Unter Linux und macOS legen Sie vorher eine virtuelle Umgebung an, sonst bricht pip mit "externally managed environment" ab:
python3 -m venv ~/modbus-kurs
source ~/modbus-kurs/bin/activate
Gestartet wird der Simulator so:
pymodbus.simulator --modbus_server server --modbus_device device
Sie bekommen einen Modbus TCP-Server auf 0.0.0.0:5020 und eine Weboberfläche auf http://127.0.0.1:8081/. Dass hier 5020 steht und nicht die registrierte Modbus-Portnummer 502, hat einen praktischen Grund: Ports unterhalb von 1024 verlangen unter Linux und macOS Administratorrechte, 5020 nicht.
Ihr erster Lesevorgang gegen 127.0.0.1
Der mitgelieferte Datensatz hat auf PDU-Adresse 3 ein einzelnes Register liegen. Lesen Sie es mit einem pymodbus-Client:
from pymodbus.client import ModbusTcpClient
client = ModbusTcpClient("127.0.0.1", port=5020)
client.connect()
rr = client.read_holding_registers(address=3, count=1, device_id=1)
print(rr.registers)
Zurück kommt [17001]. Ein einzelnes 16-Bit-Register, und dieser Wert steht still: Er ändert sich zwischen zwei Lesevorgängen nicht.
Entscheidend ist das Argument address=3. Das ist die PDU-Adresse, also die Zahl, die unverändert ins Frame wandert, und nicht die Datenblattnummer 40004. Die device_id=1 ist die Unit-ID des Simulators. Bei einem Gerät, das direkt am Netz hängt, empfiehlt die Norm stattdessen 0xFF und akzeptiert auch 0x00; hinter einem Gateway steht dort die echte Serveradresse zwischen 1 und 247. Beschrieben ist das in der Lektion zum MBAP-Header und zur Unit-ID.
Mit mbpoll, dem Kommandozeilenclient aus dem RTU-Kapitel, lautet derselbe Auftrag:
mbpoll -m tcp -p 5020 -a 1 -t 4 -r 3 -c 1 -0 -1 127.0.0.1
-0 schaltet auf die 0-basierte Zählung um, -1 beendet das Programm nach einem Durchlauf. Halten Sie das Ergebnis gegen die Python-Zeile oben: Beide sollen dieselbe Zahl liefern, und wenn nicht, haben Sie gerade den Zählversatz gefunden. mbpoll 1.5.4 steht unter der GPLv3 und ist ebenfalls frei nutzbar, auch geschäftlich.
Eine Exception gezielt auslösen
Fragen Sie im selben Datensatz die PDU-Adresse 1 ab, dann kommt kein Wert zurück, sondern eine Fehlerantwort:
rr = client.read_holding_registers(address=1, count=1, device_id=1)
print(rr.isError(), rr.function_code, rr.exception_code)
Sie sehen True, 131 und 2. In diesen drei Angaben steckt die ganze Regel. 131 ist dezimal für 0x83, und 0x83 ist der angefragte Funktionscode 0x03 plus 0x80. Ein Server setzt bei einer Fehlerantwort also das oberste Bit des Funktionscodes und hängt genau ein Byte an, den Exception-Code. Die 2 steht für ILLEGAL DATA ADDRESS, den mit Abstand häufigsten Exception-Code im Feld. Was die übrigen Codes bedeuten, steht in der Lektion zu Funktionscodes und Exceptions.
Eine Exception ist dabei auch eine gute Nachricht: Sie beweist, dass die Verbindung steht. IP-Adresse, Port und Unit-ID stimmen, sonst hätten Sie überhaupt keine Antwort bekommen.
Einen 32-Bit-Wert zusammensetzen
Ab PDU-Adresse 4 liegen zwei Register, die zusammengehören:
rr = client.read_holding_registers(address=4, count=2, device_id=1)
print(rr.registers)
Das Ergebnis ist [9, 27177]. Zwei 16-Bit-Zahlen, die einzeln nichts bedeuten. Als ein 32-Bit-Wert gelesen, mit dem höherwertigen Wort zuerst: 9 mal 65536 ist 589824, plus 27177 ergibt 617001.
Von Hand rechnen Sie das einmal, um es zu verstehen. Im Alltag überlassen Sie es der Bibliothek, aber mit einem ausdrücklich genannten Datentyp:
value = client.convert_from_registers(rr.registers, data_type=client.DATATYPE.UINT32)
Ein zweites Beispiel, das sich an genau einem Punkt unterscheidet: PDU-Adresse 6, ebenfalls zwei Register, ist im mitgelieferten Datensatz ein float32, der bei 404,17 startet. Dieselbe Menge Bytes, ein anderer Datentyp, ein völlig anderes Ergebnis. Und dieser Wert steigt bei jedem Lesevorgang weiter, rechnen Sie hier also nicht mit einer festen Zahl.
Innerhalb eines Registers liegt die Byte-Reihenfolge fest, das höherwertige Byte steht vorn. Die Reihenfolge der beiden Register zueinander legt die Norm dagegen nicht fest, das entscheidet der Hersteller. Was das anrichtet, sehen Sie in der Lektion zu Datentypen und Word Order.
Ein eigener Server mit eigenen Registern
Wenn Sie den mitgelieferten Datensatz durchhaben, bauen Sie sich Ihren eigenen. Das ist die eigentliche Stärke der Sache: Sie stellen die Registertabelle eines Geräts nach, das noch gar nicht geliefert ist, und testen Ihre Integration fertig, bevor der Schaltschrank steht.
from pymodbus.server import StartTcpServer
from pymodbus.simulator import DataType, SimData, SimDevice
block = SimData(0, values=[1234, 5678, 42], datatype=DataType.REGISTERS)
rest = SimData(3, count=97, datatype=DataType.REGISTERS)
device = SimDevice(1, simdata=[block, rest])
StartTcpServer(context=device, address=("0.0.0.0", 5020))
SimData(0, ...) legt drei Register ab PDU-Adresse 0 an, rest füllt den Bereich dahinter auf, und SimDevice(1, ...) gibt dem Ganzen die Unit-ID 1. Lesen Sie danach address=0, count=3, kommt [1234, 5678, 42] zurück.
Was der Simulator nicht zeigt
Alles, was mit Kupfer zu tun hat, bleibt außen vor. Der Simulator kennt keine Verdrahtung, keine Baudrate, keine Parität, keine Reflexionen am Leitungsende und keine Buslast durch zehn Teilnehmer. Er hat keinen Abschlusswiderstand, und er bleibt nie stumm, weil D0 und D1 vertauscht sind.
Praktisch heißt das: Läuft Ihre Integration gegen den Simulator sauber und am echten Gerät kommt nichts zurück, dann suchen Sie den Fehler nicht im Protokoll. Sie suchen ihn in der Leitung, in den seriellen Parametern oder in der Serveradresse. Für diese Seite brauchen Sie das RTU-Kapitel und einen echten Bus. Wie der Sprung von der Übung in eine laufende Installation aussieht, zeigt die Anleitung zur Modbus-Einbindung in Home Assistant.
Häufige Fehler
Code aus alten Blogbeiträgen übernehmen. pymodbus nimmt unit= seit 3.1.0 nicht mehr an, hat den Parameter in 3.3.0 in slave= umbenannt und daraus in 3.10.0 device_id= gemacht. In derselben 3.10.0 verschwand BinaryPayloadDecoder, ersetzt durch convert_from_registers. Sie bekommen eine Meldung über ein unbekanntes Argument und suchen den Fehler beim Server. Was hilft: die Signatur der installierten Version nachschlagen, statt ein Beispiel von 2021 zu kopieren.
ModbusSequentialDataBlock(0, ...) verwenden. Seit 3.13.0 zieht pymodbus intern 1 von diesem ersten Argument ab, eine 0 wird also zu Adresse -1 und endet mit TypeError: 0 <= address < 65535. Was hilft: SimData und SimDevice nehmen, oder bei 1 beginnen.
Die Datenblattnummer als Adresse eintragen. Wer statt PDU-Adresse 3 die Nummer 40004 einträgt, bekommt Exception 02 und hält den Simulator für kaputt. Was hilft: address ist immer 0-basiert und wandert unverändert ins Frame.
Simulatorverhalten für Protokollverhalten halten. Der Simulator ist eine Lernumgebung mit eigenen Regeln. Was hilft: jede überraschende Beobachtung einmal gegen ein echtes Gerät gegenprüfen, bevor Sie eine Regel daraus machen.
Selbst ausprobieren
- 1
pymodbus installieren
python -m pip install "pymodbus[serial,simulator]==3.15.0". Unter Linux und macOS vorher eine virtuelle Umgebung anlegen. Kontrolle:pymodbus.simulator --helpzeigt den Hilfetext. - 2
Simulator starten
pymodbus.simulator --modbus_server server --modbus_device device. Kontrolle: die Weboberfläche aufhttp://127.0.0.1:8081/lässt sich im Browser öffnen. - 3
Ein Register lesen
PDU-Adresse 3, ein Register, Unit-ID 1. Kontrolle: Sie bekommen 17001 zurück, und zwar bei jedem Versuch denselben Wert.
- 4
Eine Exception auslösen
Dieselbe Abfrage auf PDU-Adresse 1. Kontrolle: Funktionscode 131 und Exception-Code 2. Rechnen Sie nach, dass 131 gleich
0x83und damit0x03plus0x80ist. - 5
Einen 32-Bit-Wert zusammensetzen
PDU-Adresse 4, zwei Register. Kontrolle: Sie bekommen
[9, 27177], und 9 mal 65536 plus 27177 ergibt 617001. Prüfen Sie das gegenconvert_from_registersmitDATATYPE.UINT32. - 6
Den laufenden Wert ansehen
PDU-Adresse 6, zwei Register, als float32. Kontrolle: zweimal hintereinander lesen und feststellen, dass der Wert ab 404,17 gestiegen ist.
- 7
Eigenen Server bauen
Die fünfzehn Zeilen mit
SimDataundSimDeviceaus dem Abschnitt oben. Kontrolle:address=0, count=3liefert[1234, 5678, 42].
Mit Hardware. Wiederholen Sie Schritt 3 bis 6 gegen Ihr eigenes Gerät und vergleichen Sie. Die Frames haben dieselbe Form, nur die Werte und die Adressen kommen dann aus dem Datenblatt statt aus dem Datensatz des Simulators. Wollen Sie dieselben Frames auch auf der Leitung sehen, legt die Lektion über die Fehlersuche mit Wireshark eine Aufzeichnung daneben.
Zusammenfassung
- Ein Simulator gibt Ihnen einen echten Modbus TCP-Server auf
127.0.0.1:5020, ohne Kabel, ohne Gerät und ohne Administratorrechte. - Eine Exception-Antwort besteht aus dem Funktionscode plus
0x80und einem Byte Exception-Code, im Beispiel0x83und 02 für ILLEGAL DATA ADDRESS. - Zwei Register werden erst durch eine Vereinbarung zu einer 32-Bit-Zahl: 9 mal 65536 plus 27177 ergibt 617001.
- Mit
SimDataundSimDevicestellen Sie in wenigen Zeilen die Registertabelle eines Geräts nach, das noch nicht geliefert ist. - Protokoll, Adressierung und Datentypen üben Sie vollständig im Simulator, die physische Seite mit Verdrahtung, Baudrate und Terminierung dagegen nicht.
Überprüfen Sie sich selbst
Vier Fragen zu dieser Lektion. Zu jeder Antwort gibt es eine Erläuterung.
Frage 1 von 4
Selbst sehen, wie es funktioniert?
Das ModbusCloud Gateway liest die Geräte aus diesem Kurs aus, ohne dass Sie ein einziges Register programmieren.