Bei den meisten Automatisierungsanlagen steht man über kurz oder lang vor der Aufgabe eine Feldbusanbindung zu realisieren. Unter anderem kann das notwendig sein, wenn externe Sensoren benötigt werden, oder diverse I/O-Module integriert werden sollen. Außerdem kann über ein Feldbussystem der Datenaustausch zwischen verschiedenen Steuerungsgeräten erfolgen. Dieser Artikel beschreibt die einzelnen Schritte zur Realisierung einer Modbus-Kommunikation zwischen zwei CODESYS Steuerungen (Win Control V3 und Raspberry Pi).
Modbus-Protokoll
Die Modus-Kommunikation basiert auf der Master/Slave-Architektur und gilt in der Industrie als De-facto-Standard. Deshalb unterstützen, so ziemlich die meisten Geräte, Modbus. Falls Ihr euch bzgl. eurer CODESYS SPS unsicher seid, könnt Ihr dies im CODESYS Device Directory nachprüfen. Mehr dazu findet Ihr in einem meiner früheren Beiträge: Wie finde ich die passende SPS?
Jedes Gerät im Netzwerk, kann zugleich die Funktionalität eines Masters oder Slaves übernehmen. Dadurch könnt Ihr die Struktur eures Feldbussystems beliebig flexibel gestalten. Die Geräte können somit je nach Aufgabe eine andere Rolle erfüllen.
Erhält ein Slave eine Anfrage (Request) des Masters, beantwortet er diese mit einem Response, welche die angefragten Daten überträgt. Sofern es zu einem Übertragungsfehler kommt, werden Informationen über die Fehlerursache, anstelle der angefragt Daten übertragen. Je nach Betriebsart können dann die Daten vom Anwender im Netzwerk gelesen werden.
Es wird zwischen den folgenden Betriebsarten/Funktionen unterschieden:
- RTU: Serielle Übertragung der Daten in binärer Form
- ASCII: Serielle Übertragung der Daten im ASCII Format und dadurch direkt lesbar
- TCP: Übertragung der Daten mittels TCP/IP auf Port 502
In diesem Blog Beitrag werde ich nur die Betriebsart über TCP verwenden.
Speicherbereich
Die Modbus-Geräte speichern die Daten innerhalb vier verschiedener Tabellen. Hier gibt es zwei Datenmodelle. Zum einen könnt Ihr für jeden Objekttyp einen eigenen Adressbereich definieren. Anderseits ist es gängige Praxis, die vier Tabellen zu überlagern und damit ein Speicherbereich zu erhalten. Hier erfolgt dann ein überlappender Zugriff.
Eine Hilfreiche Beschreibung kann in der Modbus Spezifikation (Kapitel 4.3) nachgelesen werden.
Objekttyp | Zugriff | Größe |
---|---|---|
Einzelner Ein-/Ausgang „Coil“ | Read/Write | 1-bit |
Einzelner Eingang „Discrete Input“ | Read | 1-bit |
Eingänge „Input Register“ | Read | 16-bits |
Ein-/Ausgänge „Holding Register“ | Read/Write | 16-bits |
Funktionscode
Der Master führt einen Request an einem Slave durch ⇒ Der Header beinhaltet den sogenannten Funktionscode. Dadurch weiß der Slave in welcher Tabelle er die Daten abrufen soll. Ebenfalls regelt der Funktionscode klar, ob ein Schreib- oder Lesezugriff erfolgt. Genauso wird durch den Funktionscode die Anzahl der bearbeiteten Feldelemente (Dateigröße) bestimmt.
Funktionscode | Zugriff | Speicherbereich |
01 (01 hex) | Read | Einzelner Ausgang „Coil“ |
05 (05 hex) | Write single | Einzelner Ausgang „Coil“ |
15 (0F hex) | Write multiple | Einzelner Ausgang „Coil“ |
02 (02 hex) | Read single | Einzelner Eingang „Discrete Input“ |
04 (04 hex) | Read multiple | Eingänge „Input Register“ |
06 (06 hex) | Write single | Ein-/Ausgänge „Holding Register“ |
03 (03 hex) | Read multiple | Ein-/Ausgänge „Holding Register“ |
16 (10 hex) | Write multiple | Ein-/Ausgänge „Holding Register“ |
In der oberen Tabelle seht ihr die unterschiedlichen Funktionscodes und deren Zugriffsparameter. Außerdem ist in der ersten Spalte der Funktionscode zusätzlich als Hexadezimalwert abgebildet. Das hilft euch, wenn Ihr z.B. mit Wireshark den Netzwerkverkehr bei Modbus TCP mitschneidet. Denn der Funktionscode gibt dann direkt Aufschluss darüber, wie Ihr die übertragenen Daten interpretieren könnt.
ACHTUNG: Byte-Reihenfolge (Byte Order) ist von den Geräteherstellern unterschiedlich implementiert. Achtet darauf, dass die Reihenfolge bei Master und Slave gleich ist.
Voraussetzungen & Vorbereitung für die Modbus-Kommunikation
Um eine Modus-Kommunikation zu betreiben sind nur ein paar grundlegende Konfigurationen erforderlich. Ansonsten sind, außer der physikalischen Vernetzung, für unseren Aufbau keine weiteren Maßnahmen notwendig.
Beide Teilnehmen müssen lediglich im gleichen IP-Adressbereich sein.
Ich werde, für mein Beispiel, jeweils ein CODESYS Projekt für jeden Teilnehmer anlegen. Der Master wird mit einem Raspberry Pi realisiert, und den Slave werde ich mit meiner Win Control V3 SPS simulieren.
Modbus Master-Konfiguration (Raspberry Pi)
Für die Konfiguration des Masters müssen wir zunächst dem CODESYS Device ein „Ethernet Knoten“ im Gerätebaum einfügen ⇒ Device (rechtsklick) ⇒ Gerät anhängen…
Ethernet Adapter ⇒ Ethernet ⇒ Gerät anhängen
Anschließend hängt Ihr nun den Modbus TCP Master an das zuvor eingefügten Ethernet Element. Im Gerätebaum ⇒ Ethernet (rechtsklick) ⇒ Gerät anhängen… ⇒ Modbus TCP Master
Tipp: Die Geräte können bei geöffnetem Editor „Gerät anhängen“ durch Doppelklick an das aktuell ausgewählte Element angefügt werden.
Der Modbus TCP Master ist nun unterhalb des Ethernet-Knotens im Gerätebaum verfügbar. Zunächst müssten wir aber das Ethernet parametrieren. Dazu öffnen wir (per Doppelklick auf Ethernet) den Konfigurator und klicken auf den Button (…) hinter Netzwerkschnittstelle.
In dem nachfolgenden Dialog wählt Ihr nun „eth0“ (wie hier bei meinem Raspberry Pi) oder einen anderen passenden Adapter eurer Steuerung.
ACHTUNG: Um die Netzwerkschnittstelle parametrieren zu können müsst Ihr mit dem Gerät verbunden sein.
Allgemeine Parameter
Ebenso ist beim Modbus TCP Master eine Einstellung erforderlich. Hierzu ebenfalls den Geräteeditor öffnen und im Reiter „Allgemein“ die Option für Auto-reconnect aktivieren.
Jetzt haben wir alle Einstellungen für den Master fertiggestellt, und müssen nun noch den Slave mit dem Master verknüpfen. In CODESYS müsst Ihr hierzu einfach im Geräteeditor den Slave als Knotenelement unter den Master hängen. Im Gerätebaum ⇒ Modbus_TCP_Master (rechtsklick) ⇒ Gerät anhängen… ⇒ Modbus TCP Slave
Jetzt haben wir ein Slave Element in unserem Gerätebaum. Dieses hat jedoch noch nichts mit unserem reellen Slave auf meine zweiten Steuerung zu tun. Hierzu müssen wir im Editor für den Slave die IP-Adresse des Slaves (hier in diesem Fall die IP Adresse meines PCs mit der Win Control V3) angeben. Prüft in diesem Fall auch ob Ihr bei beiden Geräten den Port gleich ausgewählt habt (hier Standard 502).
Der Reiter „Allgemein” hat die folgenden Einstellungen für Modbus TCP:
- Slave IP-Adresse – die IP-Adresse des Slaves
- Response Timeout – das Zeitintervall, in dem der Master die Antwort vom Slave-Gerät abwartet. Das hier angegebene Timeout überschreibt die allgemeine Einstellung „Response Timeout“ des zugehörigen Masters.
- Port – Port-Nummer des Slaves (Standard 502)
Tipp: Solltet Ihr häufig Probleme mit Verbindungsabbrüchen haben, empfehle ich euch die „Response Timeout“ auf 2000ms zu erhöhen.
Funktionscodes
Als nächstes müssen wir noch die Funktionscodes angeben um Daten zu übertragen. Dazu können wir im Reiter „Modbus Slave-Kanal“ die Kanäle hinzufügen. Wie oben in der Tabelle erwähnt gibt es mehrere Möglichkeiten. Ich nutze nun für das Beispiel den Funktionscode 23 (Read/Write Multiple Registers) um gleichzeitig Werte zu lesen und zu schreiben. Die Daten länge habe ich exemplarisch mit 4 Byte (2 WORD) angegeben. Hier müsst ihr natürlich entsprechend eurer erforderlichen Datenmenge die Werte anpassen. Klickt im Menü Modbus Slave-Kanal ⇒ Kanal hinzufügen… und Ihr seht den nachfolgenden Dialog, mit den oben bereits erwähnten Parametern.
Modbus Slave-Konfiguration
Um eine funktionierende Modbus-Kommunikation zu installieren, müssen wir natürlich noch den Slave Konfigurieren. Das werde ich in den nachfolgenden Schritten beschreiben. Auch hier müsst Ihr zunächst (wie beim Master) einen „Ethernet Knoten“ im Gerätebaum einfügen ⇒ Device (rechtsklick) ⇒ Gerät anhängen…
Außerdem werden wir gleich den Modbus TCP Slave Gerät mit anfügen und damit, unsere Win Control V3, als Modbus Slave ausweißen ⇒ Ethernet (selektieren) ⇒ rechtsklick ⇒ Gerät anhängen… ⇒ Modbus ⇒ Modbus TCP Slave Device ⇒ Gerät anhängen ⇒ Schließen.
Damit die Modbus-Kommunikation über TCP funktionieren kann, müsst Ihr, wie beim Master, ebenfalls, die Netzwerk Einstellungen im Ethernet Knoten vornehmen ⇒ Ethernet (auswählen) ⇒ Allgemein ⇒ Netzwerkschnittstelle […]
Ihr erhaltet nun den nachfolgenden Dialog. Dieser unterscheidet sich bei euch natürlich, da Ihr vermutlich andere Hardware Komponenten verbaut habt. Bei meinem PC sieht das Interface wie folgt aus:
Konfigurierte Parameter
Ebenso wie den Ethernet-Knoten haben wir den Modbus-Slave im vorherigen Schritt schon hinzugefügt. Auch hier sind einige Parameter einzustellen. Das Konfigurationsmenü öffnet Ihr, indem Ihr im Gerätebaum, einen Doppelklick auf den Eintrag des Slaves durchführt.
Der Reiter „Allgemein” hat die folgenden Parameter:
- Watchdog – die maximale Zeitdauer, die das Slave-Gerät auf einen Schreibzugriff erwartet. Wenn kein Schreibzugriff innerhalb dieser Zeitdauer erfolgt ist, werden alle Ausgänge auf 0 gesetzt.
- Slave Port – Port-Nummer des Slaves (Default 502)
- Unit ID – Netzwerk-Adresse des Geräts (nur erforderlich für Modbus RTU)
- Holding Registers – Anzahl der Holding-Register
- Input Registers – Anzahl der Eingangsregister
ACHTUNG: Die beiden Zeilen, Holding Registers (%IW) und Input Registers (%QW), legen nicht die Adresse, sondern lediglich die Anzahl, der verwendeten Register, fest.
Während Ihr beim Slave Port und Watchdog hier einfachheitshalber die Standard Werte belassen könnt, müsst Ihr bei der Datenlänge, hier dieselben Werte wie im Master eintragen (hier jeweils 2x WORD).
Datenmodel
Je nachdem, für welches Datenmodel (siehe Kapitel Speicherbereich), Ihr euch entscheidet, müsst Ihr hier die Parameter anpassen. Falls die Speicherblöcken getrennt sind, müsst Ihr für jeden Funktionscode hier unterschiedliche Startadressen (je nach Anordnung und Größe der Sektionen) angeben.
Ich bin eher für die Methode eines gemeinsamen Datenblocks mit überlappendem Zugriff. Dies ist allerdings Geschmackssache. Hier bleiben die Startadressen bei „0“ und das Häkchen bei „Holding- und Input-Register Datenbereich überlagert“ muss gesetzt werden. Damit könnt Ihr nun die einzelnen Funktionscodes verwenden um auf ein WORD oder ein Bit eures Speicherberichs zuzugreifen. Hier empfiehlt es sich in einer externen Tabelle eine Übersicht mit eurem Datenaufbau anzulegen.
E/A-Abbild
Damit unsere Register auch beim Funktionstest übertragen werden, obwohl wir keine SPS Programm geschrieben haben, welches die Register beschreibt, sind noch die nachfolgenden Einstellungen bzgl. Aktuallisierung und Buszyklus-Task erforderlich.
Funktionstest
Die Konfiguration ist nun abgeschlossen. Zeit für einen Funktionstest der Modbus-Kommunikation. Ich habe hierzu einfach beide CODESYS Geräte gleichzeitig geöffnet und werde euch manuell Variablen schreiben um den Übertragungsvorgang zu zeigen.
Hier könnt Ihr z.B. sehen, dass meine Werte im Ausgangswort der Win Control V3 als Eingangswerte im CODESYS des Raspberry Pi ausgelesen werden können. Ebenfalls ist es durch das überlagerte Datenmodel möglich auf die Werte als WORD oder als BIT zuzugreifen.
Schlusswort
Mit Hilfe der Modbus-Kommunikation könnt Ihr einfach mehrere Steuerungen untereinander vernetzen. Im Grunde genommen ist das ganz im Sinne von Industrie 4.0. Auch wenn das Modbus-Protokoll mit einem Alter von knapp 40 Jahren zu den Urgesteinen der Kommunikationsprotokolle, für Steuerungssysteme, gehört. Wie Ihr aus den obigen zwei Konfigurationen seht, ist es sehr leicht eine Modbus-Kommunikation zu realisieren. Solltet Ihr dennoch mal Probleme mit dem Verbindungsaufbau haben, so empfehle ich euch, die Kommunikation zum Master und zum Slave separat zu prüfen. Hierzu gibt es eine Reihe von Tools, die eingesetzte werden können. Unter folgendem Link (englisch) findet Ihr eine kleine Auswahl.
Hallo Matthias,
ist es dir einmal gelungen, die IP-Adresse des Elements Modbus_TCP_Slave dynamisch zu ändern, sodass eine Konfiguration zur Laufzeit möglich wird, z.B. über den Webserver der Steuerung? Die Methode Modbus_TCP_Slave.UpdateCommunicationSettings verspricht dies, erfordert wohl aber einen Reconnect der Verbindung, zu dem ich aber keine Dokumentation gefunden habe.
Ansonsten danke für deine schönen Anleitungen! Da wir auch Modbus, OPC UA und Data Logging nutzen sehr relevant.
Hallo Stefan,
Ich hatte dies einmal mit einer CODESYS V2.3 Steuerung gemacht. Bisher allerdings noch nicht mit CODESYS V3 probiert. Ich habe leider erst nächste Woche wieder die Möglichkeit, dass an einer Steuerung mit Modbus Slave zu testen. Dann kann ich dir aber auf alle Fälle Feedback geben wie es funktioniert. Eventuell helfen dir allerdings schon diese beiden Methoden weiter.
Modbus_TCP_Slave.Enable:=FALSE;
Modbus_TCP_Master.xStop:=TRUE;
Damit du die Methode Enable beim Slave aufrufen kannst, musst du in deinem Gerät die Diagnose für alle Geräte aktivieren: Device -> PLC Settings -> Additional Settings -> Enable Diagnosis for Devices.
Ich würde beide Propertys vor der Methode ‘UpdateCommunicationSettings’ bearbeiten. Danach den Master, und anschließend den Slave wieder starten (xStop:=FALSE und Enable:=TRUE). Eventuell musst du auch auf xDone abfragen oder zumindest eine Wartezeit einbauen.
Ich hoffe die Methoden sind die richtigen. Habe aktuell nur Zugriff mit meinem Smartphone und kann diese leider nicht vorher prüfen. Mit was für einer Steuerung arbeitest du?
Beste Grüße
Matthias
Hallo Stefan, ich habe die Konfiguration getestet. Funktioniert super. Ein Beispiel findest du hier, in meinem neusten Artikel.
Viele Grüße
Matthias
Super, danke für die ausführliche Anleitung und den Hinweis auf die Diagnosefunktionen! Werde es testen sobald ich wieder an der Hardware sitze.