前言
- 使用freemodbus软件包
- 使用网口通讯(sal+lwip)
- ip地址使用dhcp动态获取
软件包
相关宏定义
/*-----------------------------------------NET 宏定义-------------------------------------------*/#define RT_USING_SAL
#define SAL_INTERNET_CHECK
/* Docking with protocol stacks */
#define SAL_USING_LWIP
//#define SAL_USING_TLS
/* end of Docking with protocol stacks */
#define SAL_SOCKETS_NUM 16
#define RT_USING_NETDEV
#define NETDEV_USING_IFCONFIG
#define NETDEV_USING_PING
#define NETDEV_USING_NETSTAT
#define NETDEV_USING_AUTO_DEFAULT
#define NETDEV_IPV4 1
#define NETDEV_IPV6 0
#define RT_USING_LWIP
#define RT_USING_LWIP203
#define RT_USING_LWIP_VER_NUM 0x20003
#define RT_LWIP_MEM_ALIGNMENT 4
#define RT_LWIP_IGMP
#define RT_LWIP_ICMP
#define RT_LWIP_DNS
#define RT_LWIP_DHCP
/* Static IPv4 Address */#define RT_LWIP_IPADDR "192.168.1.30"
#define RT_LWIP_GWADDR "192.168.1.1"
#define RT_LWIP_MSKADDR "255.255.255.0"
/* end of Static IPv4 Address */
#define RT_LWIP_UDP
#define RT_LWIP_TCP
#define RT_LWIP_RAW
#define RT_MEMP_NUM_NETCONN 8
#define RT_LWIP_PBUF_NUM 16
#define RT_LWIP_RAW_PCB_NUM 4
#define RT_LWIP_UDP_PCB_NUM 4
#define RT_LWIP_TCP_PCB_NUM 4
#define RT_LWIP_TCP_SEG_NUM 40
#define RT_LWIP_TCP_SND_BUF 8196
#define RT_LWIP_TCP_WND 8196
#define RT_LWIP_TCPTHREAD_PRIORITY 10
#define RT_LWIP_TCPTHREAD_MBOX_SIZE 8
#define RT_LWIP_TCPTHREAD_STACKSIZE 1024
#define RT_LWIP_ETHTHREAD_PRIORITY 12
#define RT_LWIP_ETHTHREAD_STACKSIZE 1024
#define RT_LWIP_ETHTHREAD_MBOX_SIZE 8
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_NETIF_LINK_CALLBACK 1
#define SO_REUSE 1
#define LWIP_SO_RCVTIMEO 1
#define LWIP_SO_SNDTIMEO 1
#define LWIP_SO_RCVBUF 1
#define LWIP_SO_LINGER 0
#define LWIP_NETIF_LOOPBACK 0
#define RT_LWIP_USING_PING
/*-----------------------------------------modbus协议----------------------------------------------*/
#define PKG_MODBUS_SLAVE_TCP
#define RT_S_DISCRETE_INPUT_START 0x0
#define RT_S_DISCRETE_INPUT_NDISCRETES 10
#define RT_S_COIL_START 0x0
#define RT_S_COIL_NCOILS 10
#define RT_S_REG_INPUT_START 0x0
#define RT_S_REG_INPUT_NREGS 10
#define RT_S_REG_HOLDING_START 0x0
#define RT_S_REG_HOLDING_NREGS 100
modbus 软件包tcp接口的实现
porttcp的实现
#include "port.h"#ifdef PKG_MODBUS_SLAVE_TCP
/* ----------------------- Modbus includes ----------------------------------*/
#include "mbport.h"
#include "lwip/sockets.h"#define DBG_ENABLE
#define DBG_TAG "app.server"
#define DBG_LVL DBG_LOG#include "rtdbg.h"
/* ----------------------- Defines -----------------------------------------*/
#define MB_TCP_DEFAULT_PORT 502
#define MB_TCP_BUF_SIZE ( 256 + 7 )
#define MB_TCP_CLIENT_TIMEOUT (30*RT_TICK_PER_SECOND) /*超时时间30s*/
/* ----------------------- Static variables ---------------------------------*/
/*** @var local_sock 本地套接字描述符*/
int local_sock = -1;
/*** @var remote_sock 远端套接字描述符*/
int remote_sock = -1;
/*** @var local_listen_port 监听端口号,默认是502*/
static rt_uint16_t local_listen_port = MB_TCP_DEFAULT_PORT;
/*** @var mb_thread 线程指针*/
static rt_thread_t mb_thread = RT_NULL;
/*** @var local_addr 本地套接字地址结构体*/
static struct sockaddr_in local_addr;
/*** @var client_addr 客户端套接字结构体*/
static struct sockaddr_in client_addr;
/*** @var prvvTCPBuf 接收数据缓存区*/
static UCHAR prvvTCPBuf[MB_TCP_BUF_SIZE];
/*** @var prvvTCPLength 接收数据长度*/
static USHORT prvvTCPLength;static void lwip_server_entry(void *parameter) {static rt_tick_t recv_tick = 0;int result;socklen_t sin_size;begin:local_sock = socket(AF_INET, SOCK_STREAM, 0);if (local_sock < 0) {LOG_E("Socket error\n");goto __exit;}local_addr.sin_family = AF_INET;local_addr.sin_addr.s_addr = INADDR_ANY;local_addr.sin_port = htons(local_listen_port);memset(&(local_addr.sin_zero), 0, sizeof(local_addr.sin_zero));if (bind(local_sock, (struct sockaddr *) &local_addr, sizeof(struct sockaddr)) == -1) {LOG_E("Unable to bind\n");goto __exit;}if (listen(local_sock, 5) == -1) {LOG_E("Listen error\n");goto __exit;}while (1) {sin_size = sizeof(struct sockaddr_in);remote_sock = accept(local_sock, (struct sockaddr *) &client_addr, &sin_size);LOG_I("new client connected from (%s, %d)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));{int flag = 1;// 不使用Nagle算法// Nagle算法的主要目的是为了预防小分组的产生,因为在广域网中,小分组会造成网络拥塞。setsockopt(remote_sock, IPPROTO_TCP, TCP_NODELAY, (void *) &flag, sizeof(int));flag = 1;ioctlsocket(remote_sock, FIONBIO, &flag); /*配置成非阻塞模式*/}recv_tick = rt_tick_get();while (1) {result = recv(remote_sock, &prvvTCPBuf, MB_TCP_BUF_SIZE, 0);if (result == 0) { /*表示连接关闭*/LOG_I("client(%s) disconnected", inet_ntoa(client_addr.sin_addr));break;} else if (result == -1) { /*表示未接收到数据*/if (abs((int) (rt_tick_get() - recv_tick)) > MB_TCP_CLIENT_TIMEOUT) /* set timeout x */{LOG_D("timeout close client(%s) connect", inet_ntoa(client_addr.sin_addr));break; /*超时跳出循环*/}rt_thread_delay(20);} else if (result > 0) {LOG_D("recv %d len data\n", result);prvvTCPLength = result;xMBPortEventPost(EV_FRAME_RECEIVED);} else {LOG_E("unknown error:%d", result);break;}/*todo 判断是否网线拔掉的情况?*/}if (remote_sock >= 0) {closesocket(remote_sock);remote_sock = -1; /*重置为-1*/}}__exit:if (local_sock >= 0) closesocket(local_sock);rt_thread_mdelay(1000);/*等待1ms*/goto begin;}BOOL
xMBTCPPortInit(USHORT usTCPPort)
{if (usTCPPort == 0)usTCPPort = MB_TCP_DEFAULT_PORT;local_listen_port = usTCPPort;if (mb_thread == NULL) {/*创建任务*/mb_thread = rt_thread_create("modbus_thread",lwip_server_entry,RT_NULL,1024,15, 10);if (mb_thread) {rt_thread_startup(mb_thread);} else {LOG_E("modbus thread create error");return FALSE;}}return TRUE;
}void
vMBTCPPortClose(void)
{closesocket(local_sock);
}void
vMBTCPPortDisable(void)
{closesocket(remote_sock);remote_sock = -1;
}BOOL
xMBTCPPortGetRequest(UCHAR **ppucMBTCPFrame, USHORT *usTCPLength)
{*ppucMBTCPFrame = &prvvTCPBuf[0];*usTCPLength = prvvTCPLength;return TRUE;
}BOOL
xMBTCPPortSendResponse(const UCHAR *pucMBTCPFrame, USHORT usTCPLength)
{rt_int16_t ret;BOOL bFrameSent = FALSE;/*如果远端*/if (remote_sock > 0){ret = send(remote_sock, (void *) pucMBTCPFrame, usTCPLength, 0);if (ret == usTCPLength)bFrameSent = TRUE;}return bFrameSent;
}
#endif
modbus线程的编写
//
// Created by shchl on 2024/3/17.
//
#include "app_config.h"
#include "sal_socket.h"
#include "netdev.h"
#include "mb.h"#define DBG_ENABLE
#define DBG_TAG "mb.server"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>#define MB_POLL_THREAD_PRIORITY 10
#define MB_POLL_CYCLE_MS 200
static struct netdev *netdev;static void mb_slave_poll(void *parameter) {netdev = netdev_get_by_name("e0");if (netdev == NULL) {LOG_E("get netdev failed");}/*等待ip地址和网关配置完成*/while (netdev->ip_addr.addr == 0x0 || netdev->gw.addr == 0x0) {LOG_W("netdev is not config finished,waiting 1 s");rt_thread_mdelay(1000);}eMBTCPInit(0); /*使用默认端口503*/eMBEnable();while (1) {eMBPoll();rt_thread_mdelay(MB_POLL_CYCLE_MS);}
}static int mb_slave_tcp() {static rt_uint8_t is_init = 0;rt_thread_t tid1 = RT_NULL;tid1 = rt_thread_create("mb_slave_tcp", mb_slave_poll, NULL, 1024, MB_POLL_THREAD_PRIORITY, 10);if (tid1 != RT_NULL) {rt_thread_startup(tid1);} else {}return RT_EOK;}INIT_APP_EXPORT(mb_slave_tcp);
modbus tcp 部分源码分析记录