Qt应用软件【协议篇】modbus-rtu示例

文章目录

1.QT Modbus类

Qt的Modbus RTU库提供了一组API,用于实现Modbus RTU通信协议的主机(Master)和从机(Slave)功能。以下是Qt Modbus RTU库中的主要API及其功能的简要介绍:

QModbusRtuSerialMaster类

  • QModbusRtuSerialMaster是用于创建Modbus RTU主机的类。
  • setConnectionParameter:配置串口参数,如串口名称、波特率、数据位等。
  • connectDevice:连接到Modbus从机设备。
  • sendReadRequest:发送读取请求以从Modbus从机读取数据。
  • sendWriteRequest:发送写入请求以向Modbus从机写入数据。
  • sendCustomRequest:发送自定义Modbus请求。
  • disconnectDevice:断开与Modbus从机的连接。
  • error:获取Modbus通信的错误状态。
  • errorString:获取错误状态的字符串描述。

QModbusResponse类

  • QModbusResponse是Modbus RTU响应的基类。
  • error:获取响应的错误状态。
  • errorString:获取错误状态的字符串描述。
  • rawResult:获取原始响应数据。

QModbusDataUnit类

  • QModbusDataUnit用于表示Modbus RTU数据单元,包括读取或写入的寄存器类型、起始地址和数据。
  • HoldingRegisters:保持寄存器类型。
  • InputRegisters:输入寄存器类型。
  • Coils:线圈类型。
  • DiscreteInputs:离散输入类型。
  • startAddress:获取数据单元的起始地址。
  • valueCount:获取数据单元中数据值的数量。
  • value:获取或设置数据单元中特定位置的数据值。

QModbusReply类

  • QModbusReply是Modbus RTU请求的响应。
  • error:获取响应的错误状态。
  • errorString:获取错误状态的字符串描述。
  • result:获取响应中的数据单元结果。
  • finished:信号,指示请求已完成。

QModbusClient类(可选):

  • QModbusClient是Qt中的Modbus RTU客户端类,提供更高级别的Modbus RTU主机功能。

QModbusServer类(可选):

  • QModbusServer是Qt中的Modbus RTU服务器类,用于实现Modbus RTU从机功能。

以上是Qt Modbus RTU库的一些核心API,用于实现Modbus通信的常见任务。您可以根据您的具体需求使用这些API来创建Modbus RTU主机或从机,并进行读取和写入操作。在实际使用中,根据Qt版本可能会有一些差异,因此建议查阅相关Qt文档以获取更详细的信息和示例代码。

2.Modbus主机和从机概念

Modbus是一种串行通信协议,常用于工业环境中设备之间的通信。在Modbus协议中,有两种角色:主机(Master)和从机(Slave)。它们之间的主要区别在于通信过程中的作用和职责。

  1. Modbus主机(Master):

    • 控制角色:Modbus主机控制着数据交换过程,它发起请求并接收响应。
    • 发起请求:主机发送命令给从机,这些命令可以是读取数据、写入数据、诊断等。
    • 处理响应:主机接收并处理从机的响应数据。
    • 通常只有一个主机:在一个Modbus网络中,通常只有一个主机控制所有的通信。
  2. Modbus从机(Slave):

    • 被动角色:从机响应主机的请求,不会主动发起通信。
    • 执行请求:从机根据主机的命令执行操作,如返回数据或更改其内部寄存器的值。
    • 多个从机:一个Modbus网络可以有多个从机,每个从机都有一个唯一的地址。
    • 简单设备:从机通常是传感器、执行器、驱动器等较为简单的设备。

在Modbus协议中,主机和从机之间的通信是不对称的。主机总是发起通信,而从机只是对主机的请求做出响应。这种模式使得Modbus非常适合于工业自动化和控制系统,其中一个中央控制器(主机)需要与多个现场设备(从机)进行通信。

组网

Modbus 协议支持几种不同的组网形状(拓扑结构)。这些拓扑结构的选择取决于具体的应用需求、物理环境和设备类型。以下是Modbus最常见的组网形状:

  1. 总线(Bus)拓扑 :
    • 所有设备都连接到一个共享的通信线路上。
    • 每个设备都通过其唯一的地址来识别。
    • 这种拓扑简单且易于实施,但线路故障可能影响整个网络。

连接 Modbus Modbus Modbus 主机 从机1 从机2 从机3

  1. 星形(Star)拓扑:

    • 每个从机通过独立的线路直接连接到主机。

    • 这种拓扑可以降低线路故障对整个网络的影响,但可能需要更多的布线。

    Modbus Modbus Modbus 主机 从机1 从机2 从机3

3.树形(Tree)或分支(Branched)拓扑:

  • 类似于总线拓扑,但在主通信线路上可以有分支。
  • 这种拓扑更灵活,可以更好地适应复杂的物理环境。

Modbus Modbus Modbus Modbus 主机 从机1 从机2 从机3 从机4

从机示例

cpp 复制代码
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QModbusRtuSerialMaster>
#include <QSerialPort>
#include <QDebug>
#include <QVBoxLayout>

class ModbusMasterWidget : public QWidget {
public:
    ModbusMasterWidget(QWidget *parent = nullptr) : QWidget(parent) {
        modbusMaster = new QModbusRtuSerialMaster(this);

        // 配置串口
        modbusMaster->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1");
        modbusMaster->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);

        QVBoxLayout *layout = new QVBoxLayout(this);
        QPushButton *readButton = new QPushButton("读取Modbus数据", this);
        layout->addWidget(readButton);

        connect(readButton, &QPushButton::clicked, this, &ModbusMasterWidget::onReadButtonClicked);
    }

private slots:
    void onReadButtonClicked() {
        if (!modbusMaster->connectDevice()) {
            qDebug() << "无法连接Modbus主机!";
            return;
        }

        const int serverAddress = 1; // Modbus从机地址
        QModbusDataUnit readRequest(QModbusDataUnit::HoldingRegisters, 0, 10); // 读取保持寄存器

        if (auto *reply = modbusMaster->sendReadRequest(readRequest, serverAddress)) {
            if (!reply->isFinished()) {
                connect(reply, &QModbusReply::finished, this, &ModbusMasterWidget::onReadFinished);
            } else {
                delete reply; // 同步请求已完成
            }
        } else {
            qDebug() << "无法发送Modbus请求!";
        }
    }

    void onReadFinished() {
        auto *reply = qobject_cast<QModbusReply *>(sender());
        if (!reply) {
            return;
        }

        if (reply->error() == QModbusDevice::NoError) {
            const QModbusDataUnit unit = reply->result();
            for (uint i = 0; i < unit.valueCount(); i++) {
                qDebug() << "寄存器" << unit.startAddress() + i << "的值:" << unit.value(i);
            }
        } else {
            qDebug() << "Modbus错误:" << reply->errorString();
        }
        reply->deleteLater();
    }

private:
    QModbusRtuSerialMaster *modbusMaster;
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    ModbusMasterWidget widget;
    widget.show();
    return app.exec();
}

主机示例

cpp 复制代码
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QModbusRtuSerialMaster>
#include <QSerialPort>
#include <QDebug>
#include <QVBoxLayout>

class ModbusMasterWidget : public QWidget {
public:
    ModbusMasterWidget(QWidget *parent = nullptr) : QWidget(parent) {
        modbusMaster = new QModbusRtuSerialMaster(this);

        // 配置串口
        modbusMaster->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1");
        modbusMaster->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
        modbusMaster->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);

        QVBoxLayout *layout = new QVBoxLayout(this);
        QPushButton *writeButton = new QPushButton("设置Modbus数据", this);
        layout->addWidget(writeButton);

        connect(writeButton, &QPushButton::clicked, this, &ModbusMasterWidget::onWriteButtonClicked);
    }

private slots:
    void onWriteButtonClicked() {
        if (!modbusMaster->connectDevice()) {
            qDebug() << "无法连接Modbus主机!";
            return;
        }

        const int serverAddress = 1; // Modbus从机地址
        QModbusDataUnit writeRequest(QModbusDataUnit::HoldingRegisters, 0, 1); // 写入单个保持寄存器
        writeRequest.setValue(0, 1234); // 设置寄存器的值

        if (auto *reply = modbusMaster->sendWriteRequest(writeRequest, serverAddress)) {
            if (!reply->isFinished()) {
                connect(reply, &QModbusReply::finished, this, &ModbusMasterWidget::onWriteFinished);
            } else {
                delete reply; // 同步请求已完成
            }
        } else {
            qDebug() << "无法发送Modbus写入请求!";
        }
    }

    void onWriteFinished() {
        auto *reply = qobject_cast<QModbusReply *>(sender());
        if (!reply) {
            return;
        }

        if (reply->error() == QModbusDevice::NoError) {
            qDebug() << "写入操作成功完成";
        } else {
            qDebug() << "Modbus写入错误:" << reply->errorString();
        }
        reply->deleteLater();
    }

private:
    QModbusRtuSerialMaster *modbusMaster;
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    ModbusMasterWidget widget;
    widget.show();
    return app.exec();
}
相关推荐
ROC_bird..1 小时前
STL - vector的使用和模拟实现
开发语言·c++
机器视觉知识推荐、就业指导1 小时前
C++中的栈(Stack)和堆(Heap)
c++
Mr_Xuhhh3 小时前
递归搜索与回溯算法
c语言·开发语言·c++·算法·github
无敌岩雀3 小时前
C++设计模式行为模式———命令模式
c++·设计模式·命令模式
Qter_Sean4 小时前
自己动手写Qt Creator插件
开发语言·qt
何曾参静谧4 小时前
「QT」文件类 之 QIODevice 输入输出设备类
开发语言·qt
爱吃生蚝的于勒5 小时前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
小白学大数据7 小时前
Python爬虫开发中的分析与方案制定
开发语言·c++·爬虫·python
versatile_zpc9 小时前
C++初阶:类和对象(上)
开发语言·c++
小鱼仙官9 小时前
MFC IDC_STATIC控件嵌入一个DIALOG界面
c++·mfc