本页内容

Modbus 客户端

该示例实现了一个 Modbus 客户端应用程序。

该示例作为 Modbus 客户端,通过串行端口或 TCP 发送 Modbus 请求。所示对话框允许定义标准请求,并显示收到的响应。

该示例必须与Modbus 服务器示例或通过 TCP 或串行端口连接的其他 Modbus 设备配合使用。

本示例中使用的关键类:

Modbus 客户端,包含连接设置、读/写部分,以及显示寄存器地址和十六进制值的返回结果

创建 QModbusClient

要进行任何通信,都需要一个QModbusClient 实例。根据指定的连接类型,本示例可以实例化QModbusRtuSerialClient (用于串行通信)或QModbusTcpClient (用于基于TCP的通信)。

    auto type = static_cast<ModbusConnection>(index);
    if (type == Serial) {
#if QT_CONFIG(modbus_serialport)
        modbusDevice = new QModbusRtuSerialClient(this);
        // Try to fill in the first available serial port name if the line edit
        // is empty, or contains a url (assume that ':' is only a part of url).
        const auto ports = QSerialPortInfo::availablePorts();
        const auto currentText = ui->portEdit->text();
        if (!ports.isEmpty() && (currentText.isEmpty() || currentText.contains(u':')))
            ui->portEdit->setText(ports.front().portName());
#endif
    } else if (type == Tcp) {
        modbusDevice = new QModbusTcpClient(this);
        const QUrl currentUrl = QUrl::fromUserInput(ui->portEdit->text());
        // Check if we already have <ip address>:<port>
        if (currentUrl.port() <= 0)
            ui->portEdit->setText(QLatin1String("127.0.0.1:50200"));
    }

创建客户端后,使用setConnectionParameter() 方法指定连接参数。参数因通信类型而异:

        const auto settings = m_settingsDialog->settings();
        if (static_cast<ModbusConnection>(ui->connectType->currentIndex()) == Serial) {
            modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                ui->portEdit->text());
#if QT_CONFIG(modbus_serialport)
            modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
                settings.parity);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                settings.baud);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                settings.dataBits);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
                settings.stopBits);
#endif
        } else {
            const QUrl url = QUrl::fromUserInput(ui->portEdit->text());
            modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
            modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
        }
        modbusDevice->setTimeout(settings.responseTime);
        modbusDevice->setNumberOfRetries(settings.numberOfRetries);

创建客户端并指定所有参数后,使用QModbusClient::connectDevice() 方法连接到 Modbus 网络。

读取数据

要从 Modbus 服务器读取数据,客户端需要指定服务器地址以及要读取的对象参数:

对象参数由QModbusDataUnit 类表示:

QModbusDataUnit MainWindow::readRequest() const
{
    const auto table = ui->writeTable->currentData().value<QModbusDataUnit::RegisterType>();

    int startAddress = ui->readAddress->value();
    Q_ASSERT(startAddress >= 0 && startAddress < 10);

    // do not go beyond 10 entries
    quint16 numberOfEntries = qMin(ui->readSize->currentText().toUShort(),
                                   quint16(10 - startAddress));
    return QModbusDataUnit(table, startAddress, numberOfEntries);
}

收集完参数后,使用sendReadRequest() 方法发送实际请求。该方法返回一个QModbusReply ,应以异步方式处理,因此使用QModbusReply::finished() 信号来检查响应何时准备就绪。

    if (auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);
        else
            delete reply; // broadcast replies return immediately
    } else {
        statusBar()->showMessage(tr("Read error: %1").arg(modbusDevice->errorString()), 5000);
    }

收到QModbusReply::finished()信号后,即可使用回复对象获取数据或检查读取错误:

void MainWindow::onReadReady()
{
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit unit = reply->result();
        for (qsizetype i = 0, total = unit.valueCount(); i < total; ++i) {
            const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i)
                                     .arg(QString::number(unit.value(i),
                                          unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
            ui->readValue->addItem(entry);
        }
    } else if (reply->error() == QModbusDevice::ProtocolError) {
        statusBar()->showMessage(tr("Read response error: %1 (Modbus exception: 0x%2)").
                                    arg(reply->errorString()).
                                    arg(reply->rawResult().exceptionCode(), -1, 16), 5000);
    } else {
        statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)").
                                    arg(reply->errorString()).
                                    arg(reply->error(), -1, 16), 5000);
    }

    reply->deleteLater();
}

写入数据

要将数据写入 Modbus 服务器,客户端需要指定服务器地址以及要写入的对象参数。与读取数据一样,使用QModbusDataUnit 类来表示待写入数据的相关信息。此时,数据还包括所需的values 。使用sendWriteRequest() 方法来写入所需数据:

    QModbusDataUnit writeUnit = writeRequest();
    QModbusDataUnit::RegisterType table = writeUnit.registerType();
    for (qsizetype i = 0, total = writeUnit.valueCount(); i < total; ++i) {
        const auto addr = i + writeUnit.startAddress();
        if (table == QModbusDataUnit::Coils)
            writeUnit.setValue(i, writeModel->m_coils[addr]);
        else
            writeUnit.setValue(i, writeModel->m_holdingRegisters[addr]);
    }

    if (auto *reply = modbusDevice->sendWriteRequest(writeUnit, ui->serverEdit->value())) {
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, [this, reply]() {
                const auto error = reply->error();
                if (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 (error != QModbusDevice::NoError) {
                    statusBar()->showMessage(tr("Write response error: %1 (code: 0x%2)").
                        arg(reply->errorString()).arg(error, -1, 16), 5000);
                }
                reply->deleteLater();
            });
        } else {
            // broadcast replies return immediately
            reply->deleteLater();
        }
    } else {
        statusBar()->showMessage(tr("Write error: %1").arg(modbusDevice->errorString()), 5000);
    }

与读取数据一样,使用返回的QModbusReply 对象来检查写入错误。

运行示例

您可以从以下位置运行示例:

示例项目 @ code.qt.io

© 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.