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 의 값을 변경할 수 있습니다. UI에서 값이 업데이트되면 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 서버 예제는 이 신호에 연결하여 업데이트된 값을 추출하고, 이에 따라 UI를 업데이트합니다:
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.