이 페이지에서

Modbus 클라이언트

이 예제는 Modbus 클라이언트 애플리케이션을 구현합니다.

이 예제는 시리얼 라인이나 TCP를 통해 Modbus 요청을 전송하는 Modbus 클라이언트 역할을 합니다. 표시된 대화 상자를 통해 표준 요청을 정의하고 수신된 응답을 확인할 수 있습니다.

이 예제는 Modbus 서버 예제나 TCP 또는 시리얼 포트를 통해 연결된 다른 Modbus 장치와 함께 사용해야 합니다.

이 예제에서 사용되는 주요 클래스:

연결 설정, 읽기/쓰기 섹션, 그리고 16진수 값과 함께 레지스터 주소를 표시하는 결과를 갖춘 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() 신호를 수신하면, reply 객체를 사용하여 데이터를 가져오거나 읽기 오류를 확인할 수 있습니다:

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.