ESP32-P4 NuttX 以太网测试完整指南:从环境配置到网络验证
文章目录
前言
ESP32-P4 是乐鑫推出的高性能 RISC-V SoC,内置以太网 MAC(EMAC),支持有线网络连接。本文记录在 Windows 环境下,基于 Apache NuttX 实时操作系统,完成 ESP32-P4-Function-Ev-Board 以太网功能的编译、烧录和测试全过程。
最终效果:ESP32-P4 通过网线连接路由器,自动获取 IP,成功 Ping 通外网,并完成 TCP 连接测试。
1. 硬件准备
- ESP32-P4-Function-Ev-Board 1.6
- USB 数据线(用于烧录和串口调试)
- 网线(连接路由器或交换机)
- 电源(USB 供电即可)
接线说明
ESP32-P4-Function-Ev-Board 自带以太网 RJ45 接口,只需插上网线即可。USB 口同时用于烧录和串口控制台(USB CDC-ACM)。
2. 开发环境概述
本文复用已有的 ESP-IDF 工具链,只补齐 NuttX 编译所需的额外组件。
工具链版本
| 工具 | 版本 | 路径 |
|---|---|---|
| RISC-V GCC | 15.2.0 | C:\Espressif\tools\riscv32-esp-elf\esp-15.2.0_20251204 |
| CMake | 4.0.3 | C:\Espressif\tools\cmake\4.0.3 |
| Ninja | 1.12.1 | C:\Espressif\tools\ninja\1.12.1 |
| esptool | 5.3.0 | C:\Espressif\tools\python\master\venv\Scripts |
| Git Bash | — | C:\Program Files\Git |
NuttX 源码
cd D:\esp32p4-nuttx
git clone --depth 1 https://github.com/apache/nuttx.git nuttx
git clone --depth 1 https://github.com/apache/nuttx-apps.git apps
3. 关键配置步骤
3.1 Python 虚拟环境
NuttX 的 Kconfig 解析需要 kconfiglib,建议单独创建 venv:
cd D:\esp32p4-nuttx
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install kconfiglib==14.1.0
3.2 工具链前缀适配
NuttX CMake 期望的 RISC-V 工具链前缀是 riscv64-unknown-elf-,而 ESP-IDF 提供的是 riscv32-esp-elf-。解决方案是创建符号链接或复制重命名:
# 创建工具目录
mkdir D:\esp32p4-nuttx\tools\bin
# 复制并重命名工具链
$src = "C:\Espressif\tools\riscv32-esp-elf\esp-15.2.0_20251204\riscv32-esp-elf\bin"
$dst = "D:\esp32p4-nuttx\tools\bin"
@("gcc","g++","ld","ar","objcopy","objdump","nm","size","strip") | ForEach-Object {
Copy-Item "$src\riscv32-esp-elf-$_.exe" "$dst\riscv64-unknown-elf-$_.exe"
}
同时设置环境变量:
$env:GCC_EXEC_PREFIX = "C:\Espressif\tools\riscv32-esp-elf\esp-15.2.0_20251204\riscv32-esp-elf\libexec\gcc\"
3.3 环境脚本
创建 env-esp32p4-nuttx.ps1 一键加载环境:
$Root = $PSScriptRoot
$toolPaths = @(
(Join-Path $Root ".venv\Scripts"),
"C:\Espressif\tools\python\master\venv\Scripts",
(Join-Path $Root "tools\bin"),
(Join-Path $Root "mingw64\bin"),
"C:\Espressif\tools\riscv32-esp-elf\esp-15.2.0_20251204\riscv32-esp-elf\bin",
"C:\Espressif\tools\cmake\4.0.3\bin",
"C:\Espressif\tools\ninja\1.12.1",
"C:\Program Files\Git\usr\bin",
"C:\Program Files\Git\bin"
)
$existingToolPaths = $toolPaths | Where-Object { Test-Path $_ }
$env:Path = ($existingToolPaths -join ";") + ";" + $env:Path
$env:GCC_EXEC_PREFIX = "C:\Espressif\tools\riscv32-esp-elf\esp-15.2.0_20251204\riscv32-esp-elf\libexec\gcc\"
Write-Host "ESP32-P4 NuttX environment ready at $Root"
4. 以太网 defconfig 定制
NuttX 为 ESP32-P4-Function-Ev-Board 提供了多个预置配置。以太网功能对应的配置是 esp32p4-function-ev-board:ethernet。
4.1 原始 ethernet 配置分析
查看 nuttx\boards\risc-v\esp32p4\esp32p4-function-ev-board\configs\ethernet\defconfig,关键配置项:
CONFIG_ESPRESSIF_EMAC=y # 启用以太网 MAC
CONFIG_ESPRESSIF_ETH_DMA_BUFFER_SIZE=512
CONFIG_NETINIT_DHCPC=y # DHCP 客户端
CONFIG_NETDB_DNSCLIENT=y # DNS 解析
CONFIG_NET_TCP=y # TCP 协议
CONFIG_NET_UDP=y # UDP 协议
CONFIG_NET_ICMP_SOCKET=y # ICMP (Ping)
CONFIG_SYSTEM_PING=y # ping 命令
CONFIG_UART0_SERIAL_CONSOLE=y # 默认用 UART0 作控制台
4.2 修改为 USB 串口控制台
默认配置使用 UART0(GPIO37/38)作为控制台,需要外接 USB-TTL 转接器。改为 USB 串口控制台后,烧录和调试都用同一根 USB 线:
- CONFIG_UART0_SERIAL_CONSOLE=y
+ # CONFIG_ESPRESSIF_UART0 is not set
+ CONFIG_ESPRESSIF_USBSERIAL=y
4.3 添加自定义测试程序
在 apps\examples\ 下创建 ethdemo 测试应用,用于验证网络连通性:
apps/examples/ethdemo/ethdemo_main.c:
#include <nuttx/config.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include "netutils/netlib.h"
#define ETHDEMO_BUF_SIZE 512
#define ETHDEMO_TIMEOUT 5
static void print_ifinfo(FAR const char *ifname)
{
struct in_addr addr, mask, gw;
uint8_t mac[IFHWADDRLEN];
printf("\n=== Network Interface: %s ===\n", ifname);
if (netlib_get_ipv4addr(ifname, &addr) >= 0)
printf(" IP Address: %s\n", inet_ntoa(addr));
if (netlib_get_ipv4netmask(ifname, &mask) >= 0)
printf(" Netmask: %s\n", inet_ntoa(mask));
if (netlib_get_dripv4addr(ifname, &gw) >= 0)
printf(" Gateway: %s\n", inet_ntoa(gw));
if (netlib_getmacaddr(ifname, mac) >= 0)
printf(" MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
static int tcp_connect_test(FAR const char *host, uint16_t port)
{
struct sockaddr_in server;
struct timeval tv;
int sockfd, ret;
char buf[ETHDEMO_BUF_SIZE];
const char *http_request = "GET / HTTP/1.0\r\nHost: test\r\n\r\n";
ssize_t nread;
printf("\n=== TCP Connect Test ===\n");
printf("Connecting to %s:%u...\n", host, port);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) { printf("ERROR: socket() failed\n"); return -errno; }
tv.tv_sec = ETHDEMO_TIMEOUT; tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
inet_pton(AF_INET, host, &server.sin_addr);
ret = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
if (ret < 0) { printf("ERROR: connect() failed\n"); close(sockfd); return -errno; }
printf("Connected successfully!\n");
send(sockfd, http_request, strlen(http_request), 0);
printf("Sent HTTP request, waiting for response...\n");
nread = recv(sockfd, buf, sizeof(buf) - 1, 0);
if (nread > 0) {
buf[nread > 200 ? 200 : nread] = '\0';
printf("Received %zd bytes:\n%s\n", nread, buf);
}
close(sockfd);
return 0;
}
int main(int argc, FAR char *argv[])
{
FAR const char *host = (argc > 1) ? argv[1] : "192.168.1.1";
uint16_t port = (argc > 2) ? (uint16_t)atoi(argv[2]) : 80;
printf("\n============================================\n");
printf(" ESP32-P4 Ethernet Network Test\n");
printf("============================================\n");
print_ifinfo("eth0");
tcp_connect_test(host, port);
printf("\n============================================\n");
printf(" Test Complete\n");
printf("============================================\n");
return 0;
}
配套的构建文件(Kconfig、Makefile、Make.defs、CMakeLists.txt)遵循 NuttX 应用标准结构,详见文末附录。
在 defconfig 中启用:
CONFIG_EXAMPLES_ETHDEMO=y
CONFIG_EXAMPLES_ETHDEMO_HOST="192.168.1.1"
CONFIG_EXAMPLES_ETHDEMO_PORT=80
5. 编译
5.1 CMake 配置
cd D:\esp32p4-nuttx
. .\env-esp32p4-nuttx.ps1
cmake -S nuttx -B build-ethernet -G Ninja `
-DCMAKE_MAKE_PROGRAM="C:\Espressif\tools\ninja\1.12.1\ninja.exe" `
-DBOARD_CONFIG="esp32p4-function-ev-board:ethernet" `
-DNUTTX_APPS_DIR="D:\esp32p4-nuttx\apps" `
-DPython3_EXECUTABLE="D:\esp32p4-nuttx\.venv\Scripts\python.exe" `
-DEXTRA_FLAGS="-std=gnu11"
5.2 处理兼容性问题
stdatomic.h 兼容头:ESP HAL 部分代码与当前工具链的 <stdatomic.h> 不兼容,需要在以下路径放置兼容包装头:
build-ethernet\arch\risc-v\src\common\espressif\esp-hal-3rdparty\nuttx\src\components\esp_libc\platform_include\stdatomic.h
核心内容:
#pragma once
#include_next <stdatomic.h>
#ifdef ATOMIC_VAR_INIT
# undef ATOMIC_VAR_INIT
#endif
#define ATOMIC_VAR_INIT(val) (val)
#ifdef atomic_init
# undef atomic_init
#endif
#define atomic_init(ptr, val) __atomic_store_n((ptr), (val), __ATOMIC_RELAXED)
libgcc.a:复制到构建目录:
Copy-Item "C:\Espressif\tools\riscv32-esp-elf\esp-15.2.0_20251204\riscv32-esp-elf\lib\gcc\riscv32-esp-elf\15.2.0\libgcc.a" `
-Destination "D:\esp32p4-nuttx\build-ethernet\libgcc.a"
5.3 编译
cmake --build build-ethernet --parallel 4
成功输出:
[1666/1666] Running utility command for nuttx_post_build
esptool v5.3.0
Creating ESP32-P4 image...
Successfully created ESP32-P4 image.
-- Generated: nuttx.bin
编译产物:
| 文件 | 大小 | 说明 |
|---|---|---|
| nuttx | 771 KB | ELF 可执行文件 |
| nuttx.bin | 339 KB | ESP32-P4 烧录镜像 |
| nuttx.hex | 812 KB | Intel HEX 格式 |
| nuttx.map | 1.9 MB | 链接 Map 文件 |
6. 烧录
ESP32-P4 使用 simple boot 模式,烧录地址为 0x2000:
esptool --chip esp32p4 -p COM4 -b 460800 write-flash -fs 4MB -fm dio -ff 80m 0x2000 .\build-ethernet\nuttx.bin
烧录日志:
Connected to ESP32-P4 on COM4
Wrote 339068 bytes at 0x00002000
Hash of data verified.
Hard resetting via RTS pin...
7. 测试验证
7.1 打开串口
python -m serial.tools.miniterm COM4 115200
按 RESET 或敲回车,看到 nsh> 提示符。
7.2 查看网络接口
nsh> ifconfig
eth0 Link encap:Ethernet HWaddr e8:f6:0a:e3:a6:46 at RUNNING mtu 1500
inet addr:0.0.0.0 DRaddr:10.0.0.1 Mask:255.255.255.0
此时已获取到 MAC 地址,但还没有 IP。
7.3 DHCP 获取 IP
nsh> renew eth0
nsh> ifconfig
eth0 Link encap:Ethernet HWaddr e8:f6:0a:e3:a6:46 at RUNNING mtu 1500
inet addr:192.168.1.135 DRaddr:192.168.1.1 Mask:255.255.255.0
成功通过 DHCP 获取到 IP 192.168.1.135。
7.4 Ping 外网
nsh> ping www.baidu.com
PING 110.242.69.21 56 bytes of data
56 bytes from 110.242.69.21: icmp_seq=0 time=10.0 ms
56 bytes from 110.242.69.21: icmp_seq=1 time=10.0 ms
56 bytes from 110.242.69.21: icmp_seq=2 time=10.0 ms
...
10 packets transmitted, 10 received, 0% packet loss, time 10100 ms
rtt min/avg/max/mdev = 0.000/9.000/10.000/3.000 ms
DNS 解析和 ICMP 全部正常,10 包 0% 丢包。
7.5 ethdemo TCP 测试
nsh> ethdemo
============================================
ESP32-P4 Ethernet Network Test
============================================
=== Network Interface: eth0 ===
IP Address: 192.168.1.135
Netmask: 255.255.255.0
Gateway: 192.168.1.1
MAC Address: e8:f6:0a:e3:a6:46
=== TCP Connect Test ===
Connecting to 192.168.1.1:80...
Connected successfully!
Sent HTTP request, waiting for response...
Received 183 bytes:
HTTP/1.1 200 OK
Cache-control:no-cache, no-store, max-age=0
Content-Type:text/html
...
============================================
Test Complete
============================================
TCP 连接、HTTP 请求、数据收发全部正常。
8. SPI Flash 文件系统(可选)
首次使用 SPI Flash 需要格式化:
nsh> mksmartfs /dev/smart0
nsh> mount -t smartfs /dev/smart0 /mnt
nsh> echo "Hello ESP32-P4" > /mnt/test.txt
nsh> cat /mnt/test.txt
Hello ESP32-P4
9. 已启用的网络功能一览
| 功能 | 配置项 | 说明 |
|---|---|---|
| 以太网 MAC | CONFIG_ESPRESSIF_EMAC=y |
内部 EMAC + 外部 PHY |
| USB 串口 | CONFIG_ESPRESSIF_USBSERIAL=y |
USB CDC-ACM 控制台 |
| DHCP | CONFIG_NETINIT_DHCPC=y |
自动获取 IP |
| DNS | CONFIG_NETDB_DNSCLIENT=y |
域名解析 |
| TCP | CONFIG_NET_TCP=y |
TCP 协议栈 |
| UDP | CONFIG_NET_UDP=y |
UDP 协议栈 |
| ICMP | CONFIG_NET_ICMP_SOCKET=y |
Ping 支持 |
| SPI Flash | CONFIG_ESPRESSIF_SPIFLASH=y |
文件系统存储 |
| PSRAM | CONFIG_ESPRESSIF_SPIRAM=y |
扩展内存 |
10. 常见问题
Q: ping 不通外网但能 ping 通网关
A: 检查 DNS 配置,手动设置:
nsh> echo "nameserver 8.8.8.8" > /etc/resolv.conf
Q: DHCP 获取不到 IP
A: 检查网线连接,确认路由器 DHCP 服务开启。也可以手动设置 IP:
nsh> ifconfig eth0 192.168.1.200 netmask 255.255.255.0
nsh> ifconfig eth0 gw 192.168.1.1
Q: 编译时报 stdatomic.h 相关错误
A: 参考第 5.2 节,放置兼容包装头。
Q: esptool 烧录失败
A: 确认 esptool 在 PATH 中,确认串口号正确。按住 BOOT 按键再插 USB 可进入下载模式。
11. 总结
ESP32-P4 内置以太网 MAC,配合 NuttX 操作系统可以快速实现有线网络功能。整个流程的关键点:
- 使用
esp32p4-function-ev-board:ethernet配置启用 EMAC - 使用 USB 串口控制台避免外接转接器
- 处理工具链前缀和
stdatomic.h兼容性 - 通过 DHCP 自动获取 IP,DNS 解析域名
ping和自定义ethdemo验证网络连通性
以太网相比 WiFi 具有更低的延迟和更高的稳定性,适合工业控制、网关设备等场景。ESP32-P4 + NuttX 的组合为这类应用提供了良好的基础。
附录:ethdemo 构建文件
Kconfig
config EXAMPLES_ETHDEMO
tristate "Ethernet demo application"
default n
depends on NET_TCP
depends on NET_UDP
if EXAMPLES_ETHDEMO
config EXAMPLES_ETHDEMO_HOST
string "HTTP test host (IP address)"
default "192.168.1.1"
config EXAMPLES_ETHDEMO_PORT
int "HTTP test port"
default 80
config EXAMPLES_ETHDEMO_PROGNAME
string "Program name"
default "ethdemo"
config EXAMPLES_ETHDEMO_STACKSIZE
int "Stack size"
default DEFAULT_TASK_STACKSIZE
config EXAMPLES_ETHDEMO_PRIORITY
int "Priority"
default 100
endif
Makefile
include $(APPDIR)/Make.defs
PRIORITY = $(CONFIG_EXAMPLES_ETHDEMO_PRIORITY)
STACKSIZE = $(CONFIG_EXAMPLES_ETHDEMO_STACKSIZE)
PROGNAME = $(CONFIG_EXAMPLES_ETHDEMO_PROGNAME)
MAINSRC = ethdemo_main.c
include $(APPDIR)/Application.mk
Make.defs
ifneq ($(CONFIG_EXAMPLES_ETHDEMO),)
CONFIGURED_APPS += $(APPDIR)/examples/ethdemo
endif
CMakeLists.txt
if(CONFIG_EXAMPLES_ETHDEMO)
nuttx_add_application(
NAME ${CONFIG_EXAMPLES_ETHDEMO_PROGNAME}
SRCS ethdemo_main.c
STACKSIZE ${CONFIG_EXAMPLES_ETHDEMO_STACKSIZE}
PRIORITY ${CONFIG_EXAMPLES_ETHDEMO_PRIORITY})
endif()
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)