一、介紹
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