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.
Überblick über das Modbus-Protokoll
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.
Modbus-Datenmodell
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.
Rahmenstruktur
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-Varianten: RTU, ASCII und TCP
Modbus existiert in mehreren Hauptvarianten, die alle dasselbe grundlegende Datenmodell und denselben Funktionsumfang teilen, jedoch unterschiedliche Übertragungsformate verwenden:
Modbus RTU
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.
Modbus ASCII
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.
Modbus TCP/IP
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. |
Qt und Modbus – Überblick
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.
Warum Qt für die Modbus-Kommunikation verwenden?
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:
Abstraktion auf hoher Ebene
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.
Eingebaute plattformübergreifende Unterstützung
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.
Effizientes ereignisbasiertes Modell
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.
Highlights der Qt-Modbus-API
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:
- QModbusClient – Schnittstellenklasse für ein Modbus-Master-/Client-Gerät, das Anfragen sendet. Sie verwenden diese Klasse (oder deren Unterklassen), um Lese-/Schreiboperationen an einen Modbus-Server (Slave) zu senden. Zum Beispiel sind QModbusRtuSerialMaster und QModbusTcpClient spezifische Implementierungen für seriell und TCP, die dieselbe API verwenden, aber unterschiedliche Transportarten bedienen. Ein einzelnes QModbusClient-Objekt kann alle Anfragen in Ihrer Anwendung verwalten und sie bei Bedarf in die Warteschlange stellen.
- QModbusServer – Schnittstellenklasse für ein Modbus-Slave-/Server-Gerät, das Anfragen empfängt und verarbeitet. In Qt können Sie QModbusServer (oder QModbusRtuSerialSlave/QModbusTcpServer) verwenden oder erweitern, um Daten für Modbus-Clients bereitzustellen. QModbusServer bietet Methoden zum Einrichten des Datenmodells (Spulen und Register) und übernimmt die Protokolllogik für Antwortnachrichten. Beispielsweise fügen Sie ein Register-Mapping in QModbusServer hinzu, und es antwortet automatisch mit den richtigen Werten, wenn ein Client diese anfragt, und sendet bei Änderungen entsprechende Signale.
- QModbusDataUnit – Containerklasse, die einen Block Modbus-Daten (eine Sequenz von Spulen oder Registern) repräsentiert. Sie verwenden QModbusDataUnit, um festzulegen, welche Daten gelesen oder geschrieben werden sollen. Zum Beispiel: Um 10 Halteregister ab Adresse 100 zu lesen, erstellen Sie ein QModbusDataUnit-Objekt vom Typ HoldingRegisters mit Startadresse 100 und Länge 10. Diese Klasse wird sowohl bei Anfragen als auch bei Rückgaben verwendet (die gelesenen Werte werden über QModbusDataUnit übermittelt).
- QModbusReply – Repräsentiert das Ergebnis einer asynchronen Modbus-Anfrage. Wenn Sie eine Lese-/Schreibfunktion von QModbusClient aufrufen, erhalten Sie ein QModbusReply-Pointer zurück. Sie können diesen auf Fehler überprüfen oder seine Signale verwenden, um benachrichtigt zu werden, wenn die Operation abgeschlossen ist. QModbusReply enthält die vom Server gelieferten Daten (zugänglich über reply->result(), das ein QModbusDataUnit-Objekt liefert) oder Fehlerinformationen, falls die Anfrage fehlgeschlagen ist. Es kapselt im Grunde die Modbus-Transaktion, sodass Sie später damit arbeiten können, wenn die Antwort eingetroffen ist.
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.
Praktisches Beispiel – Erstellung einer Qt-Modbus-Anwendung
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.
Vorbereitung
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.
Modbus-Server
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.
- Startadresse (0): Das Modbus-Protokoll verwendet eine nullbasierte Adressierung. Das bedeutet, dass die erste Spule bei Adresse 0 beginnt. Wenn Sie also die Spulen definieren, geben Sie an, ab welcher Adresse gelesen werden soll.
- Anzahl der Spulen (NUM_COILS = 10): Nach der Angabe der Startadresse definieren wir, wie viele Spulen erstellt werden sollen. In diesem Fall definieren wir 10 Spulen. Der Server wird also 10 diskrete Spulen ab Adresse 0 enthalten.
So definieren Sie dies im Code:
QModbusDataUnitMap reg;
reg.insert(QModbusDataUnit::Coils, { QModbusDataUnit::Coils, 0, NUM_COILS });
server->setMap(reg);
- QModbusDataUnit::Coils: Gibt an, dass wir im Modbus-Server Spulen definieren.
- 0: Dies ist die Startadresse der Spulen. Sie werden von Adresse 0 bis 9 adressiert.
- NUM_COILS (10): Definiert die Anzahl der Spulen. Der Server enthält 10 Spulen ab Adresse 0.
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!
Modbus-Client
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.";
}
Daten vom Modbus-Client schreiben
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();
}
- sendWriteRequest: Sendet eine Anfrage, um die in writeUnit enthaltenen Daten an den Server mit Adresse 1 zu schreiben.
- QModbusReply::finished: Wenn der Server antwortet, wird das finished-Signal ausgelöst. Bei einem Fehler geben wir eine Fehlermeldung aus, andernfalls wird das Antwortobjekt freigegeben.
Daten vom Server lesen
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
}
}
- sendReadRequest: Sendet eine Anfrage zum Lesen von Spulen ab Adresse 0 für die angegebene Anzahl (NUM_COILS) an Serveradresse 1.
- QModbusReply::finished: Sobald der Server antwortet, wird das Signal ausgelöst. Wir prüfen das Ergebnis und geben die empfangenen Werte aus.
Ergebnisse und Erkenntnisse
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.
- Das rechte Fenster im Video zeigt die Konsolenanwendung, die den Modbus-Server ausführt. Dieses Fenster simuliert das Verhalten des Servers, der auf Anfragen des Clients antwortet.
- Das linke Fenster zeigt die QML-Anwendung, die als Modbus-Client fungiert. Der Client kommuniziert mit dem Server, um Spulenwerte anzufordern, anzuzeigen und zu schreiben.
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.
Fazit
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.