当前位置:首页 > 通信技术 > 通信技术
[导读]在嵌入式Linux/RTOS设备联调中,常需一个轻量网络调试助手——既能发RAW TCP/UDP包、又能做简单HTTP查看,以验证PHY、交换机或云端连通性。lwIP(lightweight IP stack) 以其极小footprint(~40KB ROM/10KB RAM)成为STM32/RT-Thread/NXP平台首选。本文以STM32F407 + lwIP RAW API为例,实现TCP Echo Server + UDP Send/Recv + 简易HTTP状态页。



嵌入式Linux/RTOS设备联调中,常需一个轻量网络调试助手——既能发RAW TCP/UDP包、又能做简单HTTP查看,以验证PHY、交换机或云端连通性。lwIP(lightweight IP stack) 以其极小footprint(~40KB ROM/10KB RAM)成为STM32/RT-Thread/NXP平台首选。本文以STM32F407 + lwIP RAW API为例,实现TCP Echo Server + UDP Send/Recv + 简易HTTP状态页。


一、lwIP移植与基础初始化(简述)


假定你已完成:

• ethernetif.c 对接 HAL_ETH_ReadFrame / HAL_ETH_Transmit


• lwip_init() + netif_add(..., &ethernetif_init) + dhcp_start() 或静态IP


关键片段(main中):

struct netif g_netif;

ip4_addr_t ipaddr, netmask, gw;


IP4_ADDR(&ipaddr,   192,168,1,100);

IP4_ADDR(&netmask, 255,255,255,0);

IP4_ADDR(&gw,      192,168,1,1);


netif_add(&g_netif, &ipaddr, &netmask, &gw,

         NULL, ethernetif_init, ethernet_input);

netif_set_default(&g_netif);

netif_set_up(&g_netif);

dhcp_start(&g_netif);   // 或 static 用 netif_set_addr()


// 周期性调用(通常在sys_check_timeouts或RTOS任务)

sys_check_timeouts();



二、TCP Echo Server(RAW API)


lwIP RAW API无需socket层,适合资源紧张场景。

#include "lwip/tcp.h"


/* 接受新连接回调 */

static err_t echo_accept(void *arg, struct tcp_pcb *newpcb, err_t err)

{

   LWIP_UNUSED(arg);

   if (err != ERR_OK || newpcb == NULL)

       return ERR_VAL;


   tcp_arg(newpcb, NULL);

   tcp_recv(newpcb, echo_recv);     // 注册接收回调

   tcp_err(newpcb, NULL);

   tcp_poll(newpcb, NULL, 4);       // 空闲轮询间隔

   return ERR_OK;

}


/* 数据接收回调 —— 原样回显 */

static err_t echo_recv(void *arg, struct tcp_pcb *tpcb,

                       struct pbuf *p, err_t err)

{

   if (p == NULL) {                 // 客户端关闭

       tcp_close(tpcb);

       return ERR_OK;

   }


   /* 回显:不拷贝,直接引用pbuf(需置置PBUF_FLAG_REF)*/

   tcp_write(tpcb, p->payload, p->tot_len, TCP_WRITE_FLAG_COPY);

   tcp_output(tpcb);

   pbuf_free(p);

   return ERR_OK;

}


/* 启动TCP Echo(端口7或用户定义) */

void start_tcp_echo(uint16_t port)

{

   struct tcp_pcb *pcb = tcp_new();

   if (!pcb) return;

   tcp_bind(pcb, IP_ADDR_ANY, port);

   pcb = tcp_listen(pcb);

   tcp_accept(pcb, echo_accept);

}



在main()初始化后调用 start_tcp_echo(2323);,PC端用nc 192.168.1.100 2323即可测试Echo。


三、UDP收发(调试发包与抓包)


UDP更简单,常用于发送传感器快照或接收广播命令。

#include "lwip/udp.h"


static void udp_recv_cb(void *arg, struct udp_pcb *pcb,

                        struct pbuf *p, const ip_addr_t *addr, u16_t port)

{

   if (p) {

       // 简单打印长度(实际可解析协议)

       printf("[UDP] from %d.%d.%d.%d:%d len=%d\n",

              ip4_addr1(addr),ip4_addr2(addr),ip4_addr3(addr),ip4_addr4(addr),

              port, p->tot_len);

       pbuf_free(p);

   }

}


void start_udp_debug(uint16_t port)

{

   struct udp_pcb *upcb = udp_new();

   udp_bind(upcb, IP_ADDR_ANY, port);

   udp_recv(upcb, udp_recv_cb, NULL);

}


/* 主动发送UDP(例:心跳包) */

void udp_send_heartbeat(const ip_addr_t *dst, uint16_t dport)

{

   struct udp_pcb *pcb = udp_new();

   struct pbuf *pb = pbuf_alloc(PBUF_TRANSPORT, 4, PBUF_RAM);

   if (!pb) { udp_remove(pcb); return; }

   ((uint32_t*)pb->payload)[0] = (uint32_t)HAL_GetTick();

   udp_sendto(pcb, pb, dst, dport);

   pbuf_free(pb);

   udp_remove(pcb);

}



四、简易HTTP状态页(验证Web栈)


仅返回动态拼接的文本/HTML,展示IP、MAC、 uptime——用于现场浏览器确认网络存活。

static err_t http_recv(void *arg, struct tcp_pcb *tpcb,

                      struct pbuf *p, err_t err)

{

   if (!p) { tcp_close(tpcb); return ERR_OK; }


   char html[256];

   uint32_t up = HAL_GetTick()/1000;

   snprintf(html, sizeof(html),

       "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"

       "<h2>Dev Info</h2>"

       "IP: 192.168.1.100<br>"

       "Uptime: %lus<br>"

       "<a href=\"/refresh\">Refresh</a>", up);


   tcp_write(tpcb, html, strlen(html), TCP_WRITE_FLAG_COPY);

   tcp_output(tpcb);

   pbuf_free(p);

   return ERR_OK;

}


void start_http_status(uint16_t port)

{

   struct tcp_pcb *p = tcp_new();

   tcp_bind(p, IP_ADDR_ANY, port);

   p = tcp_listen(p);

   tcp_accept(p, [](void*, struct tcp_pcb *n, err_t e){

       tcp_recv(n, http_recv); return ERR_OK;

   });

}



浏览器访问 http://192.168.1.100:80 即看到设备状态。


五、调试技巧与常见坑


现象 原因 解决


能Ping不通TCP ethernetif_input()未周期性调用或中断未连接 确认 HAL_ETH_GetRxDataLength + low_level_input 正常


TCP偶发断连 sys_check_timeouts()调用周期 > 250ms 放RTOS任务或SysTick 10ms周期调


UDP sendto返回 -1 目标IP未ARP解析完 先ping目标或等arp表填充


HTTP响应乱码 Content-Length缺失但浏览器宽容;建议加Content-Length 计算strlen再发


DHCP拿不到IP PHY未Link Up或MAC地址全0 检查 phy_link_status(), 设唯一MAC


六、结语


基于lwIP打造嵌入式网络调试工具,核心是正确周期调用sys_check_timeouts() + RAW API回调注册。上述TCP Echo/UDP/HTTP三合一固件只需额外~2KB RAM,却能在现场快速验证物理链路、IP配置及防火墙策略,是嵌入式网络开发的“瑞士军刀”。


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
换一批
延伸阅读

在智能硬件遍地开花的今天,物联网、嵌入式、单片机这三个高频出现的技术名词,常常让不少入门学习者混淆边界。很多人会简单把它们等同起来,觉得“做物联网就是写单片机代码”,但实际深入行业就会发现,三者是一套从底层硬件到上层应用...

关键字: 物联网 嵌入式

随着消费电子、可穿戴设备、微型物联网终端及高密度嵌入式系统向小型化、集成化迭代,空间受限设计已成为硬件研发的主流场景。这类设计的核心矛盾集中在有限物理体积与高集成、高性能、低功耗需求的冲突,传统功率控制方案依赖大体积散热...

关键字: 物联网 嵌入式 电源模块

在嵌入式开发的日常工作里,几乎每个工程师都曾和串口通信打过无数交道。当系统需要频繁输出传感器数据、调试日志或者控制指令时,大家第一反应往往是把阻塞式的查询发送换成DMA传输——毕竟所有人都知道,DMA能把CPU从逐字节搬...

关键字: 嵌入式 串口

在嵌入式产品开发中,兼容性问题是最容易被忽视却影响深远的“隐形陷阱”:同一套软件在首批芯片上运行正常,更换批次就出现不定期死机;在开发板上调试完美,换到量产PCB就功能异常;用A编译器编译运行稳定,升级编译器版本就出现启...

关键字: 嵌入式 兼容性

在嵌入式开发领域,有一句老生常谈的话:“内存玩得转,开发一半顺”。和PC端开发不同,嵌入式系统的RAM资源往往极其紧缺——很多MCU的RAM大小只有几KB到几十KB,高端嵌入式芯片也不过几百MB,远不如PC动辄几个GB的...

关键字: 嵌入式 内存

康佳特嵌入式模块与软件技术栈开发与支持流程已获认证

关键字: 嵌入式 自动化 机器人

在嵌入式开发领域,C语言始终是绝对的主流,而指针则是C语言最核心、最灵活也最容易踩坑的特性。对于嵌入式开发来说,我们需要直接操作硬件寄存器、管理内存缓冲区、处理网络数据包、回调驱动事件,几乎所有核心功能都离不开指针。而随...

关键字: C语言 嵌入式

在STM32嵌入式开发中,精确延时是非常基础但又极其关键的功能。无论是驱动单总线传感器(比如DS18B20)、控制LCD屏幕时序、还是生成精确的脉冲信号,都需要用到微秒级甚至纳秒级精度的延时。很多新手刚开始使用STM32...

关键字: STM32 嵌入式

随着半导体测试向更高复杂性与并行度演进,多工位自动测试设备(ATE)和SiC/GaN测试对电感、电容和电阻(LCR)测量的需求不断提升。然而,传统的外接台式LCR仪表和基于线缆的设置难以扩展,而且会降低可重复性。本文介绍...

关键字: 半导体 电阻 嵌入式

智能高尔夫球追踪系统是一项创新的嵌入式电子项目,旨在展示如何将紧凑型物联网硬件集成到体育科技应用中。在体育领域,高尔夫球扮演着主要角色,但在现代时代,所有设备都变得更加智能化,高尔夫球也由此演变为智能高尔夫球。本项目结合...

关键字: 嵌入式 物联网 NRF无线技术
关闭