在上一篇文章 《【瑞萨RA8D1 LVGL/LWIP评测】RA8D1部署FreeRTOS+LWIP》 中,我们基于瑞萨 CPKCOR-RA8D1 开发板部署了 FreeRTOS + LWIP 的代码,并进行了 DHCP 与 ICMP 协议的测试。本文我们将更进一步,对 TCP、UDP、HTTP、MQTT 等多个应用场景同时部署,进行联合测试。由于本文测试的是LWIP中间件的通用基础能力,代码同样适用于基于裸机、uCOS、RT-Thread等环境,有任一测试需求的伙伴们都可以参考。那么让我们立即发车~
一、基础环境准备硬件部分
CPKCOR-RA8D1 核心板
CPKEXP-EKRA8x1 扩展板
路由器 & 网线

软件部分
e²Studio 2025-12 (25.12.0)
Renesas FSP 6.3.0
J-Link RTT Viewer V8.92
请基于上篇文章部署好FreeRTOS+LWIP的基础环境。
由于我们本次将同时测试多个协议,请提前增加LWIP的堆栈与内存池资源,下面是测试可行的参考配置。

二、TCP echo服务器测试
测试程序
LWIP的TCP功能是默认开启的,因此我们无需额外配置,可以直接开始测试。
下面是测试用的TCP echo服务器测试代码,基于LWIP的Socket接口编写。
/*
* tcp_echo_server_thread.c
*/
#include <lwip_thread.h>
#include "common_utils.h"
#include "lwip/init.h"
#include "lwip/tcpip.h"
#include "lwip/tcp.h"
#include "lwip/timeouts.h"
#include "lwip/init.h"
#include "lwip/ip4.h"
#include "lwip/dhcp.h"
#include <lwip/sockets.h>
#define LOCAL_PORT 5001
#define RECV_DATA (1024)
static void tcpecho_thread(void *arg)
{
int sock = -1,connected;
char *recv_data;
struct sockaddr_in server_addr,client_addr;
socklen_t sin_size;
size_t recv_data_len;
(void)arg;
APP_PRINT("Local port: %d\n\n", LOCAL_PORT);
recv_data = (char *)pvPortMalloc(RECV_DATA);
if (recv_data == NULL)
{
APP_PRINT("No memory\n");
goto __exit;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
APP_PRINT("Socket error\n");
goto __exit;
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(LOCAL_PORT);
memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
APP_PRINT("Unable to bind\n");
goto __exit;
}
if (listen(sock, 5) == -1)
{
APP_PRINT("Listen error\n");
goto __exit;
}
while(1)
{
sin_size = sizeof(struct sockaddr_in);
connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);
APP_PRINT("new client connected from (%s, %d)\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
{
int flag = 1;
setsockopt(connected,
IPPROTO_TCP, /* set option at TCP level */
TCP_NODELAY, /* name of option */
(void *) &flag, /* the cast is historical cruft */
sizeof(int)); /* length of option value */
}
while(1)
{
recv_data_len = recv(connected, recv_data, RECV_DATA, 0);
if (recv_data_len <= 0)
break;
APP_PRINT("recv %d len data\n",recv_data_len);
write(connected, recv_data, recv_data_len);
}
if (connected >= 0)
closesocket(connected);
connected = -1;
}
__exit:
if (sock >= 0) closesocket(sock);
if (recv_data) free(recv_data);
}
/*-----------------------------------------------------------------------------------*/
void tcp_echo_server_init(void)
{
sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, 1024, 4);
}源文件添加完毕后,我们在LWIP初始化代码后加入 tcp_echo_server_init 。
void lwip_thread_entry(void *pvParameters)
{
...
LwIP_Init();
tcp_echo_server_init();
...
}测试效果代码烧录完毕后,我们使用网络调试助手连接开发板,并发送字符串 Hello Renesas! ,可以看到开发板运行的echo服务器成功回复了相同字符串,如图:

三、UDP echo服务器测试
测试程序
同样地,LWIP的UDP功能也是默认开启的,我们无需额外配置,直接开始测试。
下面是测试用的UDP echo服务器测试代码,基于LWIP的Socket接口编写。
/*
* udp_echo_server_thread.c
*/
#include "FreeRTOS.h"
#include "task.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#define UDP_ECHO_PORT 7 // 标准 Echo 端口
#define RECV_BUF_SIZE 1500 // 适配以太网 MTU
void udp_echo_thread(void *arg) {
int sock;
int err;
struct sockaddr_in server_addr, client_addr;
char *recv_buf;
socklen_t addr_len = sizeof(client_addr);
(void)arg;
// 1. 为接收缓冲区分配内存
recv_buf = (char *)pvPortMalloc(RECV_BUF_SIZE);
if (recv_buf == NULL) {
vTaskDelete(NULL);
return;
}
// 2. 创建 UDP Socket
// AF_INET: IPv4, SOCK_DGRAM: 数据报(UDP)
sock = lwip_socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
vPortFree(recv_buf);
vTaskDelete(NULL);
return;
}
// 3. 准备本地监听地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = lwip_htons(UDP_ECHO_PORT);
server_addr.sin_addr.s_addr = lwip_htonl(IPADDR_ANY); // 监听所有网卡 IP
// 4. 绑定端口
err = lwip_bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err < 0) {
lwip_close(sock);
vPortFree(recv_buf);
vTaskDelete(NULL);
return;
}
while (1) {
// 5. 等待接收数据
// recvfrom 是阻塞的,FreeRTOS 此时会挂起该任务不占用 CPU
int n = lwip_recvfrom(sock, recv_buf, RECV_BUF_SIZE, 0,
(struct sockaddr *)&client_addr, &addr_len);
if (n > 0) {
// 6. 将收到的数据原样发回给客户端
lwip_sendto(sock, recv_buf, (size_t)n, 0,
(struct sockaddr *)&client_addr, addr_len);
} else if (n < 0) {
// 处理错误,例如由于资源耗尽导致的接收失败
vTaskDelay(pdMS_TO_TICKS(10));
}
}
}
// 任务创建入口
void udp_echo_server_init(void) {
xTaskCreate(udp_echo_thread, "udp_echo", 1024 / sizeof(StackType_t), NULL, 4, NULL);
}源文件添加完毕后,我们同样在LWIP初始化代码后加入 udp_echo_server_init 。
void lwip_thread_entry(void *pvParameters)
{
...
LwIP_Init();
tcp_echo_server_init();
udp_echo_server_init();
...
}测试效果网络调试助手不支持作为udp客户端连接,我们使用 netcat 进行测试。
代码烧录完毕后,发送字符串 hello Renesas! ,可以看到开发板运行的UDP echo服务器成功回复了相同字符串,如图:

四、HTTP服务器测试
功能配置
LWIP的HTTP功能是属于附加APP,需要我们手动添加相应的功能。在Stack页面,添加Networking -> LwIP HTTP Server。框图如下:

配置好框图后,LWIP不需要额外增加业务代码。但要在初始化方法中加入httpd服务的初始化。
void lwip_thread_entry(void *pvParameters)
{
...
LwIP_Init();
tcp_echo_server_init();
udp_echo_server_init();
httpd_init();
...
}HTTP模块不需要修改配置项,使用默认配置即可。

官方会提供一个默认的页面。我们打开浏览器,直接访问开发板的IP地址,默认80端口不用填写。效果如下:

五、MQTT服务器测试
功能配置
LWIP的MQTT功能同样属于附加APP,需要我们手动添加相应的功能。在Stack页面,添加Networking -> LwIP MQTT Client。框图如下:

接下来我们添加MQTT客户端的测试代码,如下:
#include "bsp_api.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "hal_data.h"
#include "lwip/init.h"
#include "lwip/tcpip.h"
#include "lwip/tcp.h"
#include "lwip/timeouts.h"
#include "lwip/ip4.h"
#include "lwip/apps/mqtt.h"
#include "common_utils.h"
//#define HOST "broker.emqx.io"
#define PORT 1883
#define TOPIC "/test"
#define SIGNAL_CONNECTED 0x01
// 使用 FreeRTOS 原生句柄
static SemaphoreHandle_t publish_semaphore_id = NULL;
static TaskHandle_t mqtt_thread_id = NULL;
static ip_addr_t host_ip;
void start_mqtt_task(void *argument);
void mqtt_client_init(void) {
// FreeRTOS v11 任务创建
BaseType_t ret = xTaskCreate(start_mqtt_task,
"mqtt_task",
4096 / sizeof(StackType_t), // 瑞萨平台注意:此处通常为字数,RA8建议给足堆栈
NULL,
configMAX_PRIORITIES - 2, // 优先级调整为系统常用优先级
&mqtt_thread_id);
if (ret == pdTRUE) {
APP_PRINT("lwip mqtt init success...\n\n");
} else {
APP_PRINT("lwip mqtt init fail...\n\n");
}
}
static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
{
(void)client; (void)arg;
if(status == MQTT_CONNECT_ACCEPTED) {
APP_PRINT("MQTT connected\n");
// 使用任务通知代替 osSignalSet
if (mqtt_thread_id != NULL) {
xTaskNotify(mqtt_thread_id, SIGNAL_CONNECTED, eSetBits);
}
}
}
static void mqtt_pub_request_cb(void *arg, err_t result)
{
(void)arg; (void)result;
APP_PRINT("MQTT published\n");
// 释放计数信号量
if (publish_semaphore_id != NULL) {
xSemaphoreGive(publish_semaphore_id);
}
}
void start_mqtt_task(void *argument)
{
(void)argument;
uint32_t notified_value;
mqtt_client_t *client = mqtt_client_new();
struct mqtt_connect_client_info_t ci;
memset(&ci, 0, sizeof(ci));
ci.client_id = "lwip_mqtt_test";
// 创建计数信号量:最大值 MQTT_REQ_MAX_IN_FLIGHT,初始值相同
publish_semaphore_id = xSemaphoreCreateCounting(MQTT_REQ_MAX_IN_FLIGHT, MQTT_REQ_MAX_IN_FLIGHT);
IP4_ADDR(&host_ip, 192, 168, 1, 9);
// 假设此处已通过 DNS 获取了 host_ip,或手动填入 IP
mqtt_client_connect(client, &host_ip, PORT, mqtt_connection_cb, NULL, &ci);
char payload[100];
u8_t qos = 1;
u8_t retain = 0;
// 等待任务通知
// 参数:进入前不清除位,退出时清除所有位,指向通知值的指针,永久等待
if (xTaskNotifyWait(0, 0xFFFFFFFF, ¬ified_value, portMAX_DELAY) == pdTRUE) {
if (notified_value & SIGNAL_CONNECTED) {
for(int i = 0; ; i++) {
// 获取信号量 (代替 osSemaphoreWait)
if (xSemaphoreTake(publish_semaphore_id, portMAX_DELAY) == pdPASS) {
snprintf(payload, sizeof(payload), "Hello Renesas! cnt:%d", i);
mqtt_publish(client, TOPIC, payload, (u16_t)strlen(payload), qos, retain, mqtt_pub_request_cb, NULL);
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
}
}最后在LWIP初始化方法中加入MQTT服务的初始化。
void lwip_thread_entry(void *pvParameters)
{
...
LwIP_Init();
tcp_echo_server_init();
udp_echo_server_init();
httpd_init();
mqtt_client_init();
...
}测试效果首先启动 MQTT 服务器。

随后连接开发板,打开客户端软件 MQTTX,可以看到开发板每秒发布一次的 Hello Renesas! 消息:



至此,我们已经在 RA8D1 上同时运行了 TCP、UDP、HTTP、MQTT 多个应用。看起来很繁重的样子,但实际上这对于 RA8D1 强大的 480MHz Cortex-M85 核心来说简直小菜一碟,内存也仅仅占用了28%。DTCM、ITCM 和 32M SDRAM 还未投入使用。我后续会联合LVGL和LWIP编写一个电脑副屏项目,敬请期待~
本文工程源码:
lwip_rtos-udp+tcp+http+mqtt.zip
我要赚赏金
