Qt下使用modbus-c库实现PLC线圈/保持寄存器的读写

2024-02-28 14:28

本文主要是介绍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线圈/保持寄存器的读写的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/755756

相关文章

AJAX请求上传下载进度监控实现方式

《AJAX请求上传下载进度监控实现方式》在日常Web开发中,AJAX(AsynchronousJavaScriptandXML)被广泛用于异步请求数据,而无需刷新整个页面,:本文主要介绍AJAX请... 目录1. 前言2. 基于XMLHttpRequest的进度监控2.1 基础版文件上传监控2.2 增强版多

使用Python自建轻量级的HTTP调试工具

《使用Python自建轻量级的HTTP调试工具》这篇文章主要为大家详细介绍了如何使用Python自建一个轻量级的HTTP调试工具,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录一、为什么需要自建工具二、核心功能设计三、技术选型四、分步实现五、进阶优化技巧六、使用示例七、性能对比八、扩展方向建

Redis分片集群的实现

《Redis分片集群的实现》Redis分片集群是一种将Redis数据库分散到多个节点上的方式,以提供更高的性能和可伸缩性,本文主要介绍了Redis分片集群的实现,具有一定的参考价值,感兴趣的可以了解一... 目录1. Redis Cluster的核心概念哈希槽(Hash Slots)主从复制与故障转移2.

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

使用Python实现一键隐藏屏幕并锁定输入

《使用Python实现一键隐藏屏幕并锁定输入》本文主要介绍了使用Python编写一个一键隐藏屏幕并锁定输入的黑科技程序,能够在指定热键触发后立即遮挡屏幕,并禁止一切键盘鼠标输入,这样就再也不用担心自己... 目录1. 概述2. 功能亮点3.代码实现4.使用方法5. 展示效果6. 代码优化与拓展7. 总结1.

Mybatis 传参与排序模糊查询功能实现

《Mybatis传参与排序模糊查询功能实现》:本文主要介绍Mybatis传参与排序模糊查询功能实现,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、#{ }和${ }传参的区别二、排序三、like查询四、数据库连接池五、mysql 开发企业规范一、#{ }和${ }传参的

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

Docker镜像修改hosts及dockerfile修改hosts文件的实现方式

《Docker镜像修改hosts及dockerfile修改hosts文件的实现方式》:本文主要介绍Docker镜像修改hosts及dockerfile修改hosts文件的实现方式,具有很好的参考价... 目录docker镜像修改hosts及dockerfile修改hosts文件准备 dockerfile 文

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java