前言
- 使用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 部分源码分析记录