Benutzerdefinierter Modbus-Befehl
Das Beispiel zeigt, wie benutzerdefinierte Modbus-Funktionscodes verarbeitet werden.
Das Beispiel fungiert in einer einzigen Anwendung sowohl als Modbus-Client als auch als Modbus-Server. Die Verbindung zwischen beiden wird über Modbus-TCP hergestellt. Es dient zum Senden und Empfangen benutzerdefinierter Modbus-Anfragen und passt seine internen Zustände entsprechend der Anfrage und der Antwort an.
Der Hauptzweck des Beispiels besteht darin, Demo-Code zur Implementierung eines Modbus-Clients oder Modbus-Servers bereitzustellen, der benutzerdefinierte Modbus-Funktionscodes verarbeitet.

Benutzerdefinierte Modbus-Funktionscodes
Das Modbus-Protokoll unterstützt Funktionscodes im Bereich 1 - 127 (0x01 - 0x7F HEX). Die meisten Funktionscodes sind genau definiert und öffentlich dokumentiert. Es gibt jedoch zwei Bereiche, die für benutzerdefinierte Funktionen genutzt werden können. Dies sind 65 - 72 (0x41 - 48 HEX) und 100 - 110 (0x64 - 0x6E HEX). Der Benutzer kann Funktionscodes aus diesen Bereichen auswählen und sie auf individuelle Weise verarbeiten.
Diese Anwendung verwendet den Funktionscode 65 (0x41 HEX) zur Implementierung des Befehls „ CustomRead “ und den Funktionscode 66 (0x42 HEX) zur Implementierung des Befehls „ CustomWrite “. In diesem Beispiel werden die benutzerdefinierten Befehle lediglich zum Lesen und Schreiben der Datei „ Holding Registers “ verwendet.
Senden benutzerdefinierter Modbus-Befehle
Die benutzerdefinierten Modbus-Befehle werden mithilfe der Methode „ QModbusClient::sendRawRequest()“ gesendet. Diese Methode erfordert die Erstellung eines „ QModbusRequest “-Objekts mit dem gewünschten Funktionscode und einer Liste von Argumenten, die in ein „ QByteArray “-Objekt kodiert werden:
QModbusDataUnit unit {
QModbusDataUnit::HoldingRegisters,
ui->startAddress->value(),
qMin(ui->numberOfRegisters->currentText().toUShort(),
quint16(10 - ui->startAddress->value())) // nicht über 10 Einträge hinausgehen
};
for (qsizetype i = 0, total = unit.valueCount(); i < total;++i)
unit.setValue(i, m_model->m_registers[i + unit.startAddress()]);
const quint8 byteCount = quint8(unit.valueCount() * 2);
QModbusRequest writeRequest {
QModbusPdu::FunctionCode(ModbusClient::CustomWrite),
quint16(unit.startAddress()),
quint16(unit.valueCount()), byteCount, unit.values()
};Die Methode ` QModbusClient::sendRawRequest()` gibt ein ` QModbusReply `-Objekt zurück, mit dem wie gewohnt auf Fehler geprüft werden kann:
if (auto *reply = m_client.sendRawRequest(writeRequest, ui->serverAddress->value())) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [this, reply]() {
if (reply->error() == QModbusDevice::ProtocolError) {
statusBar()->showMessage(tr("Write response error: %1 (Modbus exception: 0x%2)")
.arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16),
5000);
} else if (reply->error() != QModbusDevice::NoError) {
statusBar()->showMessage(tr("Write response error: %1 (code: 0x%2)").
arg(reply->errorString()).arg(reply->error(), -1, 16), 5000);
}
reply->deleteLater();
}
);
}
} else {
statusBar()->showMessage(tr("Write error: ") + m_client.errorString(), 5000);
}Benutzerdefinierter Modbus-Server
Der benutzerdefinierte Server leitet sich von der Klasse ` QModbusTcpServer ` ab. Er überschreibt die Methode ` QModbusServer::processPrivateRequest()`.
class ModbusServer : public QModbusTcpServer
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(ModbusServer)
public:
ModbusServer(QObject *parent = nullptr);
private:
QModbusResponse processPrivateRequest(const QModbusPdu &request) override;
};Die Basis-Serverklasse ruft die Methode ` processPrivateRequest()` auf, wenn ein Befehl mit einem benutzerdefinierten Funktionscode empfangen wird.
Die benutzerdefinierte Implementierung verarbeitet den Befehl „ CustomRead “, indem sie einen „ QModbusResponse “ mit den Werten der angeforderten Register generiert:
if (ModbusClient::CustomRead == request.functionCode()) {
quint16 startAddress, count;
request.decodeData(&startAddress, &count);
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, startAddress, count);
if (!data(&unit)) {
return QModbusExceptionResponse(request.functionCode(),
QModbusExceptionResponse::IllegalDataAddress);
}
return QModbusResponse(request.functionCode(), startAddress, quint8(count * 2), unit.values());
}Die Verarbeitung des Befehls „ CustomWrite “ umfasst das Extrahieren der neuen Werte aus dem empfangenen „ QModbusPdu “, die Durchführung der eigentlichen Wertaktualisierung sowie die Rückgabe eines „ QModbusResponse “ mit den Registern, die tatsächlich aktualisiert wurden:
if (ModbusClient::CustomWrite == request.functionCode()) {
quint8 byteCount;
quint16 startAddress, numberOfRegisters;
request.decodeData(&startAddress, &numberOfRegisters, &byteCount);
if (byteCount % 2 != 0) {
return QModbusExceptionResponse(request.functionCode(),
QModbusExceptionResponse::IllegalDataValue);
}
const QByteArray pduData = request.data().remove(0, WriteHeaderSize);
QDataStream stream(pduData);
QList<quint16> values;
for (int i = 0; i < numberOfRegisters; i++) {
quint16 tmp;
stream >> tmp;
values.append(tmp);
}
if (!writeData({QModbusDataUnit::HoldingRegisters, startAddress, values})) {
return QModbusExceptionResponse(request.functionCode(),
QModbusExceptionResponse::ServerDeviceFailure);
}
return QModbusResponse(request.functionCode(), startAddress, numberOfRegisters);
}Benutzerdefinierter Modbus-Client
Der benutzerdefinierte Client leitet sich von der Klasse „ QModbusTcpClient “ ab. Er überschreibt die Methode „ QModbusClient::processPrivateResponse()“.
class ModbusClient : public QModbusTcpClient
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(ModbusClient)
public:
ModbusClient(QObject *parent = nullptr);
static constexpr QModbusPdu::FunctionCode CustomRead {QModbusPdu::FunctionCode(0x41)};
static constexpr QModbusPdu::FunctionCode CustomWrite {QModbusPdu::FunctionCode(0x42)};
private:
bool processPrivateResponse(const QModbusResponse &response, QModbusDataUnit *data) override;
};Die Basis-Client-Klasse ruft die Methode ` processPrivateResponse()` auf, um die Serverantworten mit benutzerdefinierten Funktionscodes zu verarbeiten.
Die benutzerdefinierte Implementierung verarbeitet die Antworten mit den Funktionscodes „ CustomRead “ und „ CustomWrite “:
bool ModbusClient::processPrivateResponse(const QModbusResponse &response, QModbusDataUnit *data)
{
if (!response.isValid())
return QModbusClient::processPrivateResponse(response, data);
if (CustomRead == response.functionCode())
return collateBytes(response, data);
if (CustomWrite == response.functionCode())
return collateMultipleValues(response, data);
return QModbusClient::processPrivateResponse(response, data);
}Die Antwort „ CustomRead “ wird verarbeitet, indem der übermittelte „ QModbusPdu “ dekodiert und die Werte für die angeforderten Register extrahiert werden:
static bool collateBytes(const QModbusPdu &response, QModbusDataUnit *data)
{
if (response.dataSize() < MinimumReadResponseSize)
return false;
quint16 address; quint8 byteCount;
response.decodeData(&address, &byteCount);
if (byteCount % 2 != 0)
return false;
if (data) {
QDataStream stream(response.data().remove(0, 3));
QList<quint16> values;
const quint8 itemCount = byteCount / 2;
for (int i = 0; i < itemCount; i++) {
quint16 tmp;
stream >> tmp;
values.append(tmp);
}
*data = {QModbusDataUnit::HoldingRegisters, address, values};
}
return true;
}Die Antwort auf den Befehl „ CustomWrite “ wird verarbeitet, indem die Antwortparameter einfach überprüft werden:
static bool collateMultipleValues(const QModbusPdu &response, QModbusDataUnit *data)
{
if (response.dataSize() != WriteResponseSize)
return false;
quint16 address, count;
response.decodeData(&address, &count);
if (count < 1 || count > 10)
return false;
if (data)
*data = {QModbusDataUnit::HoldingRegisters, address, count};
return true;
}Ausführen des Beispiels
Sie können das Beispiel ausführen unter:
- Qt Creator
Öffnen Sie den Modus „ Welcome “ und wählen Sie das Beispiel unter „ Examples “ aus. Weitere Informationen finden Sie unter Qt Creator: Tutorial: Erstellen und Ausführen.
- Qt Extension for Visual Studio Code
Führen Sie den Befehl „ Qt: Open Qt examples “ aus dem Verzeichnis „ Command Palette “ aus und wählen Sie das Beispiel aus der Liste aus. Weitere Informationen finden Sie unter Qt Extension for Visual Studio Code: Tutorial: Erstellen und Ausführen.
Dieses Beispiel kann nicht in Verbindung mit anderen Anwendungen verwendet werden. Sobald das Beispiel gestartet ist, kann es nur benutzerdefinierte Modbus-Befehle innerhalb der Anwendung selbst austauschen. Alle Interaktionen zwischen Client und Server erfolgen über das Modbus-TCP-Protokoll.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.