当前位置: 首页 > news >正文

Qt ModbusTCP和ModBusRTU读写数据

文章目录

  • ModbusTCP和ModBusRTU 的区别
  • ModbusTCP
    • 添加模块
    • ModBus读写
  • ModBusRTU
    • 添加模块

ModbusTCP和ModBusRTU 的区别

Modbus RTU 和 Modbus TCP 是 Modbus 协议的两种不同实现方式,主要区别如下:

  1. 通信介质

    Modbus RTU:基于串行通信(如 RS-232 或 RS-485),使用二进制编码传输数据。

    Modbus TCP:基于以太网,使用 TCP/IP 协议,数据通过以太网传输。

  2. 数据编码

    Modbus RTU:采用二进制编码,数据紧凑,传输效率高。

    Modbus TCP:数据封装在 TCP/IP 数据包中,增加了 IP 地址和端口号等信息。

  3. 传输速度

    Modbus RTU:速度受限于串行通信,通常较慢。

    Modbus TCP:基于以太网,速度更快,适合高速数据传输。

  4. 传输距离

    Modbus RTU:RS-485 最大传输距离约 1200 米。

    Modbus TCP:理论上无距离限制,实际受网络设备影响。

  5. 网络拓扑

    Modbus RTU:通常为总线型拓扑,设备串联。

    Modbus TCP:支持星型、树型等多种拓扑,设备通过交换机或路由器连接。

  6. 协议开销

    Modbus RTU:协议开销小,数据帧简单。

    Modbus TCP:协议开销较大,增加了 TCP/IP 包头。

  7. 应用场景

    Modbus RTU:适用于工业控制、传感器网络等低速、短距离场景。

    Modbus TCP:适用于需要高速、远距离通信的工业自动化系统。

  8. 设备成本

    Modbus RTU:设备成本较低,适合预算有限的项目。

    Modbus TCP:设备成本较高,但性能更强。

ModbusTCP

添加模块

在这里插入图片描述

ModBus读写

#ifndef WIDGET_H // 如果没有定义 WIDGET_H
#define WIDGET_H // 定义 WIDGET_H,防止重复包含

#include <QWidget> // 包含QWidget类的定义,QWidget是所有UI对象的基础类
#include <QModbusTcpClient> // 包含QModbusTcpClient类的定义,用于实现Modbus TCP协议的客户端

QT_BEGIN_NAMESPACE // 开始Qt命名空间
namespace Ui { class Widget; } // 声明一个名为Widget的类在Ui命名空间中,通常由Qt Designer生成的UI类
QT_END_NAMESPACE // 结束Qt命名空间

class Widget : public QWidget // 定义Widget类,继承自QWidget
{
    Q_OBJECT // 必须的宏,用于启用信号和槽机制

public:
    Widget(QWidget *parent = nullptr); // 构造函数,允许创建无父级窗口部件的对象,默认为nullptr
    ~Widget(); // 析构函数

    QModbusTcpClient *modbusClient; // 指向QModbusTcpClient类型的指针,用于管理与Modbus TCP服务器的连接

private slots: // 私有槽,用于处理特定事件或响应信号
    void on_ReadBtn_clicked(); // 当ReadBtn按钮被点击时调用的槽函数
    void ReceiveData(); // 接收数据的槽函数
    void on_WriteBtn_clicked(); // 当WriteBtn按钮被点击时调用的槽函数

private: // 私有成员变量和函数
    Ui::Widget *ui; // 指向Ui::Widget实例的指针,该实例包含了通过Qt Designer设计的UI元素
    bool isconnect; // 标志位,表示是否已经连接到Modbus TCP服务器
    void ReadValue(); // 读取值的私有函数
};

#endif // WIDGET_H // 结束WIDGET_H的条件编译指令
#include "widget.h"
#include "ui_widget.h"
#include <QModbusTcpClient>
#include <QModbusDataUnit>
#include <QModbusReply>
#include <QMessageBox>
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    modbusClient = new QModbusTcpClient(this);
    modbusClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, 503); // 设置Modbus端口
    modbusClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "127.0.0.1"); // 设置Modbus服务器地址

    if (!modbusClient->connectDevice()) {
            qDebug() << "连接失败";
        }
}

Widget::~Widget()
{
    if(modbusClient->state() == QModbusTcpClient::ConnectedState)
    {
        modbusClient->disconnectDevice();
    }
    delete ui;
}

//写入数据
void Widget::on_WriteBtn_clicked()
{
    if(ui->tb_Line->text() == "")
    {
        QMessageBox::warning(this,"提示","请输入设定值!");
        return;
    }
    if(modbusClient->state() == QModbusDevice::ConnectedState)
    {
        QModbusDataUnit writeUnit(QModbusDataUnit::HoldingRegisters, 0, 1); // 写入一个寄存器
        writeUnit.setValue(0,ui->tb_Line->text().toUInt());
        if (auto *reply = modbusClient->sendWriteRequest(writeUnit, 1))
        {
          reply->deleteLater();
        }
    }
}

//读取数据
void Widget::on_ReadBtn_clicked()
{
    ReadValue();
}

//读取Modbus数据
void Widget::ReadValue()
{
    if(modbusClient->state() == QModbusDevice::ConnectedState)
    {
        QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 0, 1); // 读取一个寄存器
        if (auto *reply = modbusClient->sendReadRequest(readUnit, 1))
        {
           if (!reply->isFinished())
           {
           connect(reply,QModbusReply::finished,this,ReceiveData);
           return;
           }
          reply->deleteLater();
        }
        else {
                qDebug()<< "提前退出";
            }
    }
}

void Widget::ReceiveData()
{
  QModbusReply* reply = (QModbusReply*)(sender());
  QModbusDataUnit unit = reply->result();
  reply->deleteLater();
  if(unit.valueCount() > 0)
  {
      ui->lb_templbtemp->setText(QString::number(unit.value(0)));
  }
}

ModBusRTU

添加模块

在这里插入图片描述

#ifndef WIDGET_H // 如果没有定义WIDGET_H
#define WIDGET_H // 定义WIDGET_H,防止重复包含

#include <QWidget> // 包含QWidget类,所有用户界面对象的基础类
#include <QModbusRtuSerialMaster> // 包含QModbusRtuSerialMaster类,用于实现Modbus RTU协议主站功能
#include <QModbusDataUnit> // 包含QModbusDataUnit类,用于表示Modbus数据单元

namespace Ui { // 定义命名空间Ui
class Widget; // 声明一个名为Widget的类,在此命名空间中,通常由Qt Designer生成的UI类
}

class Widget : public QWidget // 定义Widget类,继承自QWidget
{
    Q_OBJECT // 必须的宏,用于启用信号和槽机制

public:
    explicit Widget(QWidget *parent = 0); // 构造函数,explicit防止隐式转换,默认父级窗口部件为nullptr
    ~Widget(); // 析构函数

    QModbusRtuSerialMaster *modbusMaster; // 指向QModbusRtuSerialMaster类型的指针,用于管理与Modbus RTU从站设备的通信

private slots: // 私有槽,处理特定事件或响应信号
    void on_pushButton_clicked(); // 当pushButton按钮被点击时调用的槽函数
    void on_pushButton_2_clicked(); // 当pushButton_2按钮被点击时调用的槽函数

private:
    Ui::Widget *ui; // 指向Ui::Widget实例的指针,该实例包含了通过Qt Designer设计的UI元素
    void IniModbus(); // 初始化Modbus主站配置的私有成员函数
};

#endif // WIDGET_H // 结束WIDGET_H的条件编译指令
#include "widget.h"
#include "ui_widget.h"
#include "QDebug"
#include <QSerialPort>

// Widget类构造函数,初始化成员变量和UI组件
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this); // 设置UI
    QWidget::setWindowTitle("ModbusRTU Demo"); // 设置窗口标题
    IniModbus(); // 初始化Modbus主站配置
}

// Widget类析构函数,释放资源
Widget::~Widget()
{
    delete ui; // 删除UI对象
}

// 初始化Modbus RTU主站配置
void Widget::IniModbus()
{
    modbusMaster = new QModbusRtuSerialMaster(this); // 创建一个新的QModbusRtuSerialMaster实例

    // 设置连接参数
    modbusMaster->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1");
    modbusMaster->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::EvenParity);
    modbusMaster->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud9600);
    modbusMaster->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
    modbusMaster->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);

    // 尝试连接设备
    if(modbusMaster->connectDevice())
        qDebug()<<"连接成功";
    else
        qDebug()<<"连接失败";
}

// 当pushButton按钮被点击时调用此槽函数
void Widget::on_pushButton_clicked()
{
    // 创建读取单元
    QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 0, 1);
    if (auto *reply = modbusMaster->sendReadRequest(readUnit, 1)) { // 发送读请求

        // 等待回复完成
        while (!reply->isFinished()) {
            QCoreApplication::processEvents(); // 处理事件循环
        }

        // 检查错误
        if (reply->error() == QModbusDevice::NoError) {

            const QModbusDataUnit resultUnit = reply->result();
            QString result = "";
            for (int i = 0; i < resultUnit.valueCount(); ++i) {
                result += QString::number(resultUnit.value(i)) + " "; // 构造结果字符串
            }
            ui->label->setText(result); // 显示结果
        } else {
            qDebug()<<reply->errorString(); // 输出错误信息
        }

        reply->deleteLater(); // 安全删除回复对象
    } else {
        qDebug()<<modbusMaster->errorString(); // 如果请求失败,输出错误信息
    }
}

// 当pushButton_2按钮被点击时调用此槽函数
void Widget::on_pushButton_2_clicked()
{
    // 创建写入单元
    QModbusDataUnit writeUnit(QModbusDataUnit::HoldingRegisters, 0, 1);
    qint32 value = ui->lineEdit->text().toInt(); // 获取输入框中的值
    writeUnit.setValue(0, value); // 设置写入值

    if (auto *reply = modbusMaster->sendWriteRequest(writeUnit, 1)) { // 发送写请求

        // 等待回复完成
        while (!reply->isFinished()) {
            QCoreApplication::processEvents(); // 处理事件循环
        }

        // 检查错误
        if (reply->error() == QModbusDevice::NoError) {
            qDebug()<< "写入成功"; // 输出成功信息
        } else {
            qDebug()<< "写入失败" + reply->errorString(); // 输出错误信息
        }

        reply->deleteLater(); // 安全删除回复对象
    } else {
        qDebug()<< "写入失败" + modbusMaster->errorString(); // 如果请求失败,输出错误信息
    }
}

相关文章:

  • 力扣hot100——无重复字符最长子串
  • 自制AirTag,支持安卓/鸿蒙/PC/Home Assistant,无需拥有iPhone
  • 解决 WSL Ubuntu 中 /etc/resolv.conf 自动重置问题
  • Redis如何解决热Key问题
  • AcWing 1236. 递增三元组(蓝桥杯C++ AB辅导课)
  • C语言流程控制学习笔记
  • 特力康输电线路杆塔倾斜智能监测装置:创新技术如何提升电网安全
  • 《千恋万花》无广版手游安卓苹果免费下载直装版
  • React之旅-02 创建项目
  • python: SQLAlchemy (ORM) Simple example using SQLite
  • 2023年河北省职业院校技能大赛网络系统管理赛项样题解法
  • 汽车零部件工厂如何通过工业一体机实现精准控制
  • “深入浅出”系列之C++:(8)libevent 库
  • 机器学习小项目之加利福尼亚房价数据分析
  • 【Spring详解三】默认标签的解析
  • BuildFarm Worker 简要分析
  • Git命令行入门
  • 【数据分析】通过个体和遗址层面的遗传相关性网络分析
  • 前端面试题-异步任务队列控制
  • 叠焊和平面焊
  • 广西壮族自治区党委政法委副书记李文博接受审查调查
  • 上海国际电影节特设“今日亚洲”单元
  • 全国层面首次!《防震减灾基本知识与技能大纲》发布
  • 湛江霞山通报渔船火灾:起火船舶共8艘,无人员伤亡或被困
  • 民生谣言误导认知,多方联动守护清朗——中国互联网联合辟谣平台2025年4月辟谣榜综述
  • IPO周报|本周A股暂无新股网上申购,年内最低价股周二上市