Qt-Libmodbus

一、介绍

  libmodbus是一个快速且可移植的Modbus库,支持传统的RS-232、RS-422、RS-485和以太网设备。

  A Modbus library for Linux, Mac OS X, FreeBSD, QNX and Win32.

  libmodbus is a free software library to send/receive data according to the Modbus protocol. This library is written in C and supports RTU (serial) and TCP (Ethernet) communications.The license of libmodbus is LGPL v2.1+ and the licence of programs in the tests directory is BSD 3-clause.

二、下载及安装

  下载地址:https://libmodbus.org/

   安装:

1 //进入解压目录
2 cd libmodbus-3.1.6
3 //配置编译选项(以ARM为例,--build为系统构架 --host为编译对应系统工具链,--prefix为输出目录,选项均是可选的)
4 ./configure --build=i686 --host=arm-linux --enable-static --prefix=[install path]/
5 //编译安装
6 make && make install

 

三、示例

  1、RTU slave

  笔者在使用的时候发现串口接收函数在未接收到数据时会阻塞,如果同时使用TCP通讯建议放在两个不同的线程中。使用的时候只需创建一个此线程的对象,初始化完成后打开线程即可。

  rtucomthread.cpp文件的内容:

  1 #include "rtucommthread.h"
  2 
  3 #include <sys/ioctl.h>
  4 #include <unistd.h>
  5 #include <netinet/in.h>
  6 #include <arpa/inet.h>
  7 #include <fcntl.h>
  8 
  9 uint16_t data_buff[256] = {1};
 10 
 11 RtuCommThread::RtuCommThread(QObject *parent) :
 12     QThread(parent),
 13     ctx_rtu(NULL),
 14     mb_mapping(NULL)
 15 {
 16     Baud[0] = 115200;           //波特率
 17     Baud[1] = 19200;
 18     Baud[2] = 9600;
 19     Baud[3] = 4800;
 20     Baud[4] = 2400;
 21 
 22     Parity[0] = 'N';            //校验
 23     Parity[1] = 'O';
 24     Parity[2] = 'E';
 25 
 26     DataBits[0] = 7;            //数据位
 27     DataBits[1] = 8;
 28 
 29     StopBit[0] = 1;             //停止位
 30     StopBit[1] = 2;
 31 
 32     /* 分配寄存器位的数组大小为125 */
 33     mb_mapping = modbus_mapping_new(0, 0, MODBUS_MAX_READ_REGISTERS, 0);
 34 }
 35 
 36 RtuCommThread::~RtuCommThread()
 37 {
 38     /* 释放分配的数组单元 */
 39     modbus_mapping_free(mb_mapping);
 40 
 41     modbus_close(ctx_rtu);
 42     modbus_free(ctx_rtu);
 43     mb_mapping = NULL;
 44     ctx_rtu = NULL;
 45 }
 46 
 47 /*******************************************************************************************
 48 * 函数名称:run()
 49 * 功能描述:线程执行函数
 50 * 输入参数:无
 51 * 返回参数:无
 52 * 创建者:
 53 * 创建日期:2020.3.23
 54 *******************************************************************************************/
 55 void RtuCommThread::run()
 56 {
 57     int rc;
 58     int offset;
 59     uint16_t data;
 60 
 61     while(1)
 62     {
 63         /* 接收请求 */
 64         rc = modbus_receive(ctx_rtu, query);
 65         if(rc == -1)
 66         {
 67             msleep(100);
 68             continue;
 69         }
 70         /* 得到头文件的长度 */
 71         offset = modbus_get_header_length(ctx_rtu);
 72         switch(query[offset])
 73         {
 74             case 0x03:      /* 读保持寄存器 */
 75                 /* 如果接收到请求,将要发送的数据放入mb_mapping寄存器表中*/
 76                 for(quint8 i = 0; i < 10; i++)
 77                 {
 78                     mb_mapping->tab_registers[i] = data_buff[i];
 79                 }
 80                 /* 响应 */
 81                 modbus_reply(ctx_rtu, query, rc, mb_mapping);
 82                 break;
 83             case 0x06:      /* 写单个寄存器 */
 84                 /* 响应 */
 85                 modbus_reply(ctx_rtu, query, rc, mb_mapping);
 86                 data = mb_mapping->tab_registers[(query[offset + 1] << 8) + query[offset + 2]];
 87                 data_buff[(query[offset + 1] << 8) + query[offset + 2]] = data;
 88                 break;
 89             case 0x10:      /* 写多个寄存器 */
 90                 /* 响应 */
 91                 modbus_reply(ctx_rtu, query, rc, mb_mapping);
 92                 for(int i = 0; i < 10; i++)
 93                 {
 94                    data_buff[i] = mb_mapping->tab_registers[i];
 95                 }
 96                 break;
 97             default:
 98                 break;
 99         }
100     }
101 }
102 
103 /*******************************************************************************************
104 * 函数名称:modbus_rtu_init()
105 * 功能描述:modbus_rtu初始化函数
106 * 输入参数:无
107 * 返回参数:无
108 * 创建者:
109 * 创建日期:2020.3.23
110 *******************************************************************************************/
111 void RtuCommThread::modbus_rtu_init()
112 {
113     if(ctx_rtu != NULL)
114     {
115         modbus_close(ctx_rtu);
116         modbus_free(ctx_rtu);
117         ctx_rtu = NULL;
118     }
119     120     slave    = 1;
121     baud     = Baud[0];
122     parity   = Parity[0];
123     data_bit = DataBits[0];
124     stop_bit = StopBit[0];
125 
126 127 /* create a libmodbus context for RTU */ 128 ctx_rtu = modbus_new_rtu("/dev/ttySP2", baud, parity, data_bit, stop_bit); 129 /* set slave number in the context */ 130 modbus_set_slave(ctx_rtu, slave); 131 /* establish a Modbus connection */ 132 modbus_connect(ctx_rtu); 133 }

  rtucomthread.h文件中的内容:

 1 #ifndef RTUCOMMTHREAD_H
 2 #define RTUCOMMTHREAD_H
 3 
 4 #include <QThread>
 5 #include "libmodbus/include/modbus/modbus.h"
 6 
 7 class RtuCommThread : public QThread
 8 {
 9     Q_OBJECT
10 public:
11     explicit RtuCommThread(QObject *parent = 0);
12     ~RtuCommThread();
13 
14 public:
15     void modbus_rtu_init();
16 
17 private:
18     modbus_t *ctx_rtu;
19     int  Baud[5];
20     char Parity[2];
21     int  DataBits[2];
22     int  StopBit[2];
23 
24     int  slave;         //设备地址
25     int  baud;          //波特率
26     char parity;        //校验
27     int  data_bit;      //数据位
28     int  stop_bit;      //停止位
29 
30     modbus_mapping_t *mb_mapping;
31     uint8_t query[MODBUS_RTU_MAX_ADU_LENGTH];
32 
33     void run();
34 
35     
36 signals:
37     
38 public slots:
39     
40 };
41 
42 #endif // RTUCOMMTHREAD_H

 

  2、TCP slave

  使用的时候只需创建一个此线程的对象,初始化完成后打开线程即可。

  支持断线重连。

  tcpcomthread.cpp文件中的内容:

  1 #include "tcpcommthread.h"
  2 
  3 #include <sys/ioctl.h>
  4 #include <unistd.h>
  5 #include <netinet/in.h>
  6 #include <arpa/inet.h>
  7 #include <fcntl.h>
  8 
  9 uint16_t data_buff[256] = {1};
 10 
 11 TcpCommThread::TcpCommThread(QObject *parent) :
 12     QThread(parent),
 13     ctx_tcp(NULL),
 14     server_socket(-1),
 15     client_Socket(-1)
 16 {
 17     tv.tv_sec = 0;
 18     tv.tv_usec = 1*1000*1000;
 19 
 20     /* 分配寄存器位的数组大小为125 */
 21     mb_mapping = modbus_mapping_new(0, 0, MODBUS_MAX_READ_REGISTERS, 0);
 22 }
 23 
 24 TcpCommThread::~TcpCommThread()
 25 {
 26     /* 释放分配的数组单元 */
 27     modbus_mapping_free(mb_mapping);
 28 
 29     modbus_close(ctx_tcp);
 30     modbus_free(ctx_tcp);
 31     mb_mapping = NULL;
 32     ctx_tcp = NULL;
 33 }
 34 
 35 /*******************************************************************************************
 36 * 函数名称:run()
 37 * 功能描述:线程执行函数
 38 * 输入参数:无
 39 * 返回参数:无
 40 * 创建者:
 41 * 创建日期:2020.3.23
 42 *******************************************************************************************/
 43 void TcpCommThread::run()
 44 {
 45     int rc;
 46     int offset;
 47     uint16_t data;
 48 
 49     while(1)
 50     {
 51         while(client_Socket == -1)
 52         {
 53             /* 清空refset */
 54             FD_ZERO(&refset);
 55             FD_ZERO(&wrfset);
 56             /* 将套节字加入refset中 */
 57             FD_SET(server_socket, &refset);
 58             FD_SET(server_socket, &wrfset);
 59             /* 从refset查找就绪态的fd,最后一个参数是阻塞时长 */
 60             rc = select(server_socket+1, &refset, &wrfset, NULL, &tv);
 61             if(rc < 0)
 62             {
 63                 /* select超时返回0, 错误返回-1,查询到fd的总数 */
 64                 continue;
 65             }
 66             if(rc > 0)
 67             {
 68                 /* 判断server_socket是否在读就绪态 */
 69                 if(FD_ISSET(server_socket, &refset) || FD_ISSET(server_socket, &wrfset))
 70                 {
 71                     /*accept a new connection on a TCP Modbus socket (IPv4)*/
 72                     client_Socket = modbus_tcp_accept(ctx_tcp, &server_socket);
 73                 }
 74             }
 75         }
 76 
 77         /* 接收请求 */
 78         rc = modbus_receive(ctx_tcp, query);
 79         if(rc == -1)
 80         {
 81             msleep(100);
 82             client_Socket = -1;
 83             continue;
 84         }
 85         /* 得到头文件的长度 */
 86         offset = modbus_get_header_length(ctx_tcp);
 87         switch(query[offset])
 88         {
 89             case 0x03:      /* 读保持寄存器 */
 90                 /* 如果接收到请求,将要发送的数据放入mb_mapping寄存器表中*/
 91                 for(quint8 i = 0; i < 10; i++)
 92                 {
 93                     mb_mapping->tab_registers[i] = data_buff[i];
 94                 }
 95                 /* 响应 */
 96                 modbus_reply(ctx_tcp, query, rc, mb_mapping);
 97                 break;
 98             case 0x06:      /* 写单个寄存器 */
 99                 /* 响应 */
100                 modbus_reply(ctx_tcp, query, rc, mb_mapping);
101                 data = mb_mapping->tab_registers[(query[offset + 1] << 8) + query[offset + 2]];
102                 data_buff[(query[offset + 1] << 8) + query[offset + 2]] = data;
103                 break;
104             case 0x10:      /* 写多个寄存器 */
105                 /* 响应 */
106                 modbus_reply(ctx_tcp, query, rc, mb_mapping);
107                 for(int i = 0; i < 10; i++)
108                 {
109                    data_buff[i] = mb_mapping->tab_registers[i];
110                 }
111                 break;
112             default:
113                 break;
114         }
115     }
116 }
117 
118 /*******************************************************************************************
119 * 函数名称:modbus_tcp_init()
120 * 功能描述:modbus_tcp初始化
121 * 输入参数:无
122 * 返回参数:无
123 * 创建者:
124 * 创建日期:2020.3.23
125 *******************************************************************************************/
126 void TcpCommThread::modbus_tcp_init()
127 {
128     QString str = "wr ifconfig eth0 192.168.1.136";
129 
130     if(ctx_tcp != NULL)
131     {
132         if(server_socket != -1)
133         {
134             shutdown(server_socket, SHUT_RDWR);
135         }
136         modbus_close(ctx_tcp);
137         modbus_free(ctx_tcp);
138         ctx_tcp = NULL;
139     }
140 
141     142     port     = 502;
143     ip.sprintf("%d.%d.%d.%d",
144                192,
145                168,
146                1,
147                178);
148 149 
150     /* 设置CPU的IP地址 */
151     str.sprintf("wr ifconfig eth0 %s",ip.toLatin1().data());
152     system(str.toLatin1().data());
153     sleep(1);
154 
155     /* To listen any addresses on port 502 */
156     ctx_tcp = modbus_new_tcp(NULL, port);
157     /* create and listen a TCP Modbus socket */
158     server_socket = modbus_tcp_listen(ctx_tcp, MODBUS_TCP_CONNECT_MAX);
159     client_Socket = -1;
160 }

  tcpcomthread.h文件中的内容:

 1 #ifndef TCPCOMMTHREAD_H
 2 #define TCPCOMMTHREAD_H
 3 
 4 #include <QThread>
 5 #include "libmodbus/include/modbus/modbus.h"
 6 #include <sys/socket.h>
 7 
 8 class TcpCommThread : public QThread
 9 {
10 #define MODBUS_TCP_CONNECT_MAX  1
11 #define COMM_TYPE_PROFIBUS      0
12 #define COMM_TYPE_MODBUS_TCP    1
13 
14     Q_OBJECT
15 public:
16     explicit TcpCommThread(QObject *parent = 0);
17     ~TcpCommThread();
18 
19 public:
20     void modbus_tcp_init();
21 
22 private:
23     modbus_t *ctx_tcp;
24     modbus_mapping_t *mb_mapping;
25     uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
26     int server_socket;
27     int client_Socket;
28     fd_set refset;
29     fd_set wrfset;
30 
31     struct timeval tv;
32 
33     QString ip;         //ip
34     int     port;       //端口号
35 
36     void run();
37     
38 signals:
39     
40 public slots:
41     
42 };
43 
44 #endif // TCPCOMMTHREAD_H

 

 

end

posted @ 2020-07-03 14:07  不要让自己太懒  阅读(5246)  评论(0编辑  收藏  举报