
Secure Boot und Firmware-Schutz in eingebetteten Systemen
Benutzer von eingebetteten Geräten – von Industriecontrollern bis hin zu Unterhaltungselektronik – sind sich oft nicht der versteckten Schwachstellen bewusst, […]
Technische Manager im Embedded-Bereich stehen oft vor einer klassischen Herausforderung: die Integration industrieller Kommunikationsprotokolle in moderne Anwendungen. Eines der am weitesten verbreiteten Protokolle ist Modbus, ein bewährter Standard in der industriellen Automatisierung.
In diesem Artikel zeigen wir, wie Qt die Modbus-Integration vereinfacht – dank plattformübergreifender Unterstützung und leistungsstarker Werkzeuge wie QModbusClient und QModbusServer. Wenn Sie nach Experten für Embedded-Systems-Entwicklung suchen, können unsere Qt/C++-Kenntnisse Ihre Projekte effizient voranbringen.
Modbus ist ein serielles Kommunikationsprotokoll, das ursprünglich 1979 von Modicon (Schneider Electric) für SPS-Systeme (Speicherprogrammierbare Steuerungen) entwickelt wurde.
Dank seiner Einfachheit und Offenheit wurde Modbus schnell zum De-facto-Standard für industrielle Kommunikation. Es ist ein offen publiziertes und lizenzfreies Protokoll, was bedeutet, dass jeder Anbieter es in seine Geräte implementieren kann.
Im Laufe der Jahrzehnte wurde Modbus in SCADA-Systemen, der Fabrikautomatisierung, Energiesystemen und vielen anderen industriellen Bereichen weit verbreitet eingesetzt – als gemeinsame Sprache zur Verbindung elektronischer Geräte, die eine zuverlässige Kommunikation in industriellen Umgebungen ermöglicht.
Im Kern verwendet Modbus eine einfache Nachrichtenstruktur im Anfrage-Antwort-Prinzip. Es folgt einem Master/Slave- (Client/Server-) Modell: Ein einzelnes Master-Gerät (z. B. ein HMI oder eine zentrale Steuerung) fragt nacheinander ein oder mehrere Slave-Geräte (Sensoren, Aktoren, SPS) ab.
Der Master sendet eine Anfrage (z. B. „Lese Register X“ oder „Schreibe Wert Y in Spule Z“), und der adressierte Slave antwortet mit den angeforderten Daten oder einer Bestätigung. Slaves sprechen nur, wenn sie angesprochen werden (sie senden Daten nur als Antwort an den Master), was den Kommunikationsprozess vereinfacht. Ein Modbus-Master kann bis zu 247 Slaves in einem seriellen Netzwerk verwalten, die jeweils durch eine Geräteadresse identifiziert werden. Dieser abgefragte Kommunikationszyklus ist einfach und stellt sicher, dass immer nur ein Gerät zur gleichen Zeit auf dem Bus kommuniziert.
Jedes Modbus-Gerät organisiert seine Daten in einem Satz von Tabellen, die Spulen (binäre Ausgänge), digitale Eingänge (binäre Eingänge), Halteregister (16-Bit-Ausgangsregister) und Eingangsregister (16-Bit-Eingabewerte) enthalten.
Zum Beispiel könnte ein Temperatursensor die aktuelle Temperatur in einem Halteregister bereitstellen, das vom Master ausgelesen werden kann, oder eine SPS könnte Spulen besitzen, die der Master ein- oder ausschalten kann. Modbus-Funktionen (identifiziert durch Funktionscodes in der Nachricht) ermöglichen das Lesen und Schreiben dieser Datenpunkte. Das Protokoll überträgt rohe Bits und Wörter mit minimalem Formatierungsaufwand, was einen effizienten Datenaustausch gewährleistet.
Diese Einfachheit ist eine der größten Stärken von Modbus – es ist oft möglich, grundlegende Modbus-Unterstützung in nur einem Tag auf einem Mikrocontroller zu implementieren. Durch das Kombinieren von Daten aus mehreren Registern können Systeme fortschrittliche Datenerfassungs- und Steuerungsprozesse ermöglichen. Modbus definiert eine klare Struktur für das Organisieren und Übertragen von Datentypen wie Spulen und Register. Geräte teilen sich oft denselben Speicherbereich für Register, was die Kommunikation und Datenverwaltung effizienter macht. Die Registeradresse gibt an, wo sich bestimmte Daten im Speicher befinden. Dies ist Teil des seriellen Modbus-Protokolls, das festlegt, wie Daten im seriellen Format übertragen werden.
Modbus-Nachrichten beinhalten eine Adresse, Nutzdaten und Felder zur Fehlerprüfung. Eine Modbus-RTU-Nachricht besteht aus mehreren wichtigen Komponenten, einschließlich der Adresse, des Funktionscodes und des Datenfeldes, was eine klare Datenkommunikation gewährleistet.
Im seriellen Modbus (RTU/ASCII) wird eine zyklische Redundanzprüfung (CRC oder LRC) zur Fehlererkennung verwendet, während bei TCP/IP die integrierten Fehlerprüfmechanismen des Netzwerks (wie Checksummen des Internetprotokolls) genutzt werden. Die unkomplizierte Rahmenstruktur macht Modbus auch auf ressourcenbeschränkten Geräten leicht verständlich und einfach zu generieren.
Beim Übertragen von Daten ist es entscheidend, die Anzahl der Bytes korrekt zu behandeln, um die Genauigkeit des Datenaustauschs sicherzustellen. An dieser Stelle kommt das Datenbyte ins Spiel, das die tatsächliche Größe der übertragenen Daten definiert.
Modbus existiert in mehreren Hauptvarianten, die alle dasselbe grundlegende Datenmodell und denselben Funktionsumfang teilen, jedoch unterschiedliche Übertragungsformate verwenden:
Die am häufigsten verwendete Form von Modbus: RTU-Rahmen sind ein kompaktes binäres Format, das über serielle Leitungen (RS-485 oder RS-232) gesendet wird. Jedes 8-Bit-Byte in einer Nachricht kann zwei 4-Bit-Hex-Werte (0–255) enthalten, wodurch RTU bandbreiteneffizient ist. Nachrichten beginnen mit einem Ruheintervall und werden durch Timing (Pausen zwischen Nachrichten) eingerahmt. Eine RTU-Nachricht enthält am Ende eine 16-Bit-CRC (Cyclic Redundancy Check) zur Fehlererkennung.
Modbus RTU ist bekannt für seine Effizienz und wird aufgrund seiner schnellen, kompakten Kommunikation häufig eingesetzt. Es eignet sich gut für Mikrocontroller und Feldbussysteme und bleibt extrem beliebt bei industriellen Geräten, die über Multi-Drop-RS-485-Netzwerke kommunizieren. Mit Modbus RTU können mehrere Spulen oder physikalische Ausgangsregister gesteuert oder ausgelesen werden. Das Modbus-RTU-Protokoll eignet sich gut für Anwendungen mit geringer Latenz und Datentransfers in industriellen Umgebungen. Die Remote Terminal Unit (RTU) spielt eine Schlüsselrolle in Modbus RTU, indem sie die Kommunikation initiiert und Daten über entfernte Geräte sammelt. Außerdem verwendet Modbus RTU eine Protokolldateneinheit (PDU) für den Datenaustausch zwischen Master- und Slave-Geräten.
Eine alternative serielle Formatierung, bei der Nachrichten als ASCII-Text (lesbare Zeichen) kodiert werden. Jedes Datenbyte wird als zwei ASCII-Zeichen (hexadezimale Darstellung) gesendet, und Nachrichten werden durch ein „:“ zu Beginn und CR/LF am Ende begrenzt. Anstelle einer CRC verwendet Modbus ASCII eine LRC (Longitudinal Redundancy Check) zur Fehlererkennung.
Der ASCII-Modus ist menschenlesbar, aber verdoppelt die Nachrichtenlänge und ist weitaus weniger effizient als RTU, da bei gleicher Baudrate nur halb so viele Daten übertragen werden. Aufgrund seiner geringeren Leistung wird Modbus ASCII heute nur noch selten verwendet – typischerweise nur in Altsystemen oder in eingeschränkten Kanälen, die keine binären Daten zuverlässig verarbeiten können.
Eine neuere Variante, die für Ethernet-Netzwerke entwickelt wurde. Modbus TCP kapselt Modbus-Nachrichten in TCP/IP-Pakete ein und verwendet einen speziellen 7-Byte-MBAP-Header anstelle der in seriellen Modi verwendeten CRC. Dieser Header enthält Felder wie eine Transaktions-ID, eine Protokoll-ID, eine Länge und eine Einheitskennung (die der Slave-Adresse entspricht).
Modbus TCP verwendet typischerweise Port 502 und bietet deutlich höhere Geschwindigkeit sowie die Möglichkeit, dass mehrere Geräte über dasselbe Netzwerk kommunizieren. Es ermöglicht eine effiziente Datenübertragung über IP-Netzwerke und ist ideal für die Integration von SPS und Steuerungen in Produktionsnetzwerke, SCADA-Systeme oder IIoT-Cloud-Lösungen. Diese Variante ermöglicht auch die Übertragung des Modbus-Anwendungsprotokolls über das Netzwerk.
Die folgende Tabelle fasst die wichtigsten Unterschiede zwischen Modbus RTU, ASCII und TCP zusammen:
Variante | Transportmedium | Datenkodierung | Fehlerprüfung | Typischer Anwendungsfall |
---|---|---|---|---|
Modbus RTU | Seriell (RS-485 oder RS-232) | Binärramen (8-Bit-Daten) | CRC-16 (2 Bytes) | Hocheffizient in lokalen seriellen Netzwerken; ideal für SPS, Sensoren und andere Geräte in einem RS-485-Multidrop-Bus. Ein Master fragt bis zu 247 Slaves ab; robust und mit minimalem Overhead für Echtzeitsteuerung. |
Modbus ASCII | Seriell (RS-232/RS-485) | ASCII-Zeichen (Hex-Text) | LRC (1 Byte) | Legacy-Szenarien, in denen ein Textformat erforderlich ist oder sehr einfache Geräte. Menschenlesbar, aber nur halber Durchsatz im Vergleich zu RTU bei gleicher Baudrate; wird in modernen Systemen selten verwendet, wegen Ineffizienz und schwächerer Fehlerprüfung. |
Modbus TCP | Ethernet (TCP/IP-Netzwerk) | Binärprotokoll über TCP | TCP/IP (Netzwerk-Layer-CRC) | Moderne IP-basierte Netzwerke (LAN/WAN). Ideal für SCADA, HMIs und zur Verbindung entfernter Geräte über Ethernet. Kein dedizierter Master – mehrere Clients können einen Server abfragen. Benötigt Netzwerk-Infrastruktur, erlaubt jedoch Kommunikation über große Entfernungen mit hoher Geschwindigkeit, über Standard-Port 502. |
Wie Sie vielleicht wissen, bietet das Qt-Framework robuste Bibliotheken zur Implementierung verschiedener Kommunikationsprotokolle. Modbus bildet hier keine Ausnahme.
Das SerialBus-Modul von Qt (eingeführt in Qt 5.8) enthält High-Level-Klassen für die Modbus-Kommunikation, was die Integration von Modbus in Ihre Anwendung erheblich vereinfacht.
Egal ob Sie mit einem Modbus-RTU-Gerät über eine serielle Schnittstelle kommunizieren oder mit einem Modbus-TCP-Server über Ethernet – Qt bietet eine einheitliche API, die die Entwicklung vereinfacht und die Portabilität verbessert.
Neben den allgemeinen Vorteilen von Qt (Plattformunabhängigkeit, umfangreiches C++-Framework, moderne UI-Werkzeuge – ja, die Qt/ C++-Entwicklung gehört zu unseren Kernkompetenzen), gibt es drei Hauptvorteile beim Einsatz von Qt für Modbus:
Qt stellt eine intuitive, hochabstrahierte API für Modbus bereit, sodass Sie sich nicht mit Low-Level-Serial-Bytes, Socket-Programmierung oder manueller Rahmenbildung herumschlagen müssen. Anstatt einen benutzerdefinierten Modbus-Parser zu schreiben oder betriebssystemspezifische Schnittstellen zu verwenden, können Sie mit Qt-Klassen eine Verbindung öffnen und Lese-/Schreibanfragen mit wenigen Funktionsaufrufen stellen.
Die Klasse QModbusClient zum Beispiel ermöglicht das Senden einer Leseanfrage und kümmert sich im Hintergrund um die Formatierung von Modbus-PDU/ADU. Die Bibliothek übernimmt das Einfügen von Adressen, CRC-Berechnungen, das Timing zwischen Nachrichten und sogar die automatische Aufteilung binärer Datentypen. Qt unterstützt sowohl RTU- als auch TCP-Implementierungen des Protokolls, was es für verschiedene industrielle Anforderungen flexibel macht.
So können sich Entwickler auf den eigentlichen Datenaustausch konzentrieren und müssen sich nicht mit der Kodierung beschäftigen – das beschleunigt die Entwicklung und reduziert Fehler.
Das SerialBus-Modul von Qt enthält standardmäßige Unterstützung für Modbus in serieller und TCP-Form, sodass keine separaten Bibliotheken für verschiedene Betriebssysteme oder Modbus-Varianten erforderlich sind. Zum Beispiel kann derselbe Qt-Modbus-Code sowohl auf einem Embedded-Linux-Board als auch auf einem Windows-PC laufen – Qt verwendet intern die passende Backend-API (seriell oder TCP-Socket).
Qt stellt spezielle Klassen wie QModbusRtuSerialMaster (für Modbus RTU über seriell) und QModbusTcpClient (für Modbus über TCP/IP) zur Verfügung – ebenso wie QModbusRtuSerialSlave und QModbusTcpServer zur Implementierung eigener Modbus-Geräte über serielle oder Netzwerk-Schnittstellen. Dieses einheitliche Framework erlaubt es Ihnen, auf einer Plattform zu entwickeln und auf einer anderen zu deployen – mit minimalen Änderungen, während dank Qt die Geräteidentifikation effizient über mehrere Protokolle hinweg funktioniert.
Das Signal-Slot-Modell von Qt passt perfekt zu Modbus-Anwendungen, die auf reaktive, asynchrone Kommunikation angewiesen sind. Anstatt Schleifen zum Abfragen eingehender Daten oder blockierende Aufrufe zu schreiben, kann Ihre Anwendung auf Modbus-Ereignisse reagieren. Wenn Sie beispielsweise eine Anfrage mit QModbusClient senden, erhalten Sie sofort ein QModbusReply-Objekt; die eigentliche Antwort trifft asynchron ein. Sie können das finished-Signal dieses Replys mit einem Slot (oder Lambda) verbinden – Qt ruft Ihren Handler automatisch auf, sobald die Antwort vorliegt.
Dieses nicht-blockierende, ereignisgesteuerte Design hält die Benutzeroberfläche (oder Hauptschleife) flüssig und vereinfacht das Thread-Management erheblich, während es gleichzeitig Echtzeit-Reaktionen auf Serverseite über Signale wie dataWritten erlaubt. Das Ergebnis ist sauberer Code – ohne manuelles Polling oder Flag-Abfragen – und perfekte Integration in die Qt-Architektur für reaktive GUIs und parallele I/O-Prozesse.
Das SerialBus-Modul von Qt stellt eine Reihe von Klassen bereit, mit denen sich Modbus-Clients oder -Server leicht in C++ implementieren lassen. Hier sind einige der wichtigsten Klassen und ihre Funktionen:
Diese Qt-Klassen arbeiten zusammen, um die Modbus-Kommunikation zu vereinfachen. Im Hintergrund übernimmt Qt das Öffnen serieller Schnittstellen (über QSerialPort) oder Sockets (QTcpSocket), die Nachrichtenformatierung, das Timing und die Thread-Sicherheit.
Mit der Qt-Modbus-API kann ein Embedded-Entwickler komplexe Modbus-Interaktionen mit nur wenigen Dutzend Zeilen C++-Code auf hohem Abstraktionsniveau implementieren – und das plattformübergreifend auf Linux, Windows oder anderen Qt-kompatiblen Plattformen. Es ist ein leistungsfähiges Werkzeug, das Entwicklungszeit spart und die Zuverlässigkeit durch Qt-erprobte Bibliotheken erhöht.
In diesem Beispiel erstellen wir einen Modbus-TCP-Client und -Server, die über localhost (127.0.0.1) auf Port 50200 kommunizieren. Der Server enthält 10 Spulen, mit denen der Client interagieren kann – durch Lesen oder Schreiben von Daten.
Für den Server konfigurieren wir 10 Spulen, auf die der Client zugreifen kann. Der Client kann Daten vom Server lesen oder Daten an diesen schreiben.
Um Modbus in Ihrer Qt-Anwendung zu verwenden, benötigen Sie das Qt Serial Bus Module. Bevor Sie beginnen, stellen Sie sicher, dass das Modul Qt Serial Bus in Ihrer Qt-Umgebung installiert ist. Es kann über das Qt Maintenance Tool installiert werden – zu finden unter Qt <Version> -> Additional Libraries.
Zunächst erstellen wir den Modbus-Server. Dank QModbusTcpServer ist dieser Prozess kinderleicht. Wir müssen lediglich ein QModbusTcpServer-Objekt instanziieren und die erforderlichen Verbindungsparameter zuweisen.
So richten Sie den Server ein:
server = new QModbusTcpServer(this); const QUrl currentUrl = QUrl::fromUserInput("127.0.0.1:50200"); server->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "Scythe Studio Modbus Server"); server->setConnectionParameter(QModbusDevice::NetworkPortParameter, currentUrl.port()); server->setConnectionParameter(QModbusDevice::NetworkAddressParameter, currentUrl.host()); server->setServerAddress(1);
Als Nächstes müssen wir die Daten definieren, die der Modbus-Server enthalten soll. In unserem Beispiel möchten wir 10 Spulen (NUM_COILS) einrichten.
Im Modbus-Kontext repräsentieren Spulen diskrete Werte, die entweder ein (1) oder aus (0) sein können. Sie werden typischerweise verwendet, um Schalter oder Relais zu steuern bzw. zu überwachen.
So definieren Sie dies im Code:
QModbusDataUnitMap reg; reg.insert(QModbusDataUnit::Coils, { QModbusDataUnit::Coils, 0, NUM_COILS }); server->setMap(reg);
Das Modbus-Datenmapping (reg) enthält alle Dateneinheiten (wie Spulen, Register usw.) für den Server. Der Befehl server->setMap(reg) wendet diese Konfiguration auf den Server an und legt die 10 Spulen fest.
Danach sind wir bereit, den Server zu starten.
if (!server->connectDevice()) { qDebug() << "Fehler beim Verbindungsaufbau mit Modbus: " << server->errorString(); }
Und das war’s! Das Einrichten des Servers ist ein Kinderspiel!
Um den Modbus-Client mit dem Server zu verbinden, erstellen wir ein QModbusTcpClient-Objekt, konfigurieren die notwendigen Eigenschaften und stellen dann die Verbindung mit folgendem Code her:
// Modbus-Client-Instanz erstellen m_modbusClient = new QModbusTcpClient(this); // Serveradresse und Port definieren const QUrl currentUrl = QUrl::fromUserInput("127.0.0.1:50200"); // Verbindungsparameter setzen m_modbusClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, currentUrl.host()); m_modbusClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, currentUrl.port()); // Verbindung zum Server versuchen if (!m_modbusClient->connectDevice()) { qCritical() << "Verbindung zum Server fehlgeschlagen:" << m_modbusClient->errorString(); } else { qDebug() << "Erfolgreich mit dem Server verbunden."; }
Um Daten an den Modbus-Server zu schreiben, erstellen wir zunächst eine Modbus-Dateneinheit (writeUnit), die die Spulen enthält, die wir aktualisieren möchten. Die Einheit beginnt bei Adresse 0 und enthält die Werte aus m_coils, die die Zustände der Spulen (wahr oder falsch) speichern.
QModbusDataUnit writeUnit = QModbusDataUnit(QModbusDataUnit::Coils, 0, NUM_COILS); for (qsizetype i = 0, total = writeUnit.valueCount(); i < total; ++i) { writeUnit.setValue(i, m_coils.at(i)); }
Anschließend senden wir die Schreibanfrage mit sendWriteRequest(). Wenn die Anfrage erfolgreich ist, verbinden wir uns mit dem finished-Signal, um die Antwort des Servers zu verarbeiten:
if (auto *reply = m_modbusClient->sendWriteRequest(writeUnit, 1)) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, this, [this, reply]() { if (reply->error() != QModbusDevice::NoError) { qDebug() << "Schreibfehler:" << reply->errorString(); } reply->deleteLater(); }); } else { reply->deleteLater(); // Broadcast-Antworten kommen sofort zurück } } else { qDebug() << "Schreibfehler:" << m_modbusClient->errorString(); }
Um Daten vom Modbus-Server zu lesen, senden wir eine Leseanfrage für Spulen und verarbeiten die Antwort wie folgt:
// Leseoperation für Spulen anfordern if (auto *reply = m_modbusClient->sendReadRequest(QModbusDataUnit(QModbusDataUnit::Coils, 0, NUM_COILS), 1)) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, this, [this]() { auto reply = qobject_cast(sender()); if (!reply) return; // Antwort auf Fehler prüfen if (reply->error() == QModbusDevice::NoError) { qDebug() << reply->result().values(); } else { qDebug() << "Fehler in der Leseantwort:" << reply->errorString(); } reply->deleteLater(); }); } else { delete reply; // Broadcast-Antworten kommen sofort zurück } }
Im folgenden Demovideo sehen Sie den vollständigen Ablauf, wie ein Modbus-TCP-Client mit einem Modbus-TCP-Server interagiert. Das Video zeigt, wie der Client eine Verbindung zum Server herstellt, eine Anfrage zum Lesen von Spulenwerten sendet und die Serverantwort verarbeitet.
Hinweis: Die Modbus-Antwort des Servers enthält immer einen 16-Bit-Block, auch wenn weniger Spulen angefordert werden. Das liegt daran, dass das Modbus-Protokoll typischerweise mit 16-Bit-Registern arbeitet. In diesem Beispiel antwortet der Server mit 16 Spulenwerten, obwohl der Client nur 10 Spulen ab Adresse 0 schreibt.
Die Verwendung von Qt zur Integration von Modbus in Embedded-Systemen bietet erheblichen Mehrwert. Sie profitieren von einer hochabstrahierten, plattformübergreifenden API, die die Komplexität von Bytes und Protokollen verbirgt, während gleichzeitig die Leistung und Flexibilität geliefert wird, die industrielle Anwendungen erfordern. Die integrierte Modbus-Unterstützung von Qt ermöglicht es Ihren Anwendungen, zuverlässig mit SPS, Sensoren und anderen Feldgeräten zu kommunizieren – sei es über eine klassische RS-485-Leitung oder ein modernes Ethernet-Netzwerk. Das Signal-Slot-Modell des Frameworks passt perfekt zu reaktiver, ereignisgesteuerter Kommunikation und führt zu sauberem, wartbarem Code.
Aus Sicht des Projektmanagements kann die Qt-Entwicklung die Entwicklungs- und Debug-Zeiten für Modbus-Funktionen erheblich verkürzen. Zudem bietet sie die Skalierbarkeit, um mit minimalen Änderungen vom Prototyp zur Serienproduktion auf verschiedenster Hardware überzugehen. Das bedeutet eine schnellere Markteinführung und geringere langfristige Wartungskosten.
Benötigen Sie Expertenhilfe bei der Integration von Modbus (oder anderen Industrieprotokollen) in Ihr Qt-Projekt? Kontaktieren Sie uns.
Kommen wir zur Sache: Es ist eine Herausforderung, Top-Qt-QML-Entwickler zu finden. Helfen Sie sich selbst und starten Sie die Zusammenarbeit mit Scythe Studio – echten Experten im Qt C++ Framework.
Entdecken Sie unsere Fähigkeiten!Benutzer von eingebetteten Geräten – von Industriecontrollern bis hin zu Unterhaltungselektronik – sind sich oft nicht der versteckten Schwachstellen bewusst, […]
Grafische Benutzeroberflächen (GUIs) werden in eingebetteten Geräten – von Haushaltsgeräten bis hin zu medizinischer Ausrüstung – zunehmend wichtiger, um eine […]
Warum ist Visualisierung im Finanzwesen so wichtig? An erster Stelle steht die Klarheit. Gute Visualisierungen durchdringen die Komplexität und machen […]