Qt ModbusTCP ModbusRTU 使用同步读和异步写

本文介绍如何使用Qt库进行Modbus TCP客户端开发,重点讲解了寄存器的同步读取方法及线圈寄存器的异步写操作,并提供了具体的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用Qt自带的库开发,添加相关头文件

#include <QModbusTcpClient>
#include <QModbusReply>
#include <QSerialPort>
#include <QModbusDataUnit>
#include <QModbusRtuSerialMaster>

一、寄存器说明

Modbus寄存器的操作包括读写和只读。具体如下:

    enum RegisterType {
        Invalid,
        DiscreteInputs,
        Coils,
        InputRegisters,
        HoldingRegisters
    };

分别叫做:

  • 离散输入寄存器(只读,通常为开关量输入)
  • 线圈寄存器(读写,一般为继电器的控制)
  • 输入寄存器(只读,一般为模拟量输入)
  • 保持寄存器(读写,一般状态参数控制)

二、同步读取

QT采用事件处理机制,由于Modbus读取过程通常有时延,Qt机制不适合采用while延时等待读取的方式。网上大多采用的是基于ModbusReply的Finished信号,做异步处理。导致在获取寄存器数据时候比较麻烦。因此,可以考虑使用事件循环做同步处理。

读寄存器数据的接口:

QVector<quint16>
MainWindow::readModbusTcpUnit(QModbusDataUnit::RegisterType type, int startAddr, int numbers, int serverID, bool *isOK)
  • type:指明读取的类型,由RegisterType类型定义
  • startAddr:起始地址
  • numbers:读取的数量
  • serverID:服务器的ID
  • isOK:当前操作是否成功
  • 返回值:QVector<quint16> 类型,读取的数据放在向量中 

以下是具体实现:

QVector<quint16>
MainWindow::readModbusTcpUnit(QModbusDataUnit::RegisterType type, int startAddr, int numbers, int serverID, bool *isOK)
{
    QVector<quint16> results;
    results.clear();

    if (mModbusClient->state() != QModbusDevice::ConnectedState) {
        *isOK = false;
        return results;
    }

    QModbusDataUnit readUnit(type, startAddr, static_cast<quint16>(numbers));

    auto *reply = mModbusClient->sendReadRequest(readUnit, serverID);

    if (!reply->isFinished()) {
        QEventLoop loop;
        connect(reply, &QModbusReply::finished,&loop,&QEventLoop::quit);
        eventLoop.exec();
    }

    if (reply->error() == QModbusDevice::NoError) {

        const QModbusDataUnit unit = reply->result();

        QString strType;
        switch (type){
            case QModbusDataUnit::Coils: strType = "Coils"; break;
            case QModbusDataUnit::DiscreteInputs: strType = "DiscreteInputs"; break;
            case QModbusDataUnit::HoldingRegisters: strType = "HoldingRegisters"; break;
            case QModbusDataUnit::InputRegisters: strType = "InputRegisters"; break;
        }

        qDebug()<<"read "<<strType<< " startAddr = "<<startAddr<<" numbers = "<<numbers<<" values = " <<unit.values();

        *isOK = true;
        results = unit.values();
    } else {
        *isOK = false;
    }

    delete reply;
    return results;
}

使用方法:

/*
 * bool isOK;
 * QVector<quint16> readUnit = readModbusTcpUnit(QModbusDataUnit::Coils, 0, 10, SERVER_ID, &isOK);
 * QVector<quint16> readUnit = readModbusTcpUnit(QModbusDataUnit::DiscreteInputs, 0, 10, SERVER_ID, &isOK);
 * QVector<quint16> readUnit = readModbusTcpUnit(QModbusDataUnit::HoldingRegisters, 0, 10, SERVER_ID, &isOK);
 * QVector<quint16> readUnit = readModbusTcpUnit(QModbusDataUnit::InputRegisters, 0, 10, SERVER_ID, &isOK);
*/

三、异步写

写寄存器只有Coils和HoldingRegisters可以操作,写操作使用异步执行。

接口如下:

bool
MainWindow::writeModbusTcpCoils(QVector<quint16>  coilsValues, int startAddr, int numbers, int serverID)
  • coilsValues:向量,需要写入的值;
  • startAddr:起始地址
  • numbers:需要写寄存器的数量
  • serverID:服务器的ID

以写线圈为例,实现如下:

void
MainWindow::writeModbusTcpCoils(QVector<quint16>  coilsValues, int startAddr, int numbers, int serverID)
{
    if (coilsValues.size() < numbers){
        qDebug()<<"error : coilsValue size < numbers";
        return;
    }

    if (mModbusClient->state() != QModbusDevice::ConnectedState) {
        qDebug()<<"error : disConnectedState";
        return;
    }

    QModbusDataUnit writeUnit(QModbusDataUnit::Coils, startAddr, static_cast<quint16>(numbers));

    for (int valueIdx = 0; valueIdx < writeUnit.valueCount(); ++valueIdx) {
        writeUnit.setValue(valueIdx, coilsValues.at(valueIdx));
    }

    auto *reply = mModbusClient->sendWriteRequest(writeUnit, serverID);

    if (!reply->isFinished()) {
        connect(reply, &QModbusReply::finished, this, [this, reply]() {
            if (reply->error() == QModbusDevice::ProtocolError) {
                qDebug() << “ProtocolError”;

            } else if (reply->error() != QModbusDevice::NoError) {
                qDebug() << “Error”;
            }
        });
    }

    reply->deleteLater();
}

使用方法:

/*
 * QVector<quint16> values;
 * values.push_back(1);
 * values.push_back(0);
 * values.push_back(1);
 * writeModbusTcpCoils(values, 0, 3, SERVER_ID);
 * writeModbusTcpCoils(values, 3, 2, SERVER_ID);
*/

写保持寄存器和写线圈类似,不在赘述。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值