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通讯

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/714666.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端Vue3项目如何打包成Docker镜像运行

将前端Vue3项目打包成Docker镜像并运行包括几个主要步骤&#xff1a;项目打包、编写Dockerfile、构建镜像和运行容器。下面是一个基本的流程&#xff1a; 1. 项目打包 首先&#xff0c;确保你的Vue3项目可以正常运行和打包。在项目根目录下执行以下命令来打包你的Vue3项目&am…

nest.js使用nest-winston日志一

nest-winston文档 nest-winston - npm 参考&#xff1a;nestjs中winston日志模块使用 - 浮的blog - SegmentFault 思否 安装 cnpm install --save nest-winston winstoncnpm install winston-daily-rotate-file 在main.ts中 import { NestFactory } from nestjs/core; im…

【5G 接口协议】GTP-U协议介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

基础小白快速入门Python------>模块的作用和意义

模块&#xff0c; 这个词听起来是如此的高大威猛&#xff0c;以至于萌新小白见了瑟瑟发抖&#xff0c;本草履虫见了都直摇头&#xff0c;好像听上去很难的样子&#xff0c;但是但是&#xff0c;年轻人&#xff0c;请听本少年细细讲述&#xff0c;他只是看起来很难&#xff0c;实…

GO-接口

1. 接口 在Go语言中接口&#xff08;interface&#xff09;是一种类型&#xff0c;一种抽象的类型。 interface是一组method的集合&#xff0c;接口做的事情就像是定义一个协议&#xff08;规则&#xff09;&#xff0c;只要一台机器有洗衣服和甩干的功能&#xff0c;我就称它…

【go语言开发】swagger安装和使用

本文主要介绍go-swagger的安装和使用&#xff0c;首先介绍如何安装swagger&#xff0c;测试是否成功&#xff1b;然后列出常用的注释和给出使用例子&#xff1b;最后生成接口文档&#xff0c;并在浏览器上测试 文章目录 安装注释说明常用注释参考例子 文档生成格式化文档生成do…

大模型生成,Open API调用

大模型是怎么生成结果的 通俗原理 其实&#xff0c;它只是根据上文&#xff0c;猜下一个词&#xff08;的概率&#xff09;…… OpenAI 的接口名就叫【completion】&#xff0c;也证明了其只会【生成】的本质。 下面用程序演示【生成下一个字】。你可以自己修改 prompt 试试…

【C++】类的转换函数

使用场景 C中当你创建了一个类&#xff0c;你想把这个类对象转换成基本类型的函数。类对象->基本类型对象 原理 如下实例&#xff0c;设计一个分数类&#xff0c;实现分数转换成double 浮点数的转换函数。并在mian函数隐式调用。 #include<iostream> class Fractio…

Python爬虫Cookies 池的搭建

Cookies 池的搭建 很多时候&#xff0c;在爬取没有登录的情况下&#xff0c;我们也可以访问一部分页面或请求一些接口&#xff0c;因为毕竟网站本身需要做 SEO&#xff0c;不会对所有页面都设置登录限制。 但是&#xff0c;不登录直接爬取会有一些弊端&#xff0c;弊端主要有…

南京师范大学计电院数据结构课设——排序算法

1 排序算法 1.1 题目要求 编程实现希尔、快速、堆排序、归并排序算法。要求首先随机产生10000个数据存入磁盘文件&#xff0c;然后读入数据文件&#xff0c;分别采用不同的排序方法进行排序并将结果存入文件中。 1.2 算法思想描述 1.2.1 随机数生成 当需要生成一系列随机数…

windows 11 前后端项目部署

目录 1.准备环境&#xff1a; 2.安装jdk 测试&#xff1a;winr 输入cmd 3.安装tomcat 4.安装mysql 远程导入数据&#xff1a; 外部后台访问&#xff1a;192.168.232.1:8080/crm/sys/loginAction.action?usernamezs&password123 5.安装nginx 前后端部署&#xff1…

qsort函数的模拟实现(冒泡排序模拟)

冒泡排序&#xff1a; 从第一个元素开始&#xff0c;依次比较相邻的两个元素&#xff0c;如果顺序不对就交换它们。 经过一轮遍历后&#xff0c;最大&#xff08;或最小&#xff09;的元素会排在最后。 重复进行上述步骤&#xff0c;直到没有任何元素需要交换&#xff0c;即…

爬虫入门到精通_实战篇8(分析Ajax请求并抓取今日头条美食美图)_界面上抓取Ajax方式

1 目标 目标&#xff1a; 抓取今日头条美食美图&#xff0c;如下&#xff1a; 一些网页直接请求得到的HTML代码并没有在网页中看到的内容&#xff0c;因为一些信息是通过Ajax加载&#xff0c;并通过js渲染生成的&#xff0c;这时就需要通过分析网页的请求来获取想要爬取的内容…

CSS 盒子模型(box model)

概念 所有HTML元素可以看作盒子&#xff0c;在CSS中&#xff0c;"box model"这一术语是用来设计和布局时使用CSS盒模型本质上是一个盒子&#xff0c;封装周围的HTML元素&#xff0c;它包括&#xff1a;外边距(margin)&#xff0c;边框(border)&#xff0c;内边距(pad…

关于 HTTP 协议,你了解多少

HTTP协议 FastAPI 是建立在 HTTP 协议之上&#xff0c;所以为了更好的掌握 FastAPI。我们需要先简单的了解一下 HTTP协议 简介 HTTP&#xff08;Hypertext Transfer Protocol&#xff09;遵循经典的客户端-服务器模型&#xff0c;客户端打开连接以发出请求&#xff0c;然后等…

【Go语言】Go语言中的流程控制

Go语言中的流程控制 流程控制主要用于设定计算执行的顺序&#xff0c;简历程序的逻辑结果&#xff0c;Go语言的流程控制语句与其他语言类似&#xff0c;支持如下几种流程控制语句&#xff1a; 条件语句&#xff1a;用于条件判断&#xff0c;对应的关键字有if、else和else if&a…

使用 Docker 部署 Fiora 在线聊天室平台

一、Fiora 介绍 Fiora 简介 Fiora 是一款开源免费的在线聊天系统。 GitHub&#xff1a;https://github.com/yinxin630/fiora Fiora 功能 注册账号并登录&#xff0c;可以长久保存你的数据加入现有群组或者创建自己的群组&#xff0c;来和大家交流和任意人私聊&#xff0c;并添…

MySQL 主从读写分离入门——基本原理以及ProxySQL的简单使用

一、读写分离工作原理 读写分离的工作原理&#xff1a;在大型网站业务中&#xff0c;当单台数据库无法满足并发需求时&#xff0c;通过主从同步方式同步数据。设置一台主服务器负责增、删、改&#xff0c;多台从服务器负责查询&#xff0c;从服务器从主服务器同步数据以保持一…

C语言数据结构——队列

目录 0.前言 1.队列的基本概念 2.队列的实现 2.1实现方式 2.2具体实现 3.队列的应用场景 4.一道队列的算法题&#xff08;LeetCode225. 用队列实现栈&#xff09; 5.结语 &#xff08;图像由AI生成&#xff09; 0.前言 在计算机科学领域&#xff0c;数据结构是组织和…

Linux篇: 进程控制

一、进程创建 1.1 fork函数初识 在Linux中&#xff0c;fork函数是非常重要的函数&#xff0c;它从已存在进程中创建一个新进程。新进程为子进程&#xff0c;而原进程为父进程。 返回值&#xff1a; 在子进程中返回0&#xff0c;父进程中返回子进程的PID&#xff0c;子进程创…