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

news2024/11/20 20:22:28

系列文章目录

提示:这里是该系列文章的所有文章的目录
第一章: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.h

SOURCES += \
    $$PWD/modbus.c \
    $$PWD/modbus-data.c \
    #$$PWD/modbus-rtu.c \
    $$PWD/modbus-tcp.c \
    $$PWD/mymodbuslib.cpp

LIBS += -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_OBJECT

public:
    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 gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

DEFINES += 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.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    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_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    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.coloradmin.cn/o/1475240.html

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

相关文章

如何使用程序通过OCR识别解析PDF中的表格

https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.7/ppstructure/table/README_ch.md#41-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B Paddle-structure是目前我们能找到的可以做中英文版面分析较好的一个基础模型&#xff0c;其开源版可以识别十类页面元素。这篇文章介绍…

VL817-Q7 USB3.0 HUB芯片 适用于扩展坞 工控机 显示器

VL817-Q7 USB3.1 GEN1 HUB芯片 VL817-Q7 USB3.1 GEN1 HUB芯片 VIA Lab的VL817是一款现代USB 3.1 Gen 1集线器控制器&#xff0c;具有优化的成本结构和完全符合USB标准3.1 Gen 1规范&#xff0c;包括ecn和2017年1月的合规性测试更新。VL817提供双端口和双端口4端口配置&…

windows安装部署node.js并搭建Vue项目

一、官网下载安装包 官网地址&#xff1a;https://nodejs.org/zh-cn/download/ 二、安装程序 1、安装过程 如果有C/C编程的需求&#xff0c;勾选一下下图所示的部分&#xff0c;没有的话除了选择一下node.js安装路径&#xff0c;直接一路next 2、测试安装是否成功 【winR】…

VUE从0到1创建项目及基本路由、页面配置

一、创建项目:(前提已经安装好vue和npm) 目录:E:\personal\project_pro\ windows下,win+R 输入cmd进入命令行: cd E:\personal\project_pro E:# 创建名为test的项目 vue create test# 用上下键选择vue2或vue3,回车确认创建本次选择VUE3 创建好项目后,使用…

css transform 会影响position 定位

比如通过以下代码.实现导航条上的每个li栏目,以不同的时间间隔,从上向下移动进来并显示 .my-navbar ul li {position: relative;opacity: 0;transform: translateY(-30px);transition: transform .6s cubic-bezier(.165,.84,.44,1),opacity .6s cubic-bezier(.165,.84,.44,1);…

书生·浦语大模型全链路开源体系介绍

背景介绍 随着人工智能技术的迅猛发展&#xff0c;大模型技术已成为当今人工智能领域的热门话题。2022 年 11 月 30 日&#xff0c;美国 OpenAI 公司发布了 ChatGPT 通用型对话系统 并引发了全球 的极大关注&#xff0c;上线仅 60 天月活用户数便超过 1 亿&#xff0c;成为历史…

租赁系统|租赁软件提供免押金等多种租赁方式

租赁小程序是一种全新的租赁模式&#xff0c;为用户提供了免押金、多种租赁方式、定制化服务等多项优势&#xff0c;让用户的租赁体验更加美好。让我们来了解一下它的特点和功能。 首先&#xff0c;租赁小程序支持租完即送&#xff0c;无需等待固定租期。它提供了多种租赁方式&…

Rocky Linux 运维工具 ls

一、ls 的简介 ​​ls​ 用于列出当前目录下的文件和目录&#xff0c;以及它们的属性信息。通过 ​ls​命令可以查看文件名、文件大小、创建时间等信息&#xff0c;并方便用户浏览和管理文件。 二、ls 的参数说明 序号参数描述1-a显示所有文件&#xff0c;包括以 ​.​开头的…

辽宁博学优晨教育:视频剪辑培训的领航者

在当今数字化时代&#xff0c;视频剪辑已经成为一项炙手可热的技能。无论是短视频的火爆&#xff0c;还是影视行业的蓬勃发展&#xff0c;都离不开优秀的视频剪辑人才。辽宁博学优晨教育&#xff0c;作为视频剪辑培训的佼佼者&#xff0c;以其正规、专业的教学态度和高质量的培…

Biotin-PEG2-Thiol,生物素-PEG2-巯基,应用于抗体标记、蛋白质富集等领域

您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;Biotin-PEG2-Thiol&#xff0c;生物素-PEG2-巯基&#xff0c;Biotin PEG2 Thiol&#xff0c;生物素 PEG2 巯基 一、基本信息 【产品简介】&#xff1a;Biotin PEG2 Thiol can bind with antibodies to prepare biot…

使用docker安装otter

1、使用docker安装otter首先要把docker装好 2、使用docker把镜像拉进去 拉镜像的过程中注意使用docker load -i imagename命令。如果使用docker import imagename命令拉镜像&#xff0c;在安装过程中会报以下错误&#xff1a; 网上查资料说是需要使用docker ps -a --no-trunc…

MySQL:常用的SQL语句

提醒&#xff1a;设定下面的语句是在数据库名为 db_book执行的。 一、创建表 1. 创建t_booktype表 USE db_book; CREATE TABLE t_booktype(id INT AUTO_INCREMENT, bookTypeName VARCHAR(20),bookTypeDesc varchar(200),PRIMARY KEY (id) );2. 创建t_book表 USE db_book; C…

Linux系统——Nginx拓展

目录 一、重写功能——rewrite 1.if 1.1 if 2. return 2.1状态码301和302的区别 301 302 3. set 4. break 5. rewrite 5.1 rewrite flag使用 5.2 flag说明 5.3举例 5.3.1访问 bj 跳转 beijing 5.3.2举例——break 5.3.3 http 转 https 5.3.4 break 与 last …

OpenSSL 基础及实践探索

前言 SSL是Secure Sockets Layer&#xff08;安全套接层协议&#xff09;的缩写&#xff0c;可以在Internet上提供秘密性传输。Netscape公司在推出第一个Web浏览器的同时&#xff0c;提出了SSL协议标准。其目标是保证两个应用间通信的保密性和可靠性,可在服务器端和用户端同时…

状态空间模型(SSM)

论文&#xff1a;A new approach to linear filtering and prediction problems http://160.78.24.2/Public/Kalman/Kalman1960.pdf 状态空间模型介绍 术语状态空间模型具有非常广泛的含义&#xff0c;它简单地表示任何具有潜在状态的循环过程的概念。 它已被用来指代不同学科…

Eslint在Vscode中使用技巧的相关技巧

ps :该文章会详细结论构建一个脚手架遇到的问题&#xff0c;会持续更新&#xff0c;请定时查看 Eslint相关​ 在vscode中使用eslint插件 在vscode中用户配置没有开启eslint.enable 在vscode中工作区配置开启eslint.enable settings.json中没有做eslint相关配置 在编写的vue…

MSSQL渗透测试

目录 mssql数据库连接提权至服务器权限 拿到目标的IP地址&#xff0c;我们先对IP地址进行信息收集&#xff0c;收集信息资产&#xff0c;同时使用nmap对IP地址进行扫描 nmap -sC -sV IP从扫描的结果中&#xff0c;我们能知道目标服务器是windows操作系统&#xff0c;使用的是m…

nginx 模块 高级配置

目录 一、高级配置 1.1. 网页的状态页 1.2.Nginx 第三方模块 ehco 模块 打印 1.3.变量 1.3.1 内置变量 1.3.2自定义变量 1.4.Nginx压缩功能 1.5.https 功能 1.6.自定义图标 一、高级配置 1.1. 网页的状态页 基于nginx 模块 ngx_http_stub_status_module 实现&…

协议-http协议-基础概念01-发展历程-http组成-http是什么-相关的应用-相关的协议

发展历程-http组成-http是什么-相关的应用-相关的协议 参考来源&#xff1a; 极客时间-透视HTTP协议(作者&#xff1a;罗剑锋)&#xff1b; 01-HTTP的发展历程 1989 年&#xff0c;任职于欧洲核子研究中心&#xff08;CERN&#xff09;的蒂姆伯纳斯 - 李&#xff08;Tim Ber…

前端架构: 脚手架命令行交互核心实现之inquirer和readline的应用教程

命令行交互核心实现 核心目标&#xff1a;实现命令行行交互&#xff0c;如List命令行的交互呢比命令行的渲难度要更大&#xff0c;因为它涉及的技术点会会更多它涉及以下技术点 键盘输入的一个监听 (这里通过 readline来实现)计算命令行窗口的尺寸清屏光标的移动输出流的静默 …