]> Matthias Wimmer (溫雅石)

Timing-Fehler auf dem Modbus

Tagged as DE · debugging · hardware · logic · modbus

Written on

Modbus ist einer Feldbussystem. Eine Steuerung kommuniziert seriell über nur zwei Adern mit mehreren Sensoren und Aktoren. Ich entwickle die RemoteGuard. Diese erfasst Sensordaten so und über andere Schnittstellen und kann dann Aktionen starten oder Daten an unsere M2M-Plattform übertragen.

Für die Entwicklung muss ich oft Sensoren simulieren. Das geht gut und einfach mit einem Arduino. Schnell etwas Hardware zusammengesteckt und Software aufgespielt. Löten meist nicht nötig.

Testaufbau von hinten nach vorn: RemoteGuard (groß und blau), Arduino Due mit aufgestecktem RS485-Modul („Shield“) an dem auf der gelben und weißen Ader der MODBUS ankommt, Breadboard um den Arduino mit dem Display zu verschalten, HD44780-Displaymodul, Logikanalysator.

Das Display zeigt Daten der Simulation. Hier habe ich 24 (= 18 hex) Modbus-Nachrichten empfagen. Die letzte Nachricht hatte 3 Byte. Der (fehlerhafte) Inhalt war 11 C1 BC. (Das EF hat keine Bedeutung.)

Das Problem: ich erhalte nicht die erwarteten Nachrichten. Es kommen zwar welche an, das Modul fühlt sich aber nie zuständig. Was ist los? Auf dem Display steht die Nachricht 11 C1 BC. Korrekt wäre aber 01 11 C1 BC. Es fehlt das erste Byte! Die „01“ ist die Modbus-Adresse, „11“ der Funktionscode für „get slave ID“ und zwei Byte Prüfsumme. Auch in allen anderen Nachrichten fehlt das erste Byte, die Prüfsumme ist somit falsch. Klar, solche Nachrichten werden verworfen.

Benutzt habe ich die Bibliothek arduino-modbus-slave. Leider ist darin ein Fehler. Den Beginn einer neuen Nachricht erkennt ein Modbus-Gerät über das Timing auf dem Bus. Vor einer Nachricht wird mindestens 3,5 Zeichen lang nicht gesendet. In der Nachricht dagegen darf keine Übertragungspause gemacht werden. Mein Modbus hat 57000 baud. 3,5 Zeichen (à 11 Bit) sind damit 0,67 ms lang. Arduino-modbus-slave berechnet dies ohne Nachkomma. Alles hinter dem Komma wird abgeschnitten. Jede Pause ab 0 ms Länge wird als Beginn einer neuen Nachricht betrachtet … Freundlich gesagt: das ist nicht gaaanz richtig. Eine Pause von 0 ms Länge, die gab es natürlich überall.

Problem erkannt, Problem gebannt: ein kleiner Patch und die Bibliothek rundet dies auf 1 ms auf. (Den Bug habe ich in den Issue-Tracker von arduino-modbus-slave eingetragen. Mal schauen, ob sich jemand darum kümmert.)

Wie signalisiert MODBUS die Grenzen zwischen Nachrichten?

Wenn man sich das MODBUS-Protokoll anschaut, dann sieht man, dass einzelne Nachrichten (nur) durch eine Pause von der Dauer der Länge, die 3,5 Zeichen zur Übertragung bräuchten signalisiert wird. Es gab' also zwei Möglichkeiten: meine RemoteGuard hat schickt Nachrichten nicht am Stück raus und macht eine zu lange Pause innerhalb der Nachricht oder mein gebautes Sensormodul erkennt eine Pause wo keine Pause ist. Ein typisches Timing-Problem also.

Einen solchen Fehler durch Programmcode-Analyse zu finden ist ziemlich aufwändig. Man müsste zu mehreren Zeitpunkten einen Timer auslesen und dann schauen zu welchen Zeiten was passiert ist. Eine Nerven raubende Angelegenheit … Viel angenehmer geht es, wenn man einen Logik-Analysator zur Hand hat.

Ein Logik-Analysator ist so etwas wie ein Oszilloskop jedoch nicht für analoge Signale sondern für Digitalsignale.

Saleae_Logic_MODBUS.png Screenshot vom Logik-Analsator. Entsprechend konfiguriert dekodiert er automatisch das serielle Signal. Man sieht es kommen die Bytes 00 11 C1 BC an. (Im Gegensatz zur Displayanzeige im Foto oben hatte ich den die MODBUS-Adresse von 1 auf 0 geändert.) Die Anzeige der Bytes im Screenshot ist dezimal.

Die Punkte im Bild sind die einzelnen Bits auf der seriellen Leitung. Das erste Byte beginnt mit einem Startbit (LOW-Pegel), hat 8 Datenbits (alle LOW-Pegel), ein Parity-Bit (ebenso LOW) und ein Stopp-Bit (HIGH). Dann kommt das Startbit des zweiten Byte, gefollgt von 8 Datenbits, einem Parity und dem Stoppbit. Man sieht auch schön wie auf der seriellen Leitung immer zuerst das niederwertigste Bit übertragen wird.

Über die Anzeige im Logik-Analysator war schnell klar: die RemoteGuard sendet die MODBUS-Nachricht korrekt. Alle Bytes der Nachricht werden in einem Weg übertragen. Es gibt keine Pause von 3,5 oder mehr Zeichen in der Übertragung. Der Fehler musste also in meinem Modul-Testaufbau bzw. in der hierfür verwendeten Bibliothek liegen.

Die verwendete Bibliothek ist arduino-modbus-slave. Und in ihr war der Fehler auch schnell gefunden. Sie berechnet das was als notwendige Länge zwischen zwei Nachrichten angesehen wird als Ganzzahl in Millisekunden. Mein bus läuft mit 57600 baud. Bei einer Zeichenlänge von 11 Bit (Startbit, 8 Datenbit, Parity und Stoppbit) sind 3,5 Zeichen nur etwa 0,67 ms lang. Als Ganzzahl ergibt dies eine Länge von 0 ms. So viel „Pause“ ist eben immer und zwischen allen Bytes, die übertragen werden. Indem ich die Pausenlänge statt abzurunden nun aufrunde erhalte ich 1 ms und alles funktioniert.

Den entsprechenden Bug habe ich auch im Bugtracker des Projektes gemeldet. Mal schauen, ob sich ihm jemand annimmt.


Unless otherwise credited all material Creative Commons License by Matthias Wimmer