Qt的Modbus协议-RTU从站实现
- 1、Modbus协议
- 1.1 modbusRTU 协议格式
- 1.2 读寄存器功能码0x03
- 2、Qt实现Modbus RTU协议
- 2.1 添加相关的库
- 2.2 添加相关的头文件
- 2.3 设置串口参数
- 2.4 一秒读取一次从机数据
- 2.5 处理读取数据
- 2.6 大小端排序
- 3、文件
-
- 4、总结
1、Modbus协议
Modbus协议是一种工业自动化领域广泛应用的串行通信协议,由Modicon公司(现施耐德电气)于1979年推出,现已成为工业设备通信的业界标准13。 |
1.1 modbusRTU 协议格式
设备地址:设备的通讯地址、站号。
功能码:对数据帧的功能编号。
寄存器:存放某类数据的内存区域。一个设备可能有多种寄存器,不同的寄存器存放不同类别的数据。
寄存器地址:某个数据在寄存器里的编号。不同的设备定义不同。 |
1.2 读寄存器功能码0x03

这个意思是设备地址为04,功能码是03,,寄存器起始地址为 0x80,查询数据分别为 0x1234和 0x5678; |
2、Qt实现Modbus RTU协议
2.1 添加相关的库
QT += core gui serialport serialbus

2.2 添加相关的头文件
#include <QSerialPort>
#include <QtSerialBus>
#include <QModbusDataUnit>
#include <QModbusClient>

2.3 设置串口参数
void mainInterface::setupModbusConnectxion()
{modbusDevice = new QModbusRtuSerialMaster(this);modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1"); modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud115200); modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8); modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop); modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity); connect(modbusDevice, &QModbusDevice::errorOccurred, [](QModbusDevice::Error error) {qDebug() << "Modbus Error:" << error; });if (!modbusDevice->connectDevice()) {qDebug() << "无法连接"; } else {qDebug() << "成功连接"; }
}
2.4 一秒读取一次从机数据
dataTimer = new QTimer(this);connect(dataTimer, &QTimer::timeout, this, &mainInterface::readFlowData);dataTimer->start(1000);
QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 4104, 2);if (auto *reply = modbusDevice->sendReadRequest(readUnit, 4)) {if (!reply->isFinished()) {connect(reply, &QModbusReply::finished, this, &mainInterface::handleReadResult);} else {delete reply;}} else {qDebug() << "读取错误: " << modbusDevice->errorString();}
}
2.5 处理读取数据
void mainInterface::handleReadResult()
{auto reply = qobject_cast<QModbusReply *>(sender());if (!reply){return; }if (reply->error() == QModbusDevice::NoError) {const QModbusDataUnit unit = reply->result();QString dataStr;float flowValuee = 0;flowValuee = convertToFloat(unit.value(0), unit.value(1), 0);ui->flowlabel->setText(QString::number(flowValuee, 'f', 1));}else {qDebug() << "读取数据错误" << reply->errorString();}reply->deleteLater();
}
2.6 大小端排序
float mainInterface::convertToFloat(quint16 reg1, quint16 reg2, bool isBigEndian)
{union {quint32 i; float f; } converter;if (isBigEndian) {converter.i = (reg1 << 16) | reg2;} else {converter.i = (reg2 << 16) | reg1;}return converter.f;
}
3、文件
3,1 头文件
#ifndef MAININTERFACE_H
#define MAININTERFACE_H#include <QWidget>
#include <QDateTime>
#include <QTimer>
#include <QSerialPort>
#include <QtSerialBus>
#include <QModbusDataUnit>
#include <QModbusClient>
#include <QProcess>
#include <windows.h>
#include <QMessageBox>namespace Ui {
class mainInterface;
}class mainInterface : public QWidget
{Q_OBJECTpublic:explicit mainInterface(QWidget *parent = nullptr);~mainInterface();typedef union
{
float dataf;
uint32_t data32;
uint16_t data16[2];
uint8_t data8[4];
} data_t;private slots:void on_exitPushButton_clicked();void readFlowData();void updateDateTime();void handleReadResult();void on_keyboardPushButton_clicked();void on_historyPushButton_clicked();void on_setPushButton_clicked();void on_helpPushButton_clicked();private:Ui::mainInterface *ui;QTimer *dataTimer;QTimer *clockTimer;QModbusClient *modbusDevice;void setupModbusConnection();float parseFloatFromRegisters(const QVector<quint16> ®isters);quint16 swapBytes(quint16 value);QProcess *keyboardProcess;bool keyboardVisible;float convertToFloat(quint16 reg1, quint16 reg2, bool isBigEndian);};#endif
3.2 cpp文件
#include "maininterface.h"
#include "ui_maininterface.h"mainInterface::mainInterface(QWidget *parent) :QWidget(parent),ui(new Ui::mainInterface)
{ui->setupUi(this);keyboardVisible = false;this->setWindowFlag(Qt::FramelessWindowHint); dataTimer = new QTimer(this);connect(dataTimer, &QTimer::timeout, this, &mainInterface::readFlowData);dataTimer->start(1000); clockTimer = new QTimer(this);connect(clockTimer, &QTimer::timeout, this, &mainInterface::updateDateTime);clockTimer->start(1000); keyboardProcess = new QProcess(this);keyboardVisible = false;setupModbusConnection();updateDateTime();}mainInterface::~mainInterface()
{delete ui;
}
void mainInterface::on_exitPushButton_clicked()
{QMessageBox quitMes; quitMes.setWindowTitle("关闭界面"); quitMes.setWindowIcon(QIcon(":/widdgetMainInterface/exit.png")); quitMes.setIcon(QMessageBox::Warning); quitMes.setText("请确认是否退出"); quitMes.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); quitMes.setButtonText(QMessageBox::Ok, "确认"); quitMes.setButtonText(QMessageBox::Cancel,"取消"); int result = quitMes.exec(); if(result == QMessageBox::Ok){this->close(); }else {}this->close();
}
void mainInterface::setupModbusConnection()
{modbusDevice = new QModbusRtuSerialMaster(this);modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1");modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,QSerialPort::Baud115200);modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);connect(modbusDevice, &QModbusDevice::errorOccurred, [](QModbusDevice::Error error) {qDebug() << "Modbus Error:" << error;});if (!modbusDevice->connectDevice()) {qDebug() << "无法连接";} else {qDebug() << "成功连接";}
}
void mainInterface::readFlowData()
{if (!modbusDevice || modbusDevice->state() != QModbusDevice::ConnectedState){return;}QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 4104, 2);if (auto *reply = modbusDevice->sendReadRequest(readUnit, 4)) { if (!reply->isFinished()) {connect(reply, &QModbusReply::finished, this, &mainInterface::handleReadResult);} else {delete reply;}} else {qDebug() << "读取错误: " << modbusDevice->errorString();}
}
void mainInterface::updateDateTime()
{QDateTime currentTime = QDateTime::currentDateTime();QString ymdTimeStr = currentTime.toString("yyyy年MM月dd日");QString hmsTimeStr = currentTime.toString("hh:mm:ss");ui->currentTimeymdlabel->setText(ymdTimeStr);ui->currentTimehmslabel->setText(hmsTimeStr);}
float mainInterface::convertToFloat(quint16 reg1, quint16 reg2, bool isBigEndian)
{union {quint32 i;float f;} converter;if (isBigEndian) {converter.i = (reg1 << 16) | reg2;} else {converter.i = (reg2 << 16) | reg1;}return converter.f;
}
void mainInterface::handleReadResult()
{auto reply = qobject_cast<QModbusReply *>(sender());if (!reply){return;}if (reply->error() == QModbusDevice::NoError) {const QModbusDataUnit unit = reply->result();QString dataStr;float flowValuee = 0;flowValuee = convertToFloat(unit.value(0),unit.value(1),0);ui->flowlabel->setText(QString::number(flowValuee, 'f', 1));}else {qDebug() << "读取数据错误" << reply->errorString();}reply->deleteLater();}
quint16 mainInterface::swapBytes(quint16 value)
{return ((value << 8) & 0xFF00) | ((value >> 8) & 0x00FF);}
float mainInterface::parseFloatFromRegisters(const QVector<quint16> ®isters)
{quint16 low = swapBytes(registers[0]);quint16 high = swapBytes(registers[1]);quint32 combined = (qFromBigEndian(high) << 16) | qFromBigEndian(low);float result;memcpy(&result, &combined, sizeof(result));return result;}
void mainInterface::on_keyboardPushButton_clicked()
{if (!keyboardVisible) {bool started = false;keyboardProcess->start("osk.exe");if (keyboardProcess->waitForStarted(1500)) {keyboardVisible = true;started = true;qDebug() << "软键盘已启动 (QProcess)";}if (!started) {QString oskPath = "C:\\Windows\\System32\\osk.exe";keyboardProcess->start(oskPath);if (keyboardProcess->waitForStarted(1500)) {keyboardVisible = true;started = true;qDebug() << "软键盘已启动 (完整路径)";}}if (!started) {qDebug() << "尝试使用Windows API启动软键盘";PVOID oldValue;Wow64DisableWow64FsRedirection(&oldValue);HINSTANCE result = ShellExecuteW(0,L"open",L"osk.exe",0,0,SW_SHOWNORMAL);Wow64RevertWow64FsRedirection(oldValue);if (reinterpret_cast<int>(result) > 32) {keyboardVisible = true;started = true;qDebug() << "软键盘已启动 (Windows API)";} else {qDebug() << "Windows API启动失败, 错误码:" << reinterpret_cast<int>(result);}}if (!started) {QMessageBox::warning(this, "错误", "无法启动软键盘。请确保系统支持屏幕键盘(osk.exe)。");}else {if (keyboardProcess->state() == QProcess::Running) {keyboardProcess->terminate();if (keyboardProcess->waitForFinished(1000)) {keyboardVisible = false;qDebug() << "软键盘已关闭";} else {qDebug() << "无法关闭软键盘:" << keyboardProcess->errorString();}} else {keyboardVisible = false;}}}}
void mainInterface::on_historyPushButton_clicked()
{QMessageBox::information(this,"历史记录","查看历史记录");
}
void mainInterface::on_setPushButton_clicked()
{QMessageBox::information(this, "系统设置", "系统设置功能");
}
void mainInterface::on_helpPushButton_clicked()
{QMessageBox::information(this, "帮助", "流量监控系统帮助文档\n\n""1. 系统每秒自动更新流量数据\n""2. 点击键盘图标调用系统软键盘\n");
}
仓库地址
4、总结
以上就是Qt的Modbus协议-RTU从站实现的整个过程了,浏览过程中,如若发现错误,欢迎大家指正,有问题的欢迎评论区留言或者私信。最后,如果大家觉得有所帮助,可以点一下赞,谢谢大家!祝大家天天开心,顺遂无虞! |