王智刚

Windows 上撸 ESP32-S3 + Rust 的踩坑实录

Misc 3 分钟
目录

上一篇说了入坑过程。这篇专门记 Windows 上把 Rust + ESP-IDF 跑起来踩的坑——任何一条没绕开都会卡在编译/链接阶段一两个小时。

工具链全家桶

ESP32 的 Xtensa 架构主线 LLVM 不支持,必须装 Espressif fork 的 Rust 工具链:

工具版本作用
Rust stable1.92+主工具链
Xtensa Rust(esp channel)1.93.0.0espup install --std -t esp32s3 装的 fork
Python3.11(不是 Windows Store 那个)ESP-IDF 构建脚本依赖
espup0.17+装 / 升级 Xtensa 工具链
espflash4.4+烧写 + 串口监视
ldproxy0.3+包装 xtensa-gcc,桥接 rustc ↔ ESP-IDF
cargo-generate0.23+起项目模板用
graph LR A[cargo build] --> B[rustc xtensa] B --> C[ldproxy] C --> D[xtensa-esp32-elf-gcc] A --> E[embuild build.rs] E --> F[ESP-IDF 5.5.3] F --> G[bindgen + libclang] G --> H[bindings.rs] H --> B F --> I[idf.py build C] I --> C

espup install --std -t esp32s3 一条命令搞定 Rust fork + xtensa-gcc + esp-clang,~500MB 拉完。

坑 1:Windows 命令行 32KB 上限

这条最隐蔽,也最折磨人。

Rust 链接 ESP-IDF 时,ldproxy 把所有 .o / .a 拼成一行命令交给 xtensa-esp32-elf-gcc。Windows 的 CreateProcess API 上限 32KB,一旦 Cargo.toml 多加几个 crate(比如 embassy 全家桶),命令行直接超长,报:

error: linking with 'ldproxy' failed: (os error 206)

os error 206 = ERROR_FILENAME_EXCED_RANGE

解法:依赖刻意保持最小。我现在 Cargo.toml 里只有:

[dependencies]
log = "0.4"
esp-idf-svc = "0.52.1"
anyhow = "1.0"
embedded-graphics = "0.8"
embedded-hal = "1.0"
heapless = "0.8"
profont = "0.7"
u8g2-fonts = "0.7"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

真要用 embassy-time 之类的 async 周边,老实迁 WSL2,否则永远在跟 32KB 斗智斗勇。

坑 2:target-dir 必须重定向到短路径

ESP-IDF 在 target/xtensa-esp32s3-espidf/release/build/... 下面会再嵌几层目录,加上 Windows 默认 MAX_PATH 260 字符,常见报错:

Too long output directory ...
Shorten your project path to no more than 10 characters

解法.cargo/config.toml 里强制 target-dir 到短路径:

[build]
target = "xtensa-esp32s3-espidf"
target-dir = "D:/t/rlcd" # 全工程产物都堆这
[env]
CARGO_WORKSPACE_DIR = { value = "", relative = true } # 必须搭配

CARGO_WORKSPACE_DIR 那条是 embuild 找工作区根的依据,不写它 build script 找不到 partitions.csv

坑 3:Python 必须真 3.11,不是 Store alias

winget install python 装的可能是 Microsoft Store alias——一个名为 python.exe 的 stub,第一次运行会拉你去商店下载。ESP-IDF 的 idf.py 调它直接卡死。

解法

Terminal window
winget install Python.Python.3.11

然后 PowerShell profile 把真 Python 路径放 PATH 最前

Terminal window
$env:PATH = "$env:LOCALAPPDATA\Programs\Python\Python311;$env:PATH"

坑 4:第一次拉 ESP-IDF 极慢

embuild 第一次跑会做四件事:

  1. clone ESP-IDF + 所有 submodule(~1.5GB)
  2. 下 xtensa-gcc / cmake / ninja(~500MB)
  3. bindgen 生成 5000+ 条 FFI(CPU 满载几分钟)
  4. 编 ESP-IDF 几百个 .c 文件

全程必须挂代理,不然 GitHub 慢到放弃。两条路:

Terminal window
# 全局代理(直连 GitHub)
$env:HTTPS_PROXY = "http://127.0.0.1:7890"

或者用乐鑫官方镜像(推荐,全自动、不走梯子):

# .cargo/config.toml [env]
IDF_GITHUB_ASSETS = "dl.espressif.cn/github_assets"

ESP-IDF 内部所有 https://github.com/... 拉 release 资源都会被改写成 dl.espressif.cn/github_assets/...,国内 100MB/s 起步。

首次完整 build 30~60 分钟,后续增量秒级。

坑 5:libclang 路径要硬编码

bindgen 生成 ESP-IDF 的 Rust FFI 需要 libclang.dllespup 装了一份在 ~/.rustup/toolchains/esp/xtensa-esp32-elf-clang/esp-clang/bin/,但 bindgen 默认只在系统 PATH 里找。

解法.cargo/config.toml 显式指过去:

[env]
LIBCLANG_PATH = "C:\\Users\\<你>\\.rustup\\toolchains\\esp\\xtensa-esp32-elf-clang\\esp-clang\\bin\\libclang.dll"

注意是文件路径不是目录路径——bindgen 这点很挑。

坑 6:sdkconfig 关 bool 不能用 =n

ESP-IDF 的 Kconfig 里凡是 choice 块里的选项,关掉必须用注释式语法:

# CONFIG_ESP_WIFI_STATIC_TX_BUFFER is not set
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y

写成 CONFIG_ESP_WIFI_STATIC_TX_BUFFER=n 会被 Kconfig 静默丢弃,看起来没报错但配置根本没生效。这条吃过亏——heap_min_ever 跌到 2.8KB 排查半天,最后才发现 dynamic TX buffer 选项压根没启用。

一把流:just

写完上面这些一次性配置,日常就剩:

Terminal window
just # = flash-monitor:编译 + 烧 + 进监视器
just build # 只编译
just flash # 编译 + 烧(不监视)
just monitor # 只监视(Ctrl+R 软复位、Ctrl+C 退出)
just size # 看 bin 体积
just clean # 清构建产物(注意 target 在 D:\t\rlcd)
just doctor # 工具链自检

espflash flash 默认只烧 app 分区,自定义 partition-table 必须显式带 --bootloader + --partition-table,否则 storage 分区找不到。我的 justfile 里已经处理了。

一句话总结

Windows + Rust + ESP-IDF 90% 的坑都在第一次跑通之前。一旦 build 通了、just flash 能稳定烧进去,后面就是单片机本身的事了。

下一篇说 反射屏怎么排信息密集仪表盘 + 板子自己当 web server