ModBus smartCORE Modul
Beschreibung
Das "modbus" Modul ließt primär die ModBus Register von ModBus Teilnehmern aus. Diese Teilnehmer können hierbei
- über eine serielle Verbindung (ModBus/RTU)
- über TCP (ModBus/TCP)
verbunden sein.
Verwendete Schnittstellen & Protokolle
- ModBus
JSON-Konfiguration
Im folgenden Abschnitt soll die gesamte JSON-Konfiguration des Moduls beschrieben und die einzelnen Parameter erläutert werden.
Beispielkonfiguration
Im Folgenden sei eine ModBus/TCP Beispielkonfiguration dargestellt:
{
"module": "ModBus",
"comment": "ModBus/TCP config",
"factory": "modbus",
"config": {
"version": 1,
"type": "modbus",
"enable": true,
"pollCycleInMilliseconds": 1000,
"transport": {
"type": "tcp",
"ipAddress": "192.168.0.1",
"ipPortNumber": 502,
"timeoutInMilliseconds": 200
},
"devices": [
{
"enable": true,
"deviceName": "Device Name",
"deviceAddress": 1,
"readChannels": [
{
"name": "Channel Name",
"address": 49152,
"registerType": "HoldingReg",
"registerFormat": "REAL",
"byteOrder": "ABCD",
"bitOffset": 0,
"unit": "",
"scale": 1.0,
"offset": 0.0
}
]
}
]
}
}
Globale Modulparameter
| Parametername | Erforderlich | Datentyp | sinnvoller Wertebereich | Default | Beschreibung |
|---|---|---|---|---|---|
| version | JA | INT | 1 | ModBus Protokollversion | |
| type | JA | STRING | "modbus" | Serielle Schnittstelle für Kommunikation | |
| enable | JA | BOOL | true | Schalter für Produktion der Daten in smartCHANNELs | |
| pollCycleInMilliseconds | JA | INT | 1 - | Abfrageintervall [ms] der ModBus Zugriffe | |
| transport | JA | JSON Objekt | Konfiguration der ModBus Transportschicht (siehe unten) | ||
| devices | JA | JSON Array von JSON Objekten | Liste der ModBus Teilnehmer | ||
| pollCycleInNanoseconds | Nein | INT | 1 - | 100000000 | Abfrageintervall [ns]. Ist der Schlüssel gesetzt, hat er Vorrang vor pollCycleInMilliseconds. Fehlen beide, werden 100 ms verwendet |
| enableInternalDataReduction | Nein | BOOL | true, false | false | Hängt an jeden erzeugten Kanal eine Datenreduktion (absTolerance 0.0, timeoutMs 60000): unveränderte Werte werden nicht geschrieben, spätestens nach 60 s aber wieder |
| autoGroup | Nein | BOOL | true, false | true | Fasst benachbarte Register eines Geräts automatisch zu Registerblöcken zusammen, die in einer Transaktion gelesen werden (siehe Registerblöcke) |
| autoGroupAllowGaps | Nein | BOOL | true, false | false | Erlaubt Lücken innerhalb eines Blocks: nicht benötigte Register werden mitgelesen, statt den Block zu trennen |
| maxRegisterGroup | Nein | INT | 1 - 125 | 125 | Größte Registeranzahl eines Blocks. Größere Werte werden mit einer Warnung auf 125 begrenzt — mehr lässt ModBus in einer Transaktion nicht zu |
| useF23withoutOutputs | Nein | BOOL | true, false | false | Verwendet Funktionscode 23 (Read/Write Multiple Registers) auch dann, wenn nichts zu schreiben ist (ab smartCORE 2.12) |
| minimalChannelAlarmCount | Nein | INT | 1 - | 2 | Anzahl unmittelbar aufeinanderfolgender Fehlversuche, nach der ein Registerblock abgeschaltet wird (siehe Timeout und Abschaltung) |
| retryAfterBlockFailS | Nein | INT | 0 - | 0 | Wartezeit [s], nach der ein abgeschalteter Registerblock erneut versucht wird. 0 = kein erneuter Versuch, der Block bleibt für die Messung aus (ab smartCORE 2.12) |
| workloadAsChannel | Nein | BOOL | true, false | false | Legt einen Diagnosekanal an, der die Buslast führt |
| workloadChannelBuffersize | Nein | INT | 1 - | 100 | Puffergröße des Buslastkanals in Elementen |
| workloadChannel | Nein | STRING oder JSON Objekt | Name oder vollständige Kanaldefinition des Buslastkanals |
ModBus/TCP spezifische Transportparameter ("transport")
| Parametername | Erforderlich | Datentyp | sinnvoller Wertebereich | Default | Beschreibung |
|---|---|---|---|---|---|
| type | JA | STRING | "tcp" | Auswahl ModBus/TCP | |
| ipAddress | JA | STRING | Gültige IP Adresse | IP Adresse des ModBus Geräts | |
| ipPortNumber | JA | INT | Gültige IP Portnummer | IP Portnummer des ModBus Geräts (sollte, falls nicht anders spezifiziert, auf 502 eingestellt werden) | |
| timeoutInMilliseconds | JA | INT | 1 - | Timeout [ms] für TCP Verbindung (sollte, falls nicht anders spezifiziert, auf 200 eingestellt werden) |
ModBus/RTU spezifische Transportparameter ("transport")
| Parametername | Erforderlich | Datentyp | sinnvoller Wertebereich | Default | Beschreibung |
|---|---|---|---|---|---|
| type | JA | STRING | "rtu" | Auswahl ModBus/RTU | |
| baudRate | JA | INT | Gültige ModBus Baudrate | Baudrate der seriellen Schnittstelle | |
| dataBits | JA | INT | 5 - 8 | Datenbits der seriellen Schnittstelle | |
| parity | JA | STRING | "no", "even", "odd" | Parität der seriellen Schnittstelle | |
| stopBits | JA | DOUBLE | 1.0, 1.5, 2.0 | Stoppbits der seriellen Schnittstelle | |
| portName | JA | STRING | Gültiges Device File | Gerätedatei der seriellen Schnittstelle | |
| timeoutInMilliseconds | JA | INT | 1 - | Antwort-Timeout [ms] der seriellen Verbindung. Er begrenzt, wie lange auf die Antwort eines Teilnehmers gewartet wird, und geht als modbus_set_response_timeout() an libmodbus | |
| deviceTimeoutInMilliseconds | Nein | INT | 0 - | 0 | Zusätzlicher Timeout [ms] zwischen den Abfragen unterschiedlicher ModBus Teilnehmer |
| readDelayMs | Nein | INT | 0 - | Überschreibt die Rahmenstille t3.5 [ms], die vor jeder Anfrage eingehalten wird. Ohne den Schlüssel wird sie aus der Baudrate berechnet (siehe Rahmenstille) |
ModBus Geräteparameter ("devices")
| Parametername | Erforderlich | Datentyp | sinnvoller Wertebereich | Default | Beschreibung |
|---|---|---|---|---|---|
| enable | JA | BOOL | true | Schalter zur Aktivierung des ModBus Geräts | |
| deviceName | JA | STRING | Name des ModBus Geräts | ||
| deviceAddress | JA | INT | 1 - | Adresse des ModBus Geräts | |
| readChannels | JA | JSON Array von JSON Objekten | siehe unten |
Die Schlüssel autoGroup, autoGroupAllowGaps, maxRegisterGroup und useF23withoutOutputs
dürfen zusätzlich pro Gerät angegeben werden und überschreiben dort den globalen Wert.
ModBus Kanalkonfiguration ("readChannels")
| Parametername | Erforderlich | Datentyp | sinnvoller Wertebereich | Default | Beschreibung |
|---|---|---|---|---|---|
| name | JA | STRING | Name des smartCHANNELs | ||
| address | JA | INT | Adresse des ModBus Registers | ||
| registerType | JA | STRING | Gültiger ModBus Registertyp | Typ des ModBus Registers ("InputReg", "HoldingReg", "Coil", "Status", "0x17" (nur Lesezugriff)) | |
| registerFormat | JA | STRING | Gültiges ModBus Registerformat | Format des ModBus Registers ("I8","I16","I32","I64","UI8","UI16","UI32","UI64","REAL","BOOL") | |
| byteOrder | JA | STRING | siehe unten | Reihenfolge der Bytes des Werts, siehe Byteorder | |
| bitOffset | JA | UINT | Bitoffset des zu interpretierenden Werts | ||
| unit | JA | STRING | Einheit des smartCHANNELs | ||
| scale | JA | DOUBLE | Skalierung des smartCHANNELs | ||
| offset | JA | DOUBLE | Offset des smartCHANNELs | ||
| enable | Nein | BOOL | true, false | true | Ein Kanal mit false wird weder angelegt noch gelesen |
| pollingIntervalMultiplicator | Nein | INT | 1 - | 1 | Der Registerblock dieses Kanals wird nur in jedem n-ten Abfragezyklus gelesen |
| registerGroup | Nein | INT | 0 - | Ordnet den Kanal einem Registerblock von Hand zu. Kanäle mit derselben Nummer bilden einen Block und werden von der automatischen Gruppierung ausgenommen |
ModBus Kanalkonfiguration ("readChannelGroups")
Alternativ können mehrere Kanalkonfigurationen zu einem JSON Array zusammengefasst werden, so dass die dieser Gruppe zugrundeliegenden ModBus Register in einem einzigen Schritt ausgelesen werden können (falls der Register Typ identisch ist). Dieses so zusammengefasste readChannelGroup Array kann dann in dem JSON Array der "readChannelGroups" aufgelistet werden, z.B.
"readChannelGroups": [
[
{
"name": "Channel 1",
"address": 49152,
"registerType": "HoldingReg",
"registerFormat": "REAL",
"byteOrder": "ABCD",
"bitOffset": 0,
"unit": "",
"scale": 1.0,
"offset": 0.0
},
{
"name": "Channel 2",
"address": 49158,
"registerType": "HoldingReg",
"registerFormat": "REAL",
"byteOrder": "ABCD",
"bitOffset": 0,
"unit": "",
"scale": 1.0,
"offset": 0.0
}
]
]
Es können alternativ "readChannels" bzw. "readChannelGroups" spezifiziert werden.
Registerblöcke
Die Kanäle eines Geräts werden nicht einzeln gelesen. smartCORE fasst sie zu Registerblöcken zusammen: ein Block ist ein zusammenhängender Adressbereich eines Registertyps und wird in genau einer ModBus Transaktion gelesen. Jeder Kanal wird von genau einem Block versorgt.
Gruppiert wird nach Registertyp und Adresse. Die vier ModBus Adressräume (Coil, Discrete Input, Input Register, Holding Register) sind unabhängig voneinander — Coil 4 und Input Register 4 sind verschiedene Dinge und landen nie im selben Block. Ein neuer Block beginnt, sobald
- der Bereich mehr als
maxRegisterGroupRegister umfassen würde, oder - das nächste Register nicht unmittelbar an das vorige anschließt und
autoGroupAllowGapsauffalsesteht.
Mit autoGroupAllowGaps: true werden Lücken überbrückt: der Block liest Register mit, die kein
Kanal braucht, spart dafür aber eine Transaktion. Das lohnt sich bei kleinen Lücken und teuren
Zugriffen, nicht bei weit auseinanderliegenden Adressen.
Wer die Aufteilung selbst bestimmen will, vergibt registerGroup je Kanal: Kanäle mit derselben
Nummer bilden einen Block und werden von der automatischen Gruppierung ausgenommen.
Byteorder
Der Wert eines Kanals wird vom selben Baustein aus dem gelesenen Puffer geschnitten wie beim
fmProducer-Modul, dem SimpleBufferPicker. byteOrder kennt deshalb
zwei Schreibweisen:
- Benannt —
bigEndian(auchbig,be,motorola,msb,network) oderlittleEndian(auchlittle,le,intel,lsb). Ohne Angabe gilt Big Endian. - Als Abbildung — eine Folge von Buchstaben, die die Bytes des Werts in die Reihenfolge bringt, in der sie auf dem Bus liegen. Die Länge der Folge sagt, über wie viele Bytes sich der Wert erstreckt, die Reihenfolge der Buchstaben, wie sie zu vertauschen sind.
Damit beantwortet sich auch der Unterschied zwischen "AB" und "BA": beide beschreiben einen
Wert aus zwei Bytes. "AB" übernimmt sie in der gelesenen Reihenfolge, "BA" vertauscht sie.
Entsprechend bei vier Bytes: "ABCD" unverändert, "CDAB" wortweise getauscht, "DCBA"
vollständig umgedreht — die vier Varianten, mit denen ModBus-Geräte 32-Bit-Werte ablegen.
"BIT" ist ein Sonderfall für den Zugriff auf ein einzelnes Bit. Eine Folge, die weder ein
bekannter Name noch eine gültige Abbildung ist, schaltet den Kanal ab; die Meldung nennt ihn
beim Namen.
Zeitverhalten und Fehlerbehandlung
Abfragezyklus
Der Bus wird von einem eigenen Thread bedient, der in jedem Abfragezyklus
(pollCycleInMilliseconds bzw. pollCycleInNanoseconds) alle fälligen Registerblöcke
nacheinander liest. Ein Block mit pollingIntervalMultiplicator: n ist nur in jedem n-ten Zyklus
fällig; Blöcke mit demselben Multiplikator werden dabei auf verschiedene Zyklen verteilt, damit
sie nicht alle im selben Zyklus zusammenfallen.
Ein Block, der in einem Zyklus nicht fällig ist, produziert nichts. Bis smartCORE 2.11 wurde
stattdessen der unveränderte Inhalt des Blocks erneut unter der aktuellen Zeit ausgegeben — ein
Kanal mit pollingIntervalMultiplicator: 5 lieferte damit fünfmal so viele Werte, wie Messungen
stattgefunden hatten.
Zeitstempel der Messwerte
Jeder Kanal trägt den Zeitstempel der Transaktion, aus der sein Wert stammt: den Zeitpunkt, zu dem die Antwort des Geräts eingetroffen ist. Alle Kanäle eines Registerblocks tragen deshalb denselben Zeitstempel. Der Zeitpunkt, zu dem das Gerät den Wert gebildet oder auf die Leitung gelegt hat, ist über ModBus nicht beobachtbar — die Ankunft der Antwort ist der nächstliegende Zeitpunkt, den das Protokoll hergibt.
Bis smartCORE 2.11 trugen die Werte den Zeitpunkt ihrer Ausgabe in den smartCHANNEL, nicht den der Messung — also den Zeitpunkt nach der Abfrage aller Geräte am Bus, gegebenenfalls um mehrere Sekunden verzögert.
Rahmenstille bei ModBus/RTU
Vor jeder Anfrage wird die Leitung für die Dauer der Rahmenstille t3.5 ruhig gehalten — die Pause, die eine RTU Nachricht von der nächsten trennt. Ihre Länge ergibt sich aus:
| Bedingung | Stille |
|---|---|
readDelayMs ist gesetzt | der angegebene Wert |
| Baudrate über 19200 (oder unbekannt) | 1,75 ms — der feste Wert der MODBUS over Serial Line Specification V1.02; der gerechnete schrumpfte bei 115200 Baud auf 0,33 ms und würde nichts mehr trennen |
| sonst | 3,5 Zeichen zu 11 Bit, also 38500000 / Baudrate Mikrosekunden — 4,01 ms bei 9600 Baud |
Die Pause liegt vor der Anfrage. Bis smartCORE 2.11 wurde nach einer erfolgreichen Antwort pausiert — nach einem Timeout also gerade nicht, und damit in dem Fall nicht, in dem die Leitung die Stille am nötigsten braucht.
Zwei Dinge werden dabei mitprotokolliert, weil sie auf eine zu schnell konfigurierte Anlage hindeuten: Bytes der vorigen Anfrage, die beim Start der nächsten noch in der Sendequeue stehen („die Leitung ist langsamer, als der Abfragezyklus annimmt"), und Daten, die unerwartet im Eingangspuffer liegen. Letztere werden gelesen und mit ihren ersten Bytes gemeldet, nicht stillschweigend verworfen.
Timeout und Abschaltung eines Registerblocks
Bekommt eine Anfrage innerhalb von timeoutInMilliseconds keine Antwort, gilt der Zugriff als
fehlgeschlagen. Der Registerblock zählt solche Fehlversuche, und nach
minimalChannelAlarmCount Fehlversuchen in Folge wird er abgeschaltet: er wird nicht mehr
abgefragt, und seine Kanäle produzieren nichts mehr. Die Meldung nennt Gerät, Registeradresse,
Funktionscode, die Länge der Fehlerserie und ob ein neuer Versuch vorgesehen ist.
Nicht jeder Fehler zählt auf diese Schwelle ein:
| Antwort des Geräts | Wirkung |
|---|---|
| Keine Antwort (Timeout), Illegal Function (01), Illegal Data Address (02), Illegal Data Value (03), Server Failure (04), Negative Acknowledge (07), Memory Parity (08), Gateway Target Failed To Respond (0B) | zählt auf die Abschaltung ein — das Gerät oder die Konfiguration ist dauerhaft nicht in Ordnung |
| Acknowledge (05), Server Busy (06), Gateway Path Unavailable (0A) | zählt nicht ein — das Gerät bittet um Geduld |
| Beschädigter Rahmen, z.B. CRC Fehler | zählt nicht ein — ein Ereignis, kein Zustand; die Anfrage zu wiederholen ist die richtige Antwort darauf |
retryAfterBlockFailS entscheidet, ob ein abgeschalteter Block zurückkommt: mit 0 bleibt er
für die Dauer der Messung aus, mit einem Wert größer null wird er nach dieser Zeit erneut
versucht. Gelingt der Versuch, ist der Block wieder in Betrieb und der Zähler steht auf null;
scheitert er, beginnt die Wartezeit von neuem.
Ein Gerät, das im Feld gelegentlich verschwindet und wiederkommt, braucht
retryAfterBlockFailS. Ohne den Schlüssel ist nach minimalChannelAlarmCount Fehlversuchen
Schluss, auch wenn das Gerät zwei Minuten später wieder am Bus hängt.
Modul-Informationen
| Information | Wert |
|---|---|
| Autoren | optiMEAS GmbH |
| seit smartCORE | 0.1 |
| Modultyp | Producer |
| Abhängigkeiten | KEINE |