Modbus 服务器
该示例实现了一个 Modbus 服务器应用程序。
该示例充当 Modbus 服务器,用于接收标准的 Modbus 请求,根据请求调整其内部状态,并返回相应的响应。
本示例必须与Modbus 客户端示例配合使用。应在启动 Modbus 客户端示例之前,先启动本示例并将其置于监听状态。此后,这两个示例之间的交互将通过 Modbus 协议进行。
本示例中使用的关键类:

创建 QModbusServer
要进行任何通信,都需要一个QModbusServer 实例。根据指定的连接类型,本示例可以实例化QModbusRtuSerialServer (用于串行通信)或QModbusTcpServer (用于基于TCP的通信)。
auto type = static_cast<ModbusConnection>(index);
if (type == Serial) {
#if QT_CONFIG(modbus_serialport)
modbusDevice = new QModbusRtuSerialServer(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 QModbusTcpServer(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"));
}创建服务器后,使用QModbusServer::setMap()方法设置寄存器映射。客户端应用程序将使用此寄存器映射来读写服务器数据。
QModbusDataUnitMap reg;
reg.insert(QModbusDataUnit::Coils, { QModbusDataUnit::Coils, 0, 10 });
reg.insert(QModbusDataUnit::DiscreteInputs, { QModbusDataUnit::DiscreteInputs, 0, 10 });
reg.insert(QModbusDataUnit::InputRegisters, { QModbusDataUnit::InputRegisters, 0, 10 });
reg.insert(QModbusDataUnit::HoldingRegisters, { QModbusDataUnit::HoldingRegisters, 0, 10 });
modbusDevice->setMap(reg);随后需指定通信参数和服务器地址。通信参数取决于通信类型:
if (static_cast<ModbusConnection>(ui->connectType->currentIndex()) == Serial) {
modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
ui->portEdit->text());
#if QT_CONFIG(modbus_serialport)
modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
m_settingsDialog->settings().parity);
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
m_settingsDialog->settings().baud);
modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
m_settingsDialog->settings().dataBits);
modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
m_settingsDialog->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->setServerAddress(ui->serverEdit->text().toInt());在创建服务器并指定所有参数后,使用QModbusServer::connectDevice()方法连接到Modbus网络。
修改本地值
该示例允许通过提供的下拉列表或文本输入框,修改所有受支持的register types 的值。当用户界面中的值更新后,将调用QModbusServer::setData()方法来更新服务器上的实际值:
void MainWindow::bitChanged(int id, QModbusDataUnit::RegisterType table, bool value)
{
if (!modbusDevice)
return;
if (!modbusDevice->setData(table, quint16(id), value))
statusBar()->showMessage(tr("Could not set data: ") + modbusDevice->errorString(), 5000);
}
void MainWindow::setRegister(const QString &value)
{
if (!modbusDevice)
return;
const QString objectName = QObject::sender()->objectName();
if (registers.contains(objectName)) {
bool ok = true;
const quint16 id = quint16(QObject::sender()->property("ID").toUInt());
if (objectName.startsWith(QStringLiteral("inReg"))) {
const auto uval = value.toUShort(&ok, 16);
if (ok)
ok = modbusDevice->setData(QModbusDataUnit::InputRegisters, id, uval);
} else if (objectName.startsWith(QStringLiteral("holdReg"))) {
const auto uval = value.toUShort(&ok, 16);
if (ok)
ok = modbusDevice->setData(QModbusDataUnit::HoldingRegisters, id, uval);
}
if (!ok)
statusBar()->showMessage(tr("Could not set register: ") + modbusDevice->errorString(),
5000);
}
}处理远程写入
Modbus客户端能够通过发送写入请求来更新Coils 和HoldingRegisters 。一旦服务器端通过此类请求更新了值,就会触发QModbusServer::dataWritten()信号。
connect(modbusDevice, &QModbusServer::dataWritten,
this, &MainWindow::updateWidgets);Modbus 服务器示例会订阅此信号,提取更新后的值,并据此更新用户界面:
void MainWindow::updateWidgets(QModbusDataUnit::RegisterType table, int address, int size)
{
for (int i = 0; i < size; ++i) {
quint16 value;
QString text;
switch (table) {
case QModbusDataUnit::Coils:
modbusDevice->data(QModbusDataUnit::Coils, quint16(address + i), &value);
coilButtons.button(address + i)->setChecked(value);
break;
case QModbusDataUnit::HoldingRegisters:
modbusDevice->data(QModbusDataUnit::HoldingRegisters, quint16(address + i), &value);
registers.value(QStringLiteral("holdReg_%1").arg(address + i))->setText(text
.setNum(value, 16));
break;
default:
break;
}
}
}运行示例
您可以通过以下方式运行该示例:
- Qt Creator
打开Welcome 模式,并从Examples 中选择该示例。有关更多信息,请参阅Qt Creator :教程:构建与运行。
- Qt Extension for Visual Studio Code
在Command Palette 中运行Qt: Open Qt examples 命令,并从列表中选择该示例。有关更多信息,请参阅Qt Extension for Visual Studio Code :教程:构建和运行。
© 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.