Modbus 客户端
该示例实现了一个 Modbus 客户端应用程序。
该示例作为 Modbus 客户端,通过串行线路或 TCP 发送 Modbus 请求。显示的对话框允许定义标准请求并显示收到的响应。
该示例必须与Modbus 服务器示例或其他通过 TCP 或串行端口连接的 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 对象用于检查写入错误。
运行示例
运行示例 Qt Creator,打开Welcome 模式,并从Examples 中选择示例。更多信息,请参阅Qt Creator: 教程:构建并运行。
© 2025 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.