最近在项目中需要用到modbus TCP通信协议,Qt自带的modbus只能一对一通信,不能做到1对多,所以选择开元库libmodbus
libmodbus协议介绍中文完整带书签版:https://download.csdn.net/download/u011251940/11341158
下载libmodbus源码包:https://www.libmodbus.org/releases/libmodbus-3.0.6.tar.gz
libmodbus使用手册:https://libmodbus.org/docs/v3.0.6/
libmodbus调试工具:https://download.csdn.net/download/u011251940/11341326
安装嵌入式arm版本
tar -zxvf libmodbus-3.0.6.tar.gz
cd libmodbus-3.0.6
./configure --build=i686 --host=arm-cortexa9-linux-gnueabihf --prefix=/opt/libmodbus/install
make -j8;make install
一、简单写一个libmodbus TCP通信demo测试
#include
#include
#include
#include
#include
#include
#include "modbus/modbus.h"
#include "modbus/modbus-tcp.h"
#include "modbus/modbus-version.h"
//线圈地址数据的定义
const uint16_t UT_BITS_ADDRESS = 0x13;
const uint16_t UT_BITS_NB = 0x25;
const uint8_t UT_BITS_TAB[] = { 0xCD, 0x6B, 0xB2, 0x0E, 0x1B };
//离散量输入寄存器地址数据的定义
const uint16_t UT_INPUT_BITS_ADDRESS = 0xC4;
const uint16_t UT_INPUT_BITS_NB = 0x16;
const uint8_t UT_INPUT_BITS_TAB[] = { 0xAC, 0xDB, 0x35 };
//读保持寄存器地址数据的定义
const uint16_t UT_REGISTERS_ADDRESS = 0x6B;
/* Raise a manual exception when this adress is used for the first byte */
const uint16_t UT_REGISTERS_ADDRESS_SPECIAL = 0x6C;
const uint16_t UT_REGISTERS_NB = 0x3;
const uint16_t UT_REGISTERS_TAB[] = { 0x022B, 0x0001, 0x0064 };
/* If the following value is used, a bad response is sent.
It's better to test with a lower value than
UT_REGISTERS_NB_POINTS to try to raise a segfault. */
const uint16_t UT_REGISTERS_NB_SPECIAL = 0x2;
//输入寄存器地址数据定义
const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x08;
const uint16_t UT_INPUT_REGISTERS_NB = 0x1;
const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A };
int main(int argc, char*argv[])
{
int socket;
modbus_t *ctx;
modbus_mapping_t *mb_mapping;
int rc;
int i;
uint8_t *query;
int header_length;
int data;
int address;
ctx = modbus_new_tcp("127.0.0.1", 502);
query = malloc(MODBUS_TCP_MAX_ADU_LENGTH);
header_length = modbus_get_header_length(ctx);
modbus_set_debug(ctx, TRUE);
//new一个modbus的映射空间
mb_mapping = modbus_mapping_new(
UT_BITS_ADDRESS + UT_BITS_NB,//读线圈
UT_INPUT_BITS_ADDRESS + UT_INPUT_BITS_NB,//读离散量输入
UT_REGISTERS_ADDRESS + UT_REGISTERS_NB,//读保持寄存器
UT_INPUT_REGISTERS_ADDRESS + UT_INPUT_REGISTERS_NB);//读输入寄存器
if (mb_mapping == NULL)
{
fprintf(stderr, "Failed to allocate the mapping: %s\n",
modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
/** 初始化离散量输入寄存器 **/
modbus_set_bits_from_bytes(mb_mapping->tab_input_bits,UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB,UT_INPUT_BITS_TAB);
/** 初始化输入寄存器 **/
for (i=0; i < UT_INPUT_REGISTERS_NB; i++)
{
mb_mapping->tab_input_registers[UT_INPUT_REGISTERS_ADDRESS+i] =UT_INPUT_REGISTERS_TAB[i];
}
/**初始化读保持寄存器**/
for(i=0; itab_registers[UT_REGISTERS_ADDRESS+i]=UT_REGISTERS_TAB[i];
}
socket = modbus_tcp_listen(ctx, 1);//监听端口
modbus_tcp_accept(ctx, &socket);//连接阻塞
for (;;)
{
//接收modbus的请求报文
rc = modbus_receive(ctx, query);
if (rc == -1)
{
/* Connection closed by the client or error */
break;
}
address= (query[header_length+1]<<8) + query[header_length+2];//地址
data=(query[header_length+3]<<8)+query[header_length+4];//数据
/* 功能码 */
if (query[header_length] == 0x06)
{
//to do 写保持寄存器数据
}
//回复modbus请求的数据
rc = modbus_reply(ctx, query, rc, mb_mapping);
if (rc == -1) {
break;
}
}
printf("Quit the loop: %s\n", modbus_strerror(errno));
close(socket);
modbus_mapping_free(mb_mapping);
free(query);
modbus_free(ctx);
return 0;
}
这块代码实测基本可以和客户端连接通信,但是断网重连实现不了。
二、断网重连机制部分
#include
#include
#include
#include
#include
#include
#include "modbus.h"
#include "modbus-tcp.h"
#include "modbus-version.h"
#if defined(_WIN32)
#include
#else
#include
#include
#include
#endif
#define NB_CONNECTION 5
//线圈地址数据的定义
const uint16_t UT_BITS_ADDRESS = 0x13;
const uint16_t UT_BITS_NB = 0x25;
const uint8_t UT_BITS_TAB[] = { 0xCD, 0x6B, 0xB2, 0x0E, 0x1B };
//离散量输入寄存器地址数据的定义
const uint16_t UT_INPUT_BITS_ADDRESS = 0xC4;
const uint16_t UT_INPUT_BITS_NB = 0x16;
const uint8_t UT_INPUT_BITS_TAB[] = { 0xAC, 0xDB, 0x35 };
//读保持寄存器地址数据的定义
const uint16_t UT_REGISTERS_ADDRESS = 0x6B;
/* Raise a manual exception when this adress is used for the first byte */
const uint16_t UT_REGISTERS_ADDRESS_SPECIAL = 0x6C;
const uint16_t UT_REGISTERS_NB = 0x3;
const uint16_t UT_REGISTERS_TAB[] = { 0x022B, 0x0001, 0x0064 };
/* If the following value is used, a bad response is sent.
It's better to test with a lower value than
UT_REGISTERS_NB_POINTS to try to raise a segfault. */
const uint16_t UT_REGISTERS_NB_SPECIAL = 0x2;
//输入寄存器地址数据定义
const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x08;
const uint16_t UT_INPUT_REGISTERS_NB = 0x1;
const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A };
modbus_t *ctx = NULL;
int server_socket;
modbus_mapping_t *mb_mapping;
static void close_sigint(int dummy)
{
close(server_socket);
modbus_free(ctx);
modbus_mapping_free(mb_mapping);
exit(dummy);
}
int main(void)
{
int master_socket;
int rc;
fd_set refset;
fd_set rdset;
/* Maximum file descriptor number */
int fdmax;
ctx = modbus_new_tcp("127.0.0.1", 502);
//new一个modbus的映射空间
mb_mapping = modbus_mapping_new(
UT_BITS_ADDRESS + UT_BITS_NB,//读线圈
UT_INPUT_BITS_ADDRESS + UT_INPUT_BITS_NB,//读离散量输入
UT_REGISTERS_ADDRESS + UT_REGISTERS_NB,//读保持寄存器
UT_INPUT_REGISTERS_ADDRESS + UT_INPUT_REGISTERS_NB);//读输入寄存器
if (mb_mapping == NULL)
{
fprintf(stderr, "Failed to allocate the mapping: %s\n",
modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
/** 初始化离散量输入寄存器 **/
modbus_set_bits_from_bytes(mb_mapping->tab_input_bits,UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB,UT_INPUT_BITS_TAB);
/** 初始化输入寄存器 **/
for (i=0; i < UT_INPUT_REGISTERS_NB; i++)
{
mb_mapping->tab_input_registers[UT_INPUT_REGISTERS_ADDRESS+i] =UT_INPUT_REGISTERS_TAB[i];
}
/**初始化读保持寄存器**/
for(i=0; itab_registers[UT_REGISTERS_ADDRESS+i]=UT_REGISTERS_TAB[i];
}
server_socket = modbus_tcp_listen(ctx, NB_CONNECTION);
signal(SIGINT, close_sigint);
/* Clear the reference set of socket */
FD_ZERO(&refset);
/* Add the server socket */
FD_SET(server_socket, &refset);
/* Keep track of the max file descriptor */
fdmax = server_socket;
for (;;)
{
rdset = refset;
if (select(fdmax+1, &rdset, NULL, NULL, NULL) == -1)
{
perror("Server select() failure.");
close_sigint(1);
}
/* Run through the existing connections looking for data to be
* read */
for (master_socket = 0; master_socket <= fdmax; master_socket++)
{
if (FD_ISSET(master_socket, &rdset))
{
if (master_socket == server_socket)
{
/* A client is asking a new connection */
socklen_t addrlen;
struct sockaddr_in clientaddr;
int newfd;
/* Handle new connections */
addrlen = sizeof(clientaddr);
memset(&clientaddr, 0, sizeof(clientaddr));
newfd = accept(server_socket, (struct sockaddr *)&clientaddr, &addrlen);
if (newfd == -1)
{
perror("Server accept() error");
}
else
{
FD_SET(newfd, &refset);
if (newfd > fdmax)
{
/* Keep track of the maximum */
fdmax = newfd;
}
printf("New connection from %s:%d on socket %d\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port, newfd);
}
}
else
{
/* An already connected master has sent a new query */
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
int data = 0;
int address=0;
modbus_set_socket(ctx, master_socket);
rc = modbus_receive(ctx, query);
address = (query[header_length+1]<<8) + query[header_length+2];
if(query[header_length] == 0x06 )
{
//to do 功能码 写保持寄存器
}
if (rc != -1)
{
modbus_reply(ctx, query, rc, mb_mapping);
}
else
{
/* Connection closed by the client, end of server */
printf("Connection closed on socket %d\n", master_socket);
close(master_socket);
/* Remove from reference set */
FD_CLR(master_socket, &refset);
if (master_socket == fdmax)
{
fdmax--;
}
}
}
}
}
}
return 0;
}
断网重连机制实测有效