MODBUS

Aus TippvomTibb
Zur Navigation springen Zur Suche springen

Allgemeines

In letzter Zeit tauchen in meiner Hausautomation vermehrt Komponenten (im Wesentlichen Smartmeter) mit Modbus-Kommunikation auf.

Mich irritiert des Oefteren , dass eine Suche nach dem Modbus-Protokoll Informationen zu RS485 praesentiert.

Das ist zwar nicht grundsaetzlich falsch, wird aber nach meinem Dafuerhalten, viel zu selten differenziert genug dargestellt.

Zentrale Anlaufstelle fuer Informationen zu Modbus sollte die Modbus Organization, Inc. 4 Lan Drive, Suite 100 Westford, MA 01886 sein.

Nichtsdestotrotz gibt es unzaehlige Dokumente, die man zum Einstieg [1] oder zur Informationsgewinnung heranziehen kann/sollte.

Ich ergaenze hier nur die fuer mich notwendigen Informationen, um mir das Arbeiten mit Modbus-Komponenten zu erleichtern.

Als erstes musste ich lernen, das MODBUS in Groszbuchstaben geschrieben wird.

Ich beziehe mich hier auf die Spezifikation vom 26. April 2012 in der Version V1.1b3.

ISO/OSI

Zur Orientierung mal eine Einordnung in ISO/OSI-Schichtenmodell.

ISO/OSI Modell
Schicht 7 MODBUS TCP/IP (Client/Server)
Schicht 6
Schicht 5
Schicht 4
Schicht 3
Schicht 2 MODBUS RTU (Master/Slave)
Schicht 1 EIA/TIA 485 EIA/TIA 232

Die generelle Umbenennung von Master/Slave zu Client/Server waere eigentlich nicht notwendig gewesen. Da die Verwirrung jetzt durch das Vertauschen der Slave->Server und Master->Client nicht zur Verdeutlichung beitraegt.

Die Einordnung von MODBUS ASCII und MODBUS Plus muss ich noch genauer recherchieren.


Eine weitere sehr nuetzliche Darstellung liefert die Protokolldokumentation der Modbus-Org.

ModbusCommunicationStack.png (c) Modbus Organization Inc.

Spaetestens an dieser Stelle sollte deutlich geworden sein, dass Modbus nur bedingt etwas mit RS485 zu tun hat.

Nachrichtenformat(e)

Es ist sinnvoll, eine maximale Wartezeit im Kommunikationscode zu implemenetieren, um nicht unendlich auf eine Antwort zu warten, die vielleicht nie ankommt.
Die Datenkodierung (Data Encoding) ist sowohl fuer Adressen als auch fuer Daten Big-Edian. 

Das heisst, dass bei der Uebertragung einer numerischen Menge, die groeszer als ein einzelnes Byte ist, das hoeherwertige Byte zuerst gesendet wird.

So zum Beispiel: Registergroesze: 16-Bits Wert: 0x1234 -> Das erste gesendete Byte ist 0x12, dann 0x34

RTU

Remote Terminal Unit

KI meint dazu:

Modbus RTU-Nachrichten bestehen aus einem Frame, der aus einer Adresse, einem Funktionscode, Daten und einer CRC-Überprüfung besteht.
Die Daten werden binär übertragen und enthalten Informationen wie Registeradressen, Funktionscodes und Datenwerte. 
General MODBUS Frame
<---------- Application Data Unit (ADU) ---------->
Additional Address (1 Byte) Function code (1 Byte) Data (X Byte) CRC Error Check (2 Bytes)
<-- Protocol Data Unit (PDU) max. 253 Bytes-->

Adresse (Additional Address) 1 Byte

Die Adresse des Zielgeraetes wird als SlaveID, gelegentlich auch als UnitID oder DeviceID bezeichnet. Den Bezeichnung UnitID konnte ich in der Protokolldokumentation nicht finden.

Adressenfeld (1 Byte)
Adressen Bedeutung Erlaubt für Slave-Geraete?
1–247 Normale, eindeutige Slave-Adressen ✅ Ja, empfohlener Bereich
0 Broadcast-Adresse (alle Slaves empfangen, aber antworten nicht) ⚠️ Ja, aber nur zum Senden, kein Antwort erlaubt
248–255 Reserviert für Spezialfunktionen (z. B. Diagnose, Erweiterungen) ❌ Nicht zulässig fuer regulaere Slaves

Function code (1 Byte)

  • Gueltige Codes sind im Bereich von 1 ... 255 dezimal.
  • Der Bereich 128 – 255 ist reserviert und wird fuer Ausnahmen (Exception Responses) verwendet
  • Mit dem Code wird mitgeteilt welche Art von Aktion ausgefuehrt werden soll.
  • Der Funktionscode „0“ ist ungueltig.
  • Um mehrere Aktionen zu definieren, werden einigen Funktionscodes Unterfunktionscodes hinzugefuegt.

Data (X Byte)

Das Datenfeld von Nachrichten enthaelt zusaetzliche Informationen um die durch den Funktionscode definierte Aktion auszufuehren. Dazu gehoeren Dinge wie Diskrete- und Register-Adressen, die Anzahl der zu verarbeitenden Anfragen und die Anzahl der tatsaechlichen Datenbytes im Feld. Das Datenfeld kann bei bestimmten Anfragen nicht vorhanden sein (mit der Länge Null). In diesem Fall werden zur Bearbeitung des Funktionscodes keine weiteren Informationen benoetigt.

Wenn bei einer ordnungsgemaesz empfangenen MODBUS-ADU bei der Bearbeitung der MODBUS-Funktion kein Fehler auftritt, enthaelt das Datenfeld die Antwort der angeforderten Daten. Wenn ein Fehler im Zusammenhang mit der angeforderten MODBUS-Funktion auftritt, enthaelt das Feld einen Ausnahmecode.

CRC Error Check (2 Bytes)

[2]

FrameLaengenberechnung

Die Groesze der MODBUS PDU wird fuer alle Arten durch die maximale Laenge der Level 1 Schicht begrenzt, die die minimalste Laenge bietet.

Das ist in der MODBUS-Implementierung das Serial Line-Netzwerk RS485 und erlaubt eine ADU mit maximal 256 Bytes.

Daher gilt: MODBUS PDU für serielle Leitungskommunikation = 256 - Adresse (1 Byte) - CRC (2Bytes) = 253 Bytes.

Folglich gilt auch: RS232 / RS485 ADU = 253 Bytes (PDU) + Adresse (1 Byte) + CRC (2 Bytes) = 256 Bytes. TCP MODBUS ADU = 253 Bytes (PDU) + MBAP (7 Bytes) = 260 Bytes.

Nebenbemerkung: MBAP (MODBUS Application Protocol) ist eine spezielle Header-Information, die für das Senden von MODBUS-Paketen über UDP (Port 502) verwendet wird.

Datenmodell

Das Datenmodell von MODBUS basiert auf einer Reihe von Tabellen mit unterschiedlichen Merkmalen. Die vier Haupttabellen sind:

Primary tables Object type Type of access Comments
Discretes Input Single bit Read-Only This type of data can be provided by an I/O system.
Coils Single bit Read-Write This type of data can be alterable by an application program.
Input Registers 16-bit word Read-Only This type of data can be provided by an I/O system.
Holding Registers 16-bit word Read-Write This type of data can be alterable by an application program.

Es gibt erst mal die Unterscheidung 1 Bit und 16 Bit und dann noch RO und RW. Ein 16-Bit RW ist somit zwangslaeufig ein Zugriff auf ein Holding Register. N'est-ce pas?

Diese Unterteilung spiegelt sich auch in der Einteilung der Funktionscodes s.u. wieder.

Ein Coil-Status ist wohl ein Ein- oder Ausschaltzustand eines digitalen Ausgangs oder Bit-Registers. Eine passende Uebersetzung fuer Coil suche ich noch. Denkbar waere, das in der Automatisierungs-Steinzeit Relais damit angesteuert wurden. Und Relais halt eben eine Spule (Coil) als Aktor besitzen. Was besseres ist mir nicht eingfallen.

Eine Uebersetzung des Protokollstexten im Kapitel 4.3 ergibt beim Google-Uebersetzer folgendes.

Die Unterscheidung zwischen Ein- und Ausgaengen sowie zwischen Bit-adressierbaren und Wort-adressierbaren Datenelementen implizieren kein Anwendungsverhalten. Es ist völlig akzeptabel und sehr üblich, alle vier Tabellen als übereinanderliegend zu betrachten, wenn dies die natürlichste Interpretation auf der jeweiligen Zielmaschine.

Den Sinn versteht vermutlich nur ein Ami. Im Englischen klingt es genauso verquer.

Jede Primaertabelle ermoeglicht die individuelle Auswahl von 65536 (16 Bit 0x0000 bis 0xFFFF) Datenelementen.

Die Lese- oder Schreibvorgaenge dieser Elemente sind so konzipiert, dass sie mehrere aufeinanderfolgende Datenelemente bis zur Datengroeszenbeschraenkung ausfuehren koennen, die vom Transaktionsfunktionscode abhaengig ist.

Es ist offensichtlich, dass alle über MODBUS verarbeiteten Daten (Bits, Register) im Anwendungsspeicher des Geraetes liegen muessen. Die physische Adresse im Speicher sollte nicht mit Datenreferenzen verwechselt werden. Die einzige Notwendigkeit besteht darin, die Datenreferenz mit der physischen Adresse zu verknuepfen. Logische Referenznummern, die in MODBUS-Funktionen verwendet werden, sind vorzeichenlos ganzzahlige Indizes beginnend bei Null.

Adressierungsmodell

In a MODBUS PDU each data is addressed from 0 to 65535.

In the MODBUS data Model each element within a data block is numbered from 1 to n.
The pre-mapping between the MODBUS data model and the device application is totally vendor device specific.

Siehe dazu #Adressverschiebung

Transaktion

Das Diagramm ist sehr hilfreich beim Verstaendnis und Nachvollziehen von Fehlermeldungen (Exceptions).

MB Abkuerzung fuer MODBUS

MODBUSTransactionStateDiagram.png(c)Modbus Organization Inc.

Funktionscode Kategorieren

Es gibt drei Kategorien von Funktionscodes:

  • Public Function Codes (1-64, 73-99 und 111-127)
  • User-Defined Function Codes (65-72 und 100-110)
  • Reserved Function Codes (Annex A (Informative) MODBUS RESERVED FUNCTION CODES, SUBCODES AND MEI TYPES)

Ich habe hier mal bewusst nicht das Tabellenlayout (Datenmodell) aus der Dokumentation uebernommen.

Zur weiteren Erlauterung der Codes empfiehlt sich direkt das entsprechende Kapitel der Dokumentaion zu studieren.

Public Function Codes
Code Subcode Funktion Access Kategorie Kapitel(Seite)
01 (0x01) Read Coils (Physical Coils or Internal Bits) Adresse 0xxxxD Read 1 Bit Data Access 6.1(11)
02 (0x02) Read Discrete Inputs (Physical Discrete Inputs) Adresse 1xxxxD Read 1 Bit Data Access 6.2(12)
03 (0x03) Read Holding Registers (Internal Registers or Physical Output Registers) Adresse 4xxxxD Read 16 Bit Data Access 6.3(15)
04 (0x04) Read Input Registers (Physical Input Registers) Adresse 3xxxxD Read 16 Bit Data Access 6.4(16)
05 (0x05) Write Single Coil (Internal Bits or Physical coils) Write 1 Bit Data Access 6.5(17)
06 (0x06) Write Single Register (Internal Registers or Physical Output Registers) Write 16 Bit Data Access 6.6(19)
07 (0x07) Read Exception status Diagnostics 6.7(20)
08 (0x08) 00-18,20 Disgnostic Diagnostics 6.8(21)
09 (0x09)
10 (0x0A)
11 (0x0B) Get Com event counter Diagnostics 6.9(25)
12 (0x0C) Get Com Event Log Diagnostics 6.10(26)
13 (0x0D)
14 (0x0E)
15 (0x0F) Write Multiple Coils (Internal Bits or Physical coils) Write 1 Bit Data Access 6.11(29)
16 (0x10) Write Multiple Registers (Internal Registers or Physical Output Registers) Write 16 Bit Data Access 6.12(30)
17 (0x11) Report Server ID Diagnostics 6.13(31)
18 (0x12)
19 (0x13)
20 (0x14) Read File record File Record Access Data Access 6.14(32)
21 (0x15) Write File record File Record Access Data Access 6.15(34)
22 (0x16) Mask Write Register (Internal Registers or Physical Output Registers) Read 16 Bit Data Access 6.16(36)
23 (0x17) Read/Write Multiple Registers (Internal Registers or Physical Output Registers) Read/Write 16 Bit Data Access 6.17(38)
24 (0x18) Read FIFO queue (Internal Registers or Physical Output Registers) Read 16 Bit Data Access 6.18(40)
~
43 (0x2B) 14 Read device Identification Diagnostics 6.21(43)
43 (0x2B) 13, 14 Encapsulated Interface Transport Other 6.19(41)
43 (0x2B) 13 CANopen General Reference 6.20(42)

Adressverschiebung

Was fuer mich wie ein schlechter Scherz klingt, gibt es tatsaaechlich. ChatGPT meint dazu:

Bei der Abfrage eines Modbus-Slave-Registers kann es vorkommen, dass die Adresse um eins erhöht wird, weil Modbus-Systeme häufig zwischen 0-basierten und 1-basierten Adressierungen unterscheiden.  Hier sind zwei mögliche Gründe für diese Erhöhung der Adresse:

*Modbus-Register-Adressen:
   Modbus definiert Registeradressen für "Holding Registers" und "Input Registers" als 1-basiert, während die meisten Modbus-Bibliotheken und -Implementierungen jedoch mit einer 0-basierten Indexierung arbeiten. Das bedeutet, dass, wenn du zum Beispiel ein Register mit der Adresse 40001 anfragst, die Modbus-Implementierung die Adresse intern als 0 behandelt. Das kann in manchen Fällen zu einer Verschiebung von 1 führen.

*Internationale Standards und Modbus Implementierungen:
   In einigen Modbus-Implementierungen gibt es eine Unterscheidung zwischen den Registern und den dazugehörigen Adressen. Die Modbus-Adressierung verwendet üblicherweise Adressen, die von 1 anfangen (z.B. 40001, 30001), aber die Register werden intern in Arrays gespeichert, die bei 0 beginnen. Das kann in der Anwendung zu einer Adresse führen, die um eins verschoben wird.

Zusammengefasst: Wenn du also ein Modbus-Register mit einer bestimmten Adresse abfragst, kann die Verschiebung durch die Art und Weise bedingt sein, wie das Modbus-Protokoll und die jeweilige Software die Adressen behandeln.

Beispiele

Diese Beispiele sind fuer mich sehr hilfreich gewesen, um einigen Programmen und Libraries bei merkwuerdigem Verhalten auf die Spur zu kommen. Weil nicht nicht bei jeder Software gleich klar wird was man vorne reinstecken muss um hinten das passende Ergebnis zu erhalten.

Beispiele fuer MODBUS/TCP folgen noch.

Modbus RTU Read Holding Registers (Funktionscode 0x03)

Beispiel:

Der Master moechte die Holding Register eines Slaves lesen (z.B. 2 Register ab Adresse 0x0001).

Modbus ADU Struktur:

  • Slave-Adresse: 0x01 (Der Slave, der angesprochen wird)
  • Funkionscode: 0x03 (Lesen von Holding Registers)
  • Startadresse: 0x0001 (Die Adresse des ersten Registers, das gelesen wird)
  • Anzahl der Register: 0x0002 (Die Anzahl der zu lesenden Register)
  • CRC-Pruefziffer: (Zur Fehlerpruefung, automatisch berechnet)
Hex-Format: 01 03 04 00 01 00 02 C4 0B

Erklaerung der Felder:

  • 01 : Slave-Adresse - Adressiert den Slave, der angesprochen wird.
  • 03 : Funktionscode - 0x03 fuer "Read Holding Registers".
  • 04 : Anzahl der Bytes - Der Master erwartet 2 Register (je 2 Bytes), also 4 Bytes Daten.
  • 00 01 : Startadresse - Die Adresse des ersten Registers, hier 0x0001.
  • 00 02 : Anzahl der Register - Die Anzahl der zu lesenden Register, hier 2.
  • C4 0B : CRC - Fehlererkennung (automatisch berechnet, hier beispielhaft).

Modbus RTU Write Single Coil (Funktionscode 0x05)

Beispiel:

Der Master moechte die Coil-Adresse 0x0001 auf 0xFF00 (ein) setzen (Coil wird eingeschaltet).

Modbus ADU Struktur:

  • Slave-Adresse: 0x01
  • Funktionscode: 0x05 (Write Single Coil)
  • Coil-Adresse: 0x0001
  • Wert: 0xFF00 (Coil an)
  • CRC-Pruefziffer: (Zur Fehlerpruefung, automatisch berechnet)
Hex-Format: 01 05 00 01 FF 00 C1 8C

Erklaerung der Felder:

  • 01 : Slave-Adresse - Der Slave, an den das Kommando gesendet wird.
  • 05 : Funktionscode - 0x05 fuer "Write Single Coil".
  • 00 01 : Coil-Adresse - Die Adresse der Coil, hier 0x0001.
  • FF 00 : Wert - Der Wert, der gesetzt wird. 0xFF00 bedeutet, dass die Coil eingeschaltet wird (ON).
  • C1 8C : CRC - Fehlererkennung.

Modbus RTU Read Input Discretes (Funktionscode 0x02)

Beispiel:

Der Master moechte den Status von 3 Input Discretes ab Adresse 0x0001 abfragen.

Modbus ADU Struktur:

  • Slave-Adresse: 0x01
  • Funktionscode: 0x02 (Read Input Discretes)
  • Startadresse: 0x0001
  • Anzahl der Discretes: 0x0003
  • CRC-Pruefziffer: (Zur Fehlerpruefung, automatisch berechnet)
Hex-Format: 01 02 01 80 01 24 9F

Erklaerung der Felder:

  • 01 : Slave-Adresse - Der Slave, der die Anfrage beantworten soll.
  • 02 : Funktionscode - 0x02 fuer "Read Input Discretes".
  • 01 80 : Discretes Status - Der Status der angeforderten Discretes. In diesem Fall 3 Discretes, die als 0x01 0x80 dargestellt sind.
  • 01 24 : Anzahl der Discretes - Zeigt die Statusbits der angeforderten Discretes.
  • 9F : CRC - Fehlererkennung.

Modbus RTU Write Multiple Registers (Funktionscode 0x10)

Beispiel:

Der Master moechte die Holding-Register ab Adresse 0x0001 mit 2 Werten (0x000A, 0x001B) schreiben.

Modbus ADU Struktur:

  • Slave-Adresse: 0x01
  • Funktionscode: 0x10 (Write Multiple Registers)
  • Startadresse: 0x0001
  • Anzahl der Register: 0x0002
  • Daten: 0x000A 0x001B (Werte fuer die Register)
  • CRC-Pruefziffer: (Zur Fehlerpruefung, automatisch berechnet)
Hex-Format: 01 10 00 01 00 02 04 00 0A 00 1B D8 5C

Erkluerung der Felder:

  • 01 : Slave-Adresse - Der Slave, der das Kommando ausfuehrt.
  • 10 : Funktionscode - 0x10 fuer "Write Multiple Registers".
  • 00 01 : Startadresse - Die Adresse des ersten Registers, das geschrieben wird.
  • 00 02 : Anzahl der Register - Die Anzahl der zu schreibenden Register, hier 2.
  • 04 : Anzahl der Datenbytes - Hier 4 Bytes (je 2 Bytes fuer jedes Register).
  • 00 0A 00 1B : Daten - Die Werte fuer die beide Register.
  • D8 5C: CRC-Fehlererkennung

Links

[3]