00. 目录

文章目录

  • 00. 目录
  • 01. 概述
  • 02. 开发环境
  • 03. 写Coils程序示例
  • 04. 写HoldingRegisters程序示例
  • 05. 综合示例
  • 06. 程序下载
  • 07. 附录

01. 概述

Qt中几个常用的串口modbus类

QModbusRtuSerialSlave       //modbus串口通信方式下的服务器类
QModbusRtuSerialMaster      //串口通信方式下的客户端类
QModbusServer               // QModbusServer类接收和处理modbus的请求。
QModbusDataUnit             //存储接收和发送数据的类,数据类型为1bit和16bit
QModbusReply                //客户端访问服务器后得到的回复(如客户端读服务器数据时包含数据信息)

02. 开发环境

Windows系统:Windows10

Qt版本:Qt5.15或者Qt6

Pro配置文件如下

QT       += core gui serialbus serialportgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0SOURCES += \main.cpp \widget.cppHEADERS += \widget.h# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

03. 写Coils程序示例

widget.h文件

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>//前向声明
class QModbusClient;
class QModbusReply;class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:QModbusClient *modbusDevice = nullptr;private slots:void onReadReady();
};
#endif // WIDGET_H

widget.cpp文件

#include "widget.h"
#include <QModbusRtuSerialMaster>
#include <QModbusDataUnit>
#include <QModbusReply>
#include <QVariant>
#include <QSerialPort>
#include <QDebug>//构造函数
Widget::Widget(QWidget *parent): QWidget(parent)
{//1. 创建QModbusDevice对象modbusDevice = new QModbusRtuSerialMaster;//2. 如果处于连接状态,则断开连接if (modbusDevice->state() == QModbusDevice::ConnectedState){//断开连接设备modbusDevice->disconnectDevice();}//3. 设置串口相关参数//设置串口信息modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant("COM3"));//设置校验 无校验modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);//设置波特率modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);//设置停止位modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);//设置数据位modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);//4. 设置其他信息//设置超时时间modbusDevice->setTimeout(1000); //1秒//设置失败重试次数modbusDevice->setNumberOfRetries(3);//5. 连接到设备bool ok = modbusDevice->connectDevice();if (!ok){qDebug() << "连接到串口失败: " << modbusDevice->errorString();}else{qDebug() << "连接到串口成功";}//6. 发送写请求//从地址0开始写10个保持寄存器的值//QModbusDataUnit writeData(QModbusDataUnit::HoldingRegisters, 0, 10);//从地址0开始写10个线圈的值QModbusDataUnit writeData(QModbusDataUnit::Coils, 0, 10);for (int i = 0; i < writeData.valueCount(); i++){writeData.setValue(i, (i * i) % 2);}qDebug() << "发送的数据为: " << writeData.values();QModbusReply* reply = modbusDevice->sendWriteRequest(writeData, 1);if (reply){if (!reply->isFinished()){//接收响应信息connect(reply, &QModbusReply::finished, this, [this, reply](){if (reply->error() == QModbusDevice::ProtocolError){//接收到的响应信息是协议错误qDebug() << "写入数据错误:" << reply->errorString();}else if (reply->error() != QModbusDevice::NoError){//接收到的响应消息是其它错误qDebug() << "写入数据错误: " << reply->errorString();}else{//接收到的消息没有错误 一般没有必要解析响应消息const QModbusDataUnit data = reply->result();qDebug() << "消息数据个数:" << data.valueCount() << " :" << data.values();}reply->deleteLater();});}else{//发送没有响应数据//broadcast replies return immediatelyreply->deleteLater();}}else{qDebug() << "sendWriteRequest Error: " << reply->errorString();}//7. 发送读取数据请求//从地址0开始读取10个保持寄存器的值//QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, 0, 10);//从地址0开始读取10个离散输入量的值//QModbusDataUnit data(QModbusDataUnit::DiscreteInputs, 0, 10);//QModbusDataUnit::Coils 从地址0开始读取10个线圈值QModbusDataUnit data(QModbusDataUnit::Coils, 0, 10);//QModbusDataUnit::InputRegisters 从地址0开始读取10个输入寄存器的值//QModbusDataUnit data(QModbusDataUnit::InputRegisters, 0, 10);reply = modbusDevice->sendReadRequest(data, 0x1);if (nullptr == reply){qDebug() << "发送请求数据失败: " << modbusDevice->errorString();}else{if (!reply->isFinished()){connect(reply, &QModbusReply::finished, this, &Widget::onReadReady);}else{//broadcast replies return immediatelydelete reply;}}}//析构函数
Widget::~Widget()
{if (modbusDevice){modbusDevice->disconnectDevice();}delete modbusDevice;
}//准备读取数据的槽函数
void Widget::onReadReady()
{auto reply = qobject_cast<QModbusReply*>(sender());if (nullptr == reply){return;}//判断是否出错if (reply->error() == QModbusDevice::NoError){//读取响应数据const QModbusDataUnit responseData = reply->result();qDebug() << "读到数据为:" << responseData.values();}else if (reply->error() == QModbusDevice::ProtocolError){qDebug() << "Read response Protocol error: " << reply->errorString();}else{qDebug() << "Read response Error: " << reply->errorString();}//删除replyreply->deleteLater();
}

执行结果

20:32:27: Starting D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe ...
连接到串口成功
发送的数据为:  QVector(0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
消息数据个数: 10  : QVector(0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
读到数据为: QVector(0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
20:34:02: D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe exited with code 0

04. 写HoldingRegisters程序示例

widget.h文件

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>//前向声明
class QModbusClient;
class QModbusReply;class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:QModbusClient *modbusDevice = nullptr;private slots:void onReadReady();
};
#endif // WIDGET_H

widget.cpp文件

#include "widget.h"
#include <QModbusRtuSerialMaster>
#include <QModbusDataUnit>
#include <QModbusReply>
#include <QVariant>
#include <QSerialPort>
#include <QDebug>//构造函数
Widget::Widget(QWidget *parent): QWidget(parent)
{//1. 创建QModbusDevice对象modbusDevice = new QModbusRtuSerialMaster;//2. 如果处于连接状态,则断开连接if (modbusDevice->state() == QModbusDevice::ConnectedState){//断开连接设备modbusDevice->disconnectDevice();}//3. 设置串口相关参数//设置串口信息modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant("COM3"));//设置校验 无校验modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);//设置波特率modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);//设置停止位modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);//设置数据位modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);//4. 设置其他信息//设置超时时间modbusDevice->setTimeout(1000); //1秒//设置失败重试次数modbusDevice->setNumberOfRetries(3);//5. 连接到设备bool ok = modbusDevice->connectDevice();if (!ok){qDebug() << "连接到串口失败: " << modbusDevice->errorString();}else{qDebug() << "连接到串口成功";}//6. 发送写请求//从地址0开始写10个保持寄存器的值QModbusDataUnit writeData(QModbusDataUnit::HoldingRegisters, 0, 10);for (int i = 0; i < writeData.valueCount(); i++){writeData.setValue(i, i * i);}qDebug() << "发送的数据为: " << writeData.values();QModbusReply* reply = modbusDevice->sendWriteRequest(writeData, 1);if (reply){if (!reply->isFinished()){//接收响应信息connect(reply, &QModbusReply::finished, this, [this, reply](){if (reply->error() == QModbusDevice::ProtocolError){//接收到的响应信息是协议错误qDebug() << "写入数据错误:" << reply->errorString();}else if (reply->error() != QModbusDevice::NoError){//接收到的响应消息是其它错误qDebug() << "写入数据错误: " << reply->errorString();}else{//接收到的消息没有错误 一般没有必要解析响应消息const QModbusDataUnit data = reply->result();qDebug() << "消息数据个数:" << data.valueCount() << " :" << data.values();}reply->deleteLater();});}else{//发送没有响应数据//broadcast replies return immediatelyreply->deleteLater();}}else{qDebug() << "sendWriteRequest Error: " << reply->errorString();}//7. 发送读取数据请求//从地址0开始读取10个保持寄存器的值QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, 0, 10);//从地址0开始读取10个离散输入量的值//QModbusDataUnit data(QModbusDataUnit::DiscreteInputs, 0, 10);//QModbusDataUnit::Coils 从地址0开始读取10个线圈值//QModbusDataUnit data(QModbusDataUnit::Coils, 0, 10);//QModbusDataUnit::InputRegisters 从地址0开始读取10个输入寄存器的值//QModbusDataUnit data(QModbusDataUnit::InputRegisters, 0, 10);reply = modbusDevice->sendReadRequest(data, 0x1);if (nullptr == reply){qDebug() << "发送请求数据失败: " << modbusDevice->errorString();}else{if (!reply->isFinished()){connect(reply, &QModbusReply::finished, this, &Widget::onReadReady);}else{//broadcast replies return immediatelydelete reply;}}}//析构函数
Widget::~Widget()
{if (modbusDevice){modbusDevice->disconnectDevice();}delete modbusDevice;
}//准备读取数据的槽函数
void Widget::onReadReady()
{auto reply = qobject_cast<QModbusReply*>(sender());if (nullptr == reply){return;}//判断是否出错if (reply->error() == QModbusDevice::NoError){//读取响应数据const QModbusDataUnit responseData = reply->result();qDebug() << "读到数据为:" << responseData.values();}else if (reply->error() == QModbusDevice::ProtocolError){qDebug() << "Read response Protocol error: " << reply->errorString();}else{qDebug() << "Read response Error: " << reply->errorString();}//删除replyreply->deleteLater();
}

执行结果

20:23:23: Starting D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe ...
连接到串口成功
发送的数据为:  QVector(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
消息数据个数: 10  : QVector(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
读到数据为: QVector(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
20:23:29: D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe exited with code 0

05. 综合示例

程序界面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J2Oiv0Co-1621479015968)(assets/image-20210520104421393.png)]

settingdialog.h文件

#ifndef SETTINGDIALOG_H
#define SETTINGDIALOG_H#include <QDialog>
#include <QtSerialPort>namespace Ui {class SettingDialog;
}//串口设置相关类
class SettingDialog : public QDialog
{Q_OBJECTpublic:struct Settings{//串口名QString serialName = "COM3";//校验位int parity = QSerialPort::NoParity;//波特率int baud = QSerialPort::Baud19200;//数据位int dataBits = QSerialPort::Data8;//停止位int stopBits = QSerialPort::OneStop;//响应时间int responseTime = 1000;//重试次数int numberOfRetries = 3;};explicit SettingDialog(QWidget *parent = nullptr);~SettingDialog();//返回参数设置信息Settings  settings() const;private slots:void on_btnApply_clicked();private:Ui::SettingDialog *ui;Settings m_settings;
};#endif // SETTINGDIALOG_H

settingdialog.cpp文件

#include "settingdialog.h"
#include "ui_settingdialog.h"//构造函数
SettingDialog::SettingDialog(QWidget *parent) :QDialog(parent),ui(new Ui::SettingDialog)
{ui->setupUi(this);//设置默认参数信息ui->serialNameLineEdit->setText(tr("COM3"));ui->parityComboBox->setCurrentIndex(0);ui->baudComboBox->setCurrentText(QString::number(m_settings.baud));ui->dataBitComboBox->setCurrentText(QString::number(m_settings.dataBits));ui->stopBitComboBox->setCurrentText(QString::number(m_settings.stopBits));ui->spinBoxTimeOut->setValue(m_settings.responseTime);ui->spinBoxRetry->setValue(m_settings.numberOfRetries);}//析构函数
SettingDialog::~SettingDialog()
{delete ui;
}//返回参数信息
SettingDialog::Settings SettingDialog::settings() const
{return m_settings;
}//引用按钮槽函数
void SettingDialog::on_btnApply_clicked()
{m_settings.serialName = ui->serialNameLineEdit->text();m_settings.parity = ui->parityComboBox->currentText().toInt();m_settings.baud = ui->baudComboBox->currentText().toInt();m_settings.dataBits = ui->dataBitComboBox->currentText().toInt();m_settings.stopBits = ui->stopBitComboBox->currentText().toInt();m_settings.responseTime = ui->spinBoxTimeOut->value();m_settings.numberOfRetries = ui->spinBoxRetry->value();//隐藏参数设置对话框hide();
}

mainwindow.h文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QModbusDataUnit>
#include "writeregistermodel.h"QT_BEGIN_NAMESPACEnamespace Ui
{class MainWindow;
}class SettingDialog;
class QModbusClient;
class QModbusReply;QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();//信号与槽进行关联void initActions();//读请求数据包封装QModbusDataUnit readRequest() const;//写请求数据包封装QModbusDataUnit writeRequest() const;private slots:void onConnectButtonClicked();void onConnectTypeChanged(int);void onModbusStateChanged(int state);void onReadButtonClicked();void onReadReady();void onWriteButtonClicked();void onReadWriteButtonClicked();void onWriteTableChanged(int);private:Ui::MainWindow *ui = nullptr;SettingDialog *m_settingDialog = nullptr;QModbusClient *modbusDevice = nullptr;QModbusReply *reply = nullptr;WriteRegisterModel *writeModel = nullptr;};
#endif // MAINWINDOW_H

mainwindow.cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include "settingdialog.h"
#include <QModbusRtuSerialMaster>
#include <QModbusReply>
#include <QStandardItemModel>
#include <QModbusDataUnit>//连接类型枚举变量
enum ModbusConnection {Serial,Tcp
};MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//创建对象m_settingDialog = new SettingDialog(this);//初始化信号与槽initActions();//创建写模型writeModel = new WriteRegisterModel(this);writeModel->setStartAddress(ui->sbWriteStartAddr->value());writeModel->setNumberOfValues(ui->cbWriteCount->currentText());//MVCui->treeViewWrite->setModel(writeModel);//隐藏第二列ui->treeViewWrite->hideColumn(2);connect(writeModel, &WriteRegisterModel::updateViewport,ui->treeViewWrite->viewport(), QOverload<>::of(&QWidget::update));//默认为串口连接方式ui->cbConnType->setCurrentIndex(0);onConnectTypeChanged(0);auto model = new QStandardItemModel(10, 1, this);for (int i = 0; i < 10; i++){model->setItem(i, new QStandardItem(QStringLiteral("%1").arg(i + 1)));}ui->cbWriteCount->setModel(model);ui->cbWriteCount->setCurrentText("10");connect(ui->cbWriteCount, &QComboBox::currentTextChanged,writeModel, &WriteRegisterModel::setNumberOfValues);auto valueChanged = QOverload<int>::of(&QSpinBox::valueChanged);connect(ui->sbWriteStartAddr, valueChanged, writeModel, &WriteRegisterModel::setStartAddress);connect(ui->sbWriteStartAddr, valueChanged, this, [this, model](int i){int lastIndex = 0;const int curIndex = ui->cbWriteCount->currentIndex();for (int j = 0; j < 10; j++){//设置使能if (j < (10 - i)){lastIndex = j;model->item(j)->setEnabled(true);}else{//设置禁用model->item(j)->setEnabled(false);}}if (curIndex > lastIndex){ui->cbWriteCount->setCurrentIndex(lastIndex);}});
}//析构函数
MainWindow::~MainWindow()
{if (modbusDevice){modbusDevice->disconnectDevice();delete modbusDevice;}delete ui;
}//信号与槽进行关联s
void MainWindow::initActions()
{//使能部分功能ui->actionConnect->setEnabled(true);ui->actionDisconnect->setEnabled(false);ui->actionQuit->setEnabled(true);ui->actionOption->setEnabled(true);//禁用读写操作ui->btnRead->setEnabled(false);ui->btnWrite->setEnabled(false);ui->btnReadWrite->setEnabled(false);//信号与槽关联connect(ui->btnConnect, &QPushButton::clicked,this, &MainWindow::onConnectButtonClicked);connect(ui->actionConnect, &QAction::triggered,this, &MainWindow::onConnectButtonClicked);connect(ui->actionDisconnect, &QAction::triggered,this, &MainWindow::onConnectButtonClicked);//读操作按钮槽函数关联connect(ui->btnRead, &QPushButton::clicked,this, &MainWindow::onReadButtonClicked);connect(ui->btnWrite, &QPushButton::clicked,this, &MainWindow::onWriteButtonClicked);connect(ui->btnReadWrite, &QPushButton::clicked,this, &MainWindow::onReadWriteButtonClicked);connect(ui->cbRegisterType, QOverload<int>::of(&QComboBox::currentIndexChanged),this, &MainWindow::onWriteTableChanged);connect(ui->cbConnType, QOverload<int>::of(&QComboBox::currentIndexChanged),this, &MainWindow::onConnectTypeChanged);//退出菜单connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::close);//显示参数设置对话框connect(ui->actionOption, &QAction::triggered, m_settingDialog, &QDialog::show);connect(ui->actionAbout, &QAction::triggered, [=]() {QMessageBox::aboutQt(this, "About Qt");});
}//构建请求报文
QModbusDataUnit MainWindow::readRequest() const
{//const auto type = static_cast<QModbusDataUnit::RegisterType>(ui->cbRegisterType->currentData().toInt());QModbusDataUnit::RegisterType type = QModbusDataUnit::Invalid;qDebug() << ui->cbRegisterType->currentText();if (ui->cbRegisterType->currentText() == QString("线圈")){type = QModbusDataUnit::Coils;}else if (ui->cbRegisterType->currentText() == QString("离散输入")){type = QModbusDataUnit::DiscreteInputs;}else if (ui->cbRegisterType->currentText() == QString("输入寄存器")){type = QModbusDataUnit::InputRegisters;}else if (ui->cbRegisterType->currentText() == QString("保持寄存器")){type = QModbusDataUnit::HoldingRegisters;}qDebug() << "请求报文类型: " << type;//获取int startAddress = ui->spReadStartAddr->value();Q_ASSERT(startAddress >= 0 && startAddress < 10);quint16 numberOfEntries = ui->cbReadCount->currentText().toUShort();return QModbusDataUnit(type, startAddress, numberOfEntries);
}//写请求数据包封装
QModbusDataUnit MainWindow::writeRequest() const
{QModbusDataUnit::RegisterType type = QModbusDataUnit::Invalid;qDebug() << ui->cbRegisterType->currentText();if (ui->cbRegisterType->currentText() == QString("线圈")){type = QModbusDataUnit::Coils;}else if (ui->cbRegisterType->currentText() == QString("离散输入")){type = QModbusDataUnit::DiscreteInputs;}else if (ui->cbRegisterType->currentText() == QString("输入寄存器")){type = QModbusDataUnit::InputRegisters;}else if (ui->cbRegisterType->currentText() == QString("保持寄存器")){type = QModbusDataUnit::HoldingRegisters;}qDebug() << "请求报文类型: " << type;//获取int startAddress = ui->sbWriteStartAddr->value();Q_ASSERT(startAddress >= 0 && startAddress < 10);quint16 numberOfEntries = ui->cbWriteCount->currentText().toUShort();//qDebug() << "Test: " << startAddress << " " << numberOfEntries;return QModbusDataUnit(type, startAddress, numberOfEntries);
}//连接和断开连接的槽函数
void MainWindow::onConnectButtonClicked()
{if (!modbusDevice){return;}//清空状态栏消息statusBar()->clearMessage();if (modbusDevice->state() != QModbusDevice::ConnectedState){auto type = static_cast<ModbusConnection>(ui->cbConnType->currentIndex());if (type == Serial){//设置串口连接信息modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,m_settingDialog->settings().serialName);modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,m_settingDialog->settings().parity);modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,m_settingDialog->settings().baud);modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,m_settingDialog->settings().dataBits);modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,m_settingDialog->settings().stopBits);}else{//TCP连接信息}modbusDevice->setTimeout(m_settingDialog->settings().responseTime);modbusDevice->setNumberOfRetries(m_settingDialog->settings().numberOfRetries);if (!modbusDevice->connectDevice()){statusBar()->showMessage(tr("Connect failed..") + modbusDevice->errorString(), 5000);}else{statusBar()->showMessage(tr("Connect Successfully"), 5000);qDebug() << "连接OK";ui->actionConnect->setEnabled(false);ui->actionDisconnect->setEnabled(true);//使能读写操作ui->btnRead->setEnabled(true);ui->btnWrite->setEnabled(true);ui->btnReadWrite->setEnabled(true);}}else{//断开连接modbusDevice->disconnectDevice();ui->actionConnect->setEnabled(true);ui->actionDisconnect->setDisabled(true);qDebug() << "断开连接成功";//禁用读写操作ui->btnRead->setEnabled(false);ui->btnWrite->setEnabled(false);ui->btnReadWrite->setEnabled(false);}
}//连接类型槽函数 TCP Serial
void MainWindow::onConnectTypeChanged(int index)
{//如果之前存在连接,则断开连接,然后释放内存if(modbusDevice){modbusDevice->disconnectDevice();delete modbusDevice;modbusDevice = nullptr;}auto type = static_cast<ModbusConnection>(index);if (type == Serial){modbusDevice = new QModbusRtuSerialMaster(this);qDebug() << "new QModbusRtuSerialMaster Ok";statusBar()->showMessage("new QModbusRtuSerialMaster Ok", 3000);}else if (type == Tcp){}else{statusBar()->showMessage("连接类型非法", 5000);}connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error){statusBar()->showMessage(modbusDevice->errorString(), 5000);});if (!modbusDevice){//分配空间失败ui->btnConnect->setDisabled(true);if (type == Serial){statusBar()->showMessage(tr("创建Modbus Master失败"), 5000);}else{statusBar()->showMessage(tr("创建Modbus Client失败"), 5000);}}else{connect(modbusDevice, &QModbusClient::stateChanged,this, &MainWindow::onModbusStateChanged);}}//Modbus状态改变槽函数
void MainWindow::onModbusStateChanged(int state)
{//判断Modbus设备连接是否处于连接状态bool connected = (state != QModbusDevice::UnconnectedState);ui->actionConnect->setEnabled(!connected);ui->actionDisconnect->setEnabled(connected);if (QModbusDevice::UnconnectedState == state){ui->btnConnect->setText(tr("Connect"));}else{ui->btnConnect->setText(tr("Disconnect"));}
}//读操作槽函数
void MainWindow::onReadButtonClicked()
{if (!modbusDevice){return;}ui->textEditRead->clear();statusBar()->clearMessage();//发送请求报文数据auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->sbServerAddr->value());if (reply){if (!reply->isFinished()){//完毕之后 自动触发槽函数connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);}else{//广播消息  不需要返回响应delete reply;}}else{statusBar()->showMessage(tr("Read Error: ") + modbusDevice->errorString(), 5000);}}//读取数据
void MainWindow::onReadReady()
{auto reply = qobject_cast<QModbusReply*>(sender());if (!reply){return;}if (reply->error() == QModbusDevice::NoError){const QModbusDataUnit data = reply->result();for (int i = 0, total = (int)data.valueCount(); i < total; i++){const QString str = tr("Address: %1 Value: %2").arg(data.startAddress() + i).arg(QString::number(data.value(i), data.registerType() <= QModbusDataUnit::Coils ? 10 : 16));ui->textEditRead->append(str);}}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();
}void MainWindow::onWriteButtonClicked()
{if (!modbusDevice){return;}statusBar()->clearMessage();QModbusDataUnit writeData = writeRequest();QModbusDataUnit::RegisterType type = writeData.registerType();//qDebug() << "test: " << writeData.valueCount();for (int i = 0, total = (int)(writeData.valueCount()); i < total; i++){if (type == QModbusDataUnit::Coils){writeData.setValue(i, writeModel->m_coils[i + writeData.startAddress()]);}else{//qDebug() << "test: " << writeModel->m_holdingRegisters[i + writeData.startAddress()];writeData.setValue(i, writeModel->m_holdingRegisters[i + writeData.startAddress()]);}}qDebug() << "写数据内容为:" << writeData.values();//发送请求报文数据auto *reply = modbusDevice->sendWriteRequest(writeData, ui->sbServerAddr->value());if (reply){if (!reply->isFinished()){//完毕之后 自动触发槽函数connect(reply, &QModbusReply::finished, this, [this, reply]{if (reply->error() == QModbusDevice::ProtocolError){statusBar()->showMessage(tr("Write Protocaol response error: %1").arg(reply->errorString()), 5000);}else if (reply->error() != QModbusDevice::NoError){statusBar()->showMessage(tr("Write response error: %1").arg(reply->errorString()), 5000);}else{qDebug() << "写响应的数据: " << reply->result().values();}reply->deleteLater();});}else{//广播消息  不需要返回响应reply->deleteLater();}}else{statusBar()->showMessage(tr("Write Error: ") + modbusDevice->errorString(), 5000);}
}//读写按钮槽函数
void MainWindow::onReadWriteButtonClicked()
{if (!modbusDevice){return;}statusBar()->clearMessage();QModbusDataUnit writeData = writeRequest();QModbusDataUnit::RegisterType type = writeData.registerType();//qDebug() << "test: " << writeData.valueCount();for (int i = 0, total = (int)(writeData.valueCount()); i < total; i++){if (type == QModbusDataUnit::Coils){writeData.setValue(i, writeModel->m_coils[i + writeData.startAddress()]);}else{//qDebug() << "test: " << writeModel->m_holdingRegisters[i + writeData.startAddress()];writeData.setValue(i, writeModel->m_holdingRegisters[i + writeData.startAddress()]);}}qDebug() << "写数据内容为:" << writeData.values();//发送请求报文数据auto *reply = modbusDevice->sendReadWriteRequest(readRequest(), writeData, ui->sbServerAddr->value());if (reply){if (!reply->isFinished()){connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);//完毕之后 自动触发槽函数connect(reply, &QModbusReply::finished, this, [this, reply]{if (reply->error() == QModbusDevice::ProtocolError){statusBar()->showMessage(tr("Write Protocaol response error: %1").arg(reply->errorString()), 5000);}else if (reply->error() != QModbusDevice::NoError){statusBar()->showMessage(tr("Write response error: %1").arg(reply->errorString()), 5000);}else{qDebug() << "写响应的数据: " << reply->result().values();}reply->deleteLater();});}else{//广播消息  不需要返回响应reply->deleteLater();}}else{statusBar()->showMessage(tr("Write Error: ") + modbusDevice->errorString(), 5000);}
}void MainWindow::onWriteTableChanged(int index)
{const bool coilsOrHolding = index == 0 || index == 3;if (coilsOrHolding){ui->treeViewWrite->setColumnHidden(1, index != 0);ui->treeViewWrite->setColumnHidden(2, index != 3);ui->treeViewWrite->resizeColumnToContents(0);}ui->btnReadWrite->setEnabled(index == 3);ui->btnWrite->setEnabled(coilsOrHolding);ui->groupBox_2->setEnabled(coilsOrHolding);
}

writeregistermodel.h文件


#ifndef WRITEREGISTERMODEL_H
#define WRITEREGISTERMODEL_H#include <QAbstractItemModel>
#include <QBitArray>
#include <QObject>class WriteRegisterModel : public QAbstractTableModel
{Q_OBJECTpublic:WriteRegisterModel(QObject *parent = nullptr);int rowCount(const QModelIndex &parent = QModelIndex()) const override;int columnCount(const QModelIndex &parent = QModelIndex()) const override;QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;QVariant headerData(int section, Qt::Orientation orientation, int role) const override;bool setData(const QModelIndex &index, const QVariant &value, int role) override;Qt::ItemFlags flags(const QModelIndex &index) const override;public slots:void setStartAddress(int address);void setNumberOfValues(const QString &number);signals:void updateViewport();public:int m_number = 0;int m_address = 0;QBitArray m_coils;QVector<quint16> m_holdingRegisters;
};#endif // WRITEREGISTERMODEL_H

writeregistermodel.cpp文件

#include "writeregistermodel.h"enum { NumColumn = 0, CoilsColumn = 1, HoldingColumn = 2, ColumnCount = 3, RowCount = 10 };WriteRegisterModel::WriteRegisterModel(QObject *parent): QAbstractTableModel(parent),m_coils(RowCount, false), m_holdingRegisters(RowCount, 0u)
{}int WriteRegisterModel::rowCount(const QModelIndex &/*parent*/) const
{return RowCount;
}int WriteRegisterModel::columnCount(const QModelIndex &/*parent*/) const
{return ColumnCount;
}QVariant WriteRegisterModel::data(const QModelIndex &index, int role) const
{if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)return QVariant();Q_ASSERT(m_coils.count() == RowCount);Q_ASSERT(m_holdingRegisters.count() == RowCount);if (index.column() == NumColumn && role == Qt::DisplayRole)return QString::number(index.row());if (index.column() == CoilsColumn && role == Qt::CheckStateRole) // coilsreturn m_coils.at(index.row()) ? Qt::Checked : Qt::Unchecked;if (index.column() == HoldingColumn && role == Qt::DisplayRole) // holding registersreturn QString("0x%1").arg(QString::number(m_holdingRegisters.at(index.row()), 16));return QVariant();}QVariant WriteRegisterModel::headerData(int section, Qt::Orientation orientation, int role) const
{if (role != Qt::DisplayRole)return QVariant();if (orientation == Qt::Horizontal) {switch (section) {case NumColumn:return QStringLiteral("#");case CoilsColumn:return QStringLiteral("Coils  ");case HoldingColumn:return QStringLiteral("Holding Registers");default:break;}}return QVariant();
}bool WriteRegisterModel::setData(const QModelIndex &index, const QVariant &value, int role)
{if (!index.isValid() ||  index.row() >= RowCount || index.column() >= ColumnCount)return false;Q_ASSERT(m_coils.count() == RowCount);Q_ASSERT(m_holdingRegisters.count() == RowCount);if (index.column() == CoilsColumn && role == Qt::CheckStateRole) { // coilsauto s = static_cast<Qt::CheckState>(value.toUInt());s == Qt::Checked ? m_coils.setBit(index.row()) : m_coils.clearBit(index.row());emit dataChanged(index, index);return true;}if (index.column() == HoldingColumn && role == Qt::EditRole) { // holding registersbool result = false;quint16 newValue = value.toString().toUShort(&result, 16);if (result)m_holdingRegisters[index.row()] = newValue;emit dataChanged(index, index);return result;}return false;
}Qt::ItemFlags WriteRegisterModel::flags(const QModelIndex &index) const
{if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)return QAbstractTableModel::flags(index);Qt::ItemFlags flags = QAbstractTableModel::flags(index);if ((index.row() < m_address) || (index.row() >= (m_address + m_number)))flags &= ~Qt::ItemIsEnabled;if (index.column() == CoilsColumn) // coilsreturn flags | Qt::ItemIsUserCheckable;if (index.column() == HoldingColumn) // holding registersreturn flags | Qt::ItemIsEditable;return flags;
}void WriteRegisterModel::setStartAddress(int address)
{m_address = address;emit updateViewport();
}void WriteRegisterModel::setNumberOfValues(const QString &number)
{m_number = number.toInt();emit updateViewport();
}

06. 程序下载

6.1 RTUMaster写操作示例(一).rar
6.2 RTUMasterTest写操作(二).rar

07. 附录

7.1 Qt教程汇总
网址:https://dengjin.blog.csdn.net/article/details/115174639

【Qt】modbus之串口模式写操作相关推荐

  1. 【Qt】modbus之TCP模式写操作

    00. 目录 文章目录 00. 目录 01. 概述 02. 开发环境 03. 读Coils程序示例 04. 读HoldingRegisters程序示例 07. 综合示例 08. 程序下载 09. 附录 ...

  2. 【Qt】modbus之串口模式读操作

    00. 目录 文章目录 00. 目录 01. 概述 02. 开发环境 03. 读DiscreteInputs程序示例 04. 读Coils程序示例 05. 读InputRegisters程序示例 06 ...

  3. 【Qt】modbus之TCP模式读操作

    00. 目录 文章目录 00. 目录 01. 概述 02. 开发环境 03. 读Coils程序示例 04. 读DiscreteInputs程序示例 05. 读InputRegisters程序示例 06 ...

  4. CH376的串口模式操作U盘(读、写、txt文件、csv文件、串口调试讲解、stm32程序)

    文中介绍的可能有一些繁琐,因为也是想解释的更清楚一些,但是小弟希望大家能认真看完. 市面上关于ch376串口的知识介绍不多,可以说基本没有,博主当时也废了一些力气,在此希望给大家一些帮助,认真看完绝对 ...

  5. 工业自动化MODBUS协议读写器读卡器配置软件|工具之读写卡模式配置操作攻略

    本程序MODBUS读卡器配置软件所支持配置的读卡模式有四种,分别为标准MODBUS.Enchance Mode.Continue Read Mode和Read Once Mode.具体使用何种读卡模式 ...

  6. Write operations are not allowed in read-only mode 只读模式下(FlushMode.NEVER/MANUAL)写操作不允

    来源:http://blog.sina.com.cn/s/blog_656ffe730100ugtw.html org.springframework.dao.InvalidDataAccessApi ...

  7. 相信我,SDRAM真的不难(四)----写操作(页突发模式)

    写在前面 本文是SDRAM系列文章的第四篇,对SDRAM的突发写操作进行了详细介绍.代码编写与仿真验证. 其他博文链接:相信我,SDRAM真的不难----汇总篇(电梯直达) 1.写操作 SDRAM提供 ...

  8. 【Qt Modbus通信】libmodbus实现modbus的主机功能/从机功能 源码分享

    前言 modbus在上下位机数据交互时被广泛使用,因此写了这篇笔记和大家一起学习. 参考文献 Libmodbus源码分析(二)-常用接口函数分析 Libmodbus源码分析(四)-RTU相关函数分析 ...

  9. 如何在QT中读取串口数据

    总是能在别人的博客中学到太多太多,谢谢各位对知识的无私共享,谢谢大家 前言 去年我使用Qt编写串口通信程序时,将自己的学习过程写成了教程(Qt编写串口通信程序全程图文讲解),但是由于时间等原因,我只实 ...

最新文章

  1. 对话 | 不能与人类直接对话的智能硬件都是“伪”智能
  2. (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  3. Java中new function另外新建个文件的写法
  4. java基础57 css样式、选择器和css定位(网页知识)
  5. 从OpenKruise用户疑问开始理解K8s资源更新机制
  6. 为什么技术人一定要懂点“可信计算”?
  7. android 触摸防抖,一种触摸屏防抖优化方法、系统及触摸屏终端与流程
  8. VScode 把tab置换为空格
  9. 苹果被拒:Guideline 5.0 - Legal
  10. 服务器进pe iso安装系统教程,iso系统怎么安装( pe怎么安装iso原版镜像)
  11. php 用户控件,一个使用用户控件(包括组件)的演示-.NET教程,组件控件开发
  12. 高动态范围图像(HDR)处理
  13. Scratch之顺序、循环、选择三种程序结构
  14. python爬虫实训实验报告_用Pycharm进行scrapy爬虫的实验报告
  15. 华为服务器2288H v3和V5关机不能启动处理
  16. 数据库中的html代码放到div中放不进去
  17. SteamVR Unity Tool Kit(二):激光与移动
  18. 钽电容一般用于电源输出端滤波
  19. Modelsim10.5和ISE14.7安装及联合仿真
  20. 虚拟机如何设置静态IP

热门文章

  1. lnmp一键包502 Bad Gateway解决方法
  2. python 文件指定位置写入-Python从文件中读取指定的行以及在文件指定位置写入...
  3. matlab for 取数组,for循环中的MATLAB和单元格数组处理
  4. python安装opencv出现错误,通过pip安装opencv时出错
  5. Java黑皮书课后题第7章:*7.21(整数求和)编写程序,从命令行输入不定数目的整数,然后显示它们的和
  6. android版本相机权限,Android 11系统权限收紧,第三方APP仅支持调用原生相机
  7. 2012届华为校园招聘机试题
  8. 【git】强制覆盖本地代码(与git远程仓库保持一致)
  9. 跳转到页面后加载一个请求的方法
  10. bootstrap学习网站