Qt下使用modbus-c库实现PLC线圈/保持寄存器的读写
系列文章目录
提示:这里是该系列文章的所有文章的目录
第一章:Qt下使用ModbusTcp通信协议进行PLC线圈/保持寄存器的读写(32位有符号数)
第二章:Qt下使用modbus-c库实现PLC线圈/保持寄存器的读写
文章目录
- 系列文章目录
- 前言
- 一、下载modbus-c库
- 二、实现ModbusLib类
- 三、使用ModbusLib类
- 四、下载链接
- 总结
前言
在上一篇文章中提到了使用Qt下的Modbus模块来进行ModbusTcp的通信,采用QModbusTcpClient类作为Modbus客户端(主站)与PLC读写,正常情况下是可以满足读写需求的,但是使用过程中发现读写频率较高时会出现写入延迟等问题,后面发现使用这个C语言写的第三方Modbus库来与PLC通信会更加稳定,性能更优越,这里将结合相应的示例进行讲解,以便大家学习,如有错误之处,欢迎大家批评指正。
项目效果

提示:以下是本篇文章正文内容,下面案例可供参考
一、下载modbus-c库
通过参考博客中的下载链接到官网下载:https://sourceforge.net/projects/libmodbus/(似乎失效了,下载不了),或者通过下文中我的百度网盘链接下载压缩包,文件内容如下,其中包含相关源码和ws2_32.dll(在lib中,依赖该动态库)

二、实现ModbusLib类
这里我实现了自己的ModbusLib类的封装,使用了pri子模块的方式,也是方便日后进行此模块的复用
这里我使用的是tcp的方式来通信,pri内容如下:
ModbusLib.pri
HEADERS += \$$PWD/modbus.h \$$PWD/modbus-private.h \#$$PWD/modbus-rtu.h \#$$PWD/modbus-rtu-private.h \$$PWD/modbus-tcp.h \$$PWD/modbus-tcp-private.h \$$PWD/mymodbuslib.hSOURCES += \$$PWD/modbus.c \$$PWD/modbus-data.c \#$$PWD/modbus-rtu.c \$$PWD/modbus-tcp.c \$$PWD/mymodbuslib.cppLIBS += -L$$PWD/lib/ -lws2_32
这里会看到将modbus-c库的源码加入到工程中,所以添加好相关的头文件就可以调用库函数了,这里相关函数的使用见下文代码:
1.mymodbusLib.h
#ifndef MYMODBUSLIB_H
#define MYMODBUSLIB_H#include <QObject>
#include <QDateTime>
#include <QTimer>
#include <QThread>
#include <QEventLoop>
#include <QCoreApplication>
#include <QDebug>
#include "modbus.h"
#include "modbus-private.h"
#include "modbus-tcp.h"
#include "modbus-tcp-private.h"#define LOGDEBUG qDebug()<<QTime::currentTime().toString("[hh:mm:ss:zzz]")class MyModbusLib : public QObject
{Q_OBJECTpublic:explicit MyModbusLib(QObject *parent = nullptr);~MyModbusLib();bool initConnect(QString tcpIP,int tcpPort);bool readCoil(int readAdd);void writeCoil(int writeAdd,int writeNum);int readRegister(int writeAdd);void writeRegister32(int writeAdd,int writeNum);private:modbus_t *m_modbus;
};#endif // MYMODBUSLIB_H
2.mymodbusLib.cpp
#include "mymodbuslib.h"MyModbusLib::MyModbusLib(QObject *parent) : QObject(parent)
{}MyModbusLib::~MyModbusLib()
{modbus_close(m_modbus);modbus_free(m_modbus);
}//初始化
bool MyModbusLib::initConnect(QString tcpIP,int tcpPort)
{m_modbus = modbus_new_tcp(tcpIP.toLatin1().data(),tcpPort);modbus_set_slave(m_modbus,1);if(modbus_connect(m_modbus) == -1){return false;}//设置modbus超时时间为1000毫秒struct timeval t;t.tv_sec = 0;t.tv_usec = 1000000;modbus_set_response_timeout(m_modbus,t.tv_sec,t.tv_usec);return true;
}bool MyModbusLib::readCoil(int readAdd)
{uint8_t bitsr[1]={0};int rNum = modbus_read_bits(m_modbus,readAdd,1,bitsr);if(rNum == -1){LOGDEBUG<<"读取线圈"<<readAdd<<"失败!";return false;}else{//LOGDEBUG<<"读取线圈"<<readAdd<<"成功!";if(bitsr[0] == 1){return true;}}return false;
}void MyModbusLib::writeCoil(int writeAdd,int writeNum)
{uint8_t bitsw[1]={0};bitsw[0] = writeNum;int rNum = modbus_write_bits(m_modbus,writeAdd,1,bitsw);if(rNum == -1){LOGDEBUG<<"线圈"<<writeAdd<<"写入"<<writeNum<<"失败!";}else{LOGDEBUG<<"线圈"<<writeAdd<<"写入"<<writeNum<<"成功!";}
}int MyModbusLib::readRegister(int readAdd)
{uint16_t regsr[2]={0};int rNum = modbus_read_registers(m_modbus,readAdd,2,regsr);if(rNum == -1){LOGDEBUG<<"读取寄存器"<<readAdd<<"失败!";}else{//LOGDEBUG<<"读取寄存器"<<readAdd<<"成功!";int readNum = regsr[0] | (regsr[1] << 16);return readNum;}return 0;
}void MyModbusLib::writeRegister32(int writeAdd,int writeNum)
{uint16_t regsw[2]={0};regsw[0] = writeNum & 0xffff;regsw[1] = (writeNum >> 16) & 0xffff;int rNum = modbus_write_registers(m_modbus,writeAdd,2,regsw);if(rNum == -1){LOGDEBUG<<"寄存器"<<writeAdd<<"写入"<<writeNum<<"失败!";}else{LOGDEBUG<<"寄存器"<<writeAdd<<"写入"<<writeNum<<"成功!";}
}
三、使用ModbusLib类
工程结构如图,下面可以直接在主界面使用封装好的ModbusLib类,详细内容见代码:

1.ModbusTest.pro
QT += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11DEFINES += QT_DEPRECATED_WARNINGS#设置字符
contains( CONFIG,"msvc" ):QMAKE_CXXFLAGS += /source-charset:utf-8 /execution-charset:utf-8
contains( CONFIG,"msvc" ):QMAKE_CFLAGS +=/source-charset:utf-8 /execution-charset:utf-8#包含子模块
include (./ModbusLib/ModbusLib.pri) #libmodbus库SOURCES += \main.cpp \mainwindow.cppHEADERS += \mainwindow.hFORMS += \mainwindow.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
2.mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTime>
#include <QDebug>
#include "ModbusLib/mymodbuslib.h"QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();void initWidget();private slots:void on_pb_connect_clicked();void on_pb_readC_clicked();void on_pb_writeC_clicked();void on_pb_readH_clicked();void on_pb_writeH_clicked();private:Ui::MainWindow *ui;MyModbusLib *m_myModbus;};
#endif // MAINWINDOW_H
3.mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);this->initWidget();
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::initWidget()
{//使能按钮ui->pb_readC->setEnabled(false);ui->pb_writeC->setEnabled(false);ui->pb_readH->setEnabled(false);ui->pb_writeH->setEnabled(false);//创建modbus对象m_myModbus = new MyModbusLib();
}void MainWindow::on_pb_connect_clicked()
{QString tcpIP = ui->le_ip->text();int tcpPort = ui->le_port->text().toInt();if(m_myModbus->initConnect(tcpIP,tcpPort)){ui->lb_state->setText("连接成功");LOGDEBUG<<"ModbusTCP连接成功!";//使能按钮ui->pb_readC->setEnabled(true);ui->pb_writeC->setEnabled(true);ui->pb_readH->setEnabled(true);ui->pb_writeH->setEnabled(true);}else{ui->lb_state->setText("连接失败");LOGDEBUG<<"ModbusTCP连接失败!";}
}void MainWindow::on_pb_readC_clicked()
{int readAdd = ui->le_addC->text().toInt();ui->lb_numC->setText(QString::number(m_myModbus->readCoil(readAdd)));
}void MainWindow::on_pb_writeC_clicked()
{int writeAdd = ui->le_addC->text().toInt();int writeNum = ui->le_writeNumC->text().toInt();m_myModbus->writeCoil(writeAdd,writeNum);
}void MainWindow::on_pb_readH_clicked()
{int readAdd = ui->le_addH->text().toInt();ui->lb_numH->setText(QString::number(m_myModbus->readRegister(readAdd)));
}void MainWindow::on_pb_writeH_clicked()
{int writeAdd = ui->le_addH->text().toInt();int writeNum = ui->le_writeNumH->text().toInt();m_myModbus->writeRegister32(writeAdd,writeNum);
}
4.mainwindow.ui

四、下载链接
modbus-c库下载链接:https://pan.baidu.com/s/1rBQzOhwPIrRxL_f2CofJxQ
提取码:xxcj
总结
后续在使用modbus-c库的测试中,发现读写的效率是比Qt自带的modbus模块要高并且稳定的,建议在需要与PLC进行tcp通信时使用这种方式。这里我的ModbusLib类封装在一个文件夹内,要调用直接在工程pro中添加pri子模块就可以使用,比较方便。
hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。
参考博客:QT 使用第三方C库实现Modbus通讯
相关文章:
Qt下使用modbus-c库实现PLC线圈/保持寄存器的读写
系列文章目录 提示:这里是该系列文章的所有文章的目录 第一章:Qt下使用ModbusTcp通信协议进行PLC线圈/保持寄存器的读写(32位有符号数) 第二章:Qt下使用modbus-c库实现PLC线圈/保持寄存器的读写 文章目录 系列文章目录…...
C++ 滑动窗口
例1 209. 长度最小的子数组 ①窗口大小不固定 ②求最小长度 -> ret INT_MAX ③数组内的值都大于0, 符合单调性(sum nums[right] -> sum增大) while里面符合条件,在里面更改ret 参考代码 class Solution { public:i…...
【深度学习】TensorFlow基础介绍
TensorFlow 模型 张量、变量共同点:具有形状、类型、值等3个属性。 不同点:变量可被TensorFlow的自动求导机制求导,常被用于机器学习模型的参数。 tfrecord tensorflow定义的数据格式,一种二进制文件格式,用于保存…...
springcloud:3.3测试重试机制
服务提供者【test-provider8001】 Openfeign远程调用服务提供者搭建 文章地址http://t.csdnimg.cn/06iz8 相关接口 测试远程调用:http://localhost:8001/payment/index 服务消费者【test-consumer-resilience4j8004】 Openfeign远程调用消费者搭建 文章地址http:/…...
【笔记】【电子科大 离散数学】 3.谓词逻辑
谓词引入 因为含变量的语句(例如x > 3)不是命题,无法进行逻辑推理。 为了研究简单命题句子内部的逻辑关系,我们需要对简单命题进行分解,利用个体词,谓词和量词来描述它们,并研究个体与总体…...
倍增算法C++
倍增 倍增算法是一种优化算法,通常用于某些需要高效计算指数幂的场景。它基于分治的思想,通过反复求平方来实现快速计算指数幂的目的。在实际应用中,倍增算法经常用于解决最近公共祖先问题、二分查找等。 1、快速幂详解 ksm核心代码 倍增就是…...
uniapp制作--进步器的选择
介绍: 进步器的选择,一般用于商城购物选择物品数量的场景 注意:该输入框只能输入大于或等于0的整数 效果展示: 代码展示: 以下是一个简单的购物车页面示例,包括选择商品和显示数量的功能: 在这个示例中…...
前端高频面试--查缺补漏篇
什么是进程和线程,有什么区别 进程:进程是程序的一次执行过程,是动态的过程,有自身产生、存在、消亡的过程。 线程:线程由进程创建,是进程的一个实体。一个进程可以拥有多个线程。 举个例子:…...
【计算机学习】-- 网页视频加速
系列文章目录 文章目录 系列文章目录前言一、开发者选项二、定义和用法1.基础语法:2.什么是uncaught TypeError:Cannot read properties of null? 二、开发者工具面板:1.Elements面板:2.Console面板: 总结 前言 一、开发者选项 …...
系统运维-Linux配置C、C++、Go语言编译环境
C yum install gcc -y #安装gcc编译器 gcc --version #验证环境gcc (GCC) 11.3.1 20221121 (Red Hat 11.3.1-4) Copyright (C) 2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even f…...
【设计模式】(二)设计模式六大设计原则
一、 设计原则概述 设计模式中主要有六大设计原则,简称为SOLID ,是由于各个原则的首字母简称合并的来(两个L算一个,solid 稳定的),六大设计原则分别如下: 1、单一职责原则(Single Responsibitity Principle&#…...
go-zero官网
go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 go-zero官网:go-zero 缩短从需求到上线的距离...
Redis的应用场景以及常见问题(持续更新)
一、使用场景 1,在大型的秒杀库存扣减,app首页流量高峰,很容易将传统的关系型数据库(mysql,oracle等)给压垮 2,还有很多没必要持久化的数据,比如说短信验证码,点赞数等 3,…...
前端添加压缩包内文件名称校验
1. tar包内文件名称校验 1. 读取tar包内所有的文件名称 export class TarReader {fileInfo: any[]buffer: string | ArrayBufferconstructor() {this.fileInfo []}readFile(file) {return new Promise(resolve > {const reader new FileReader()reader.onload event &g…...
redis02 安装
官网下载 传送门https://redis.io/download/#redis-downloads 安装Redis mac m1安装 下载你需要版本的软件包放到指定的目录下进行解压 cd 到解压好的redis目录 运行下面的命令进行编译测试 sudo make test 中途可能会提示你安装make工具,按提示安装即可&…...
#QT(QT时钟)
1.IDE:QTCreator 2.实验 3.记录 qtime(qt的时间类) qtimer(qt的定时类) 4.代码 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTime> // #include <QTimer&g…...
T-RAG:结合实体检测的增强检索生成模型
内容摘要: T-RAG是一种新的大型语言模型(LLM)应用框架,在保证数据隐私的同时,提高了对私有企业文档的问答系统性能。T-RAG通过结合已有的增强检索生成(RAG)框架、自定义的开源语言模型以及一个实…...
u-boot: NAND 驱动简介
文章目录 1. 前言2. NAND 初始化3. 访问 NAND 设备3.1 查看 NAND 设备信息3.1.1 查看 NAND 设备基本信息3.1.2 查看 NAND 设备 MTD 分区3.1.3 查看 NAND 设备坏块 3.2 NAND 擦除操作3.3 NAND 写操作3.4 NAND 读操作3.5 其它 NAND 操作 1. 前言 限于作者能力水平,本…...
史上最全的大数据开发八股文【自己的吐血总结】
自我介绍 我本硕都是双非计算机专业,从研一下开始学习大数据开发的相关知识,从找实习到秋招,我投递过100公司,拿到过10的offer,包括滴滴、字节、蚂蚁、携程、蔚来、去哪儿等大厂(岗位都是大数据开发&#…...
数据库学习案例20240304-mysql数据库案例总结(碎片,统计信息)
1 表中的碎片 在InnoDB中删除行的时候,这些行只是被标记为“已删除”,而不是真正从物理存储上进行了删除,因而存储空间也没有真正被释放回收。InnoDB的Purge线程会异步地来清理这些没用的索引键和行。但是依然没有把这些释放出来的空间还给操…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...
高防服务器价格高原因分析
高防服务器的价格较高,主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因: 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器,因此…...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...
