ESP32-S3 在 Win11 上搭建 ESP-IDF 开发环境
ESP32-S3-DevKitC-1 的环境搭建记录。没用 Arduino 起步,直接上 ESP-IDF —— Arduino core 底层就是 IDF,而 S3 的差异化能力(USB OTG、TinyML、双核 FreeRTOS)在 IDF 里才是第一手的。
三个组件的关系
搞清这个能省一半排障时间:
EIM(ESP-IDF Installation Manager) ← 唯一的管理器
└─ 装/删/切换 IDF 版本、装工具链、建 Python venv
└─ 产出 C:\Espressif\tools\eim_idf.json(真相来源)
VS Code ESP-IDF 扩展 ← 消费者,不是第二个安装器
└─ 读 eim_idf.json 发现所有 IDF 安装
└─ 管环境变量,提供 Build/Flash/Monitor/Menuconfig 按钮
idf.py / esptool / openocd ← 实际干活的命令行工具
EIM 是独立的跨平台工具,不管从 GUI、CLI 还是别的 IDE 触发的安装,任何读 eim_idf.json 的工具都能自动发现。所以 EIM 装了不用删 —— 它和 VS Code 扩展共用同一份安装,不冲突,而且 eim list / eim select / eim fix 在排障时比点按钮直接。
VS Code 扩展内部也能拉起 EIM,所以 winget install Espressif.EIM 和扩展里点按钮两条路都行,选一条即可。
安装
winget install Microsoft.VisualStudioCode
# 关掉终端重开,让 code 进 PATH
code --install-extension espressif.esp-idf-extension
winget install Espressif.EIM
eim install -i v6.1
装完在 VS Code 里:
F1 → ESP-IDF: Select Current ESP-IDF Version
F1 → ESP-IDF: Doctor Command ← 验证,问题项直接标红
IDF 和 IDF_TOOLS_PATH 的路径里不能有空格,别选 Program Files。默认 C:\Espressif 就对。EIM 装到非默认位置时扩展认不出来,要设 idf.eimIdfJsonPath。
工程路径不能放在 WSL 文件系统里
踩得最久的一个坑。在 VS Code 里打开 WSL 目录、跑 Windows 版 idf.py,报:
CMakeLists.txt not found in project directory \\wsl.localhost\archlinux\home\<user>
Failed to set target esp32s3: non zero exit code 2
Your environment is not configured to handle Unicode characters
根因不是编码。\\wsl.localhost\ 是 Windows 通过 9P 协议访问 WSL 文件系统的 UNC 路径,Windows 版的 idf.py / cmake / ninja 在上面问题极多,不是配置能修的。第二条报错只是顺带吐出来的警告,真正的死因是那句话里根本没有工程目录。
修法是换到本地盘:
mkdir C:\esp -Force
cd C:\esp
顺带治编码:
setx PYTHONUTF8 1 # 用户级永久,新终端生效
比开系统那个 "Beta: Use Unicode UTF-8" 好 —— 那个会让一些老中文程序乱码,PYTHONUTF8 只影响 Python,范围可控。
两个 USB 口功能完全不同
DevKitC-1 板子上有两个 USB 口,名字很像但接的是两套硬件:
UART 口 | USB 口(原生) | |
|---|---|---|
| 芯片 | 板载 CP2102N 桥 | ESP32-S3 内置 USB Serial/JTAG |
| 驱动 | Silicon Labs CP210x | 系统自带 usbser.sys,无需装 |
| 设备管理器名称 | CP210x USB to UART Bridge (COMx) | USB Serial Device (COMx) |
| IDF 默认 console | UART0,开箱即用 | 需改 menuconfig |
| 早期启动日志 | 能看到 | 头几百毫秒丢失(USB 枚举慢) |
| JTAG 调试 | 不支持 | 唯一选择 |
设备管理器里的名称是最快的区分方法。
只插一个口 —— 双插合法(双路供电),但会冒出两个 COM 口,容易选错。换口之后 COM 号会变,扩展里要重选。
走原生 USB 口的额外配置
IDF 默认把 console 输出指向 UART0。接原生 USB 口而不改配置的话,monitor 一片空白 —— 输出跑到没有物理连接的 UART0 上去了。
Component config → ESP System Settings → Channel for console output
→ USB Serial/JTAG Controller
对应 CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG。
这里有个反直觉的点:idf.flashType 仍然是 UART。原生 USB 口的 CDC 部分对 esptool 就是个普通串口,USB Serial/JTAG 控制器能自动进下载模式,走 esptool 串口协议烧写就行,不需要 OpenOCD。
烧写方式被带偏到 JTAG
报错:
Can't perform JTAG flash, because OpenOCD server is not running!
原因是不需要 JTAG 却选了 JTAG。触发点在 ESP-IDF: Set Espressif device target 时选了 "onboard USB" —— 那个选项是给调试用的,不是给烧写用的。
改回来:Ctrl+, 搜 idf.flashType 选 UART。
真要用 JTAG 时
S3 内置 USB JTAG,不用买 ESP-Prog 就能下断点。但三件事缺一不可:
- 线插原生
USB口 - 那个口在设备管理器里是复合设备,Interface 2 必须绑 WinUSB(Interface 0 是 CDC 串口)。Win10+ 联网自动装,装不上用 Zadig —— 关键坑是按钮下拉里选 "Install Driver" 而不是 "Install WCID Driver",选错则
DeviceInterfaceGUIDs写不进注册表,OpenOCD 报libusb_open() failed with LIBUSB_ERROR_NOT_FOUND(看着像没接设备,其实是驱动没配对) - 先启动 OpenOCD server,board config 选
board/esp32s3-builtin.cfg
不想折腾驱动也有替代:idf.py monitor 自带 GDBStub,menuconfig 开 CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME,程序 panic 时直接掉进 GDB 看调用栈。不打硬件断点的话够用。
板载 RGB LED 是 WS2812
不是普通 LED。写 gpio_set_level(38, 1) 不会有任何反应,要走 RMT 外设 + 编码器。
引脚按板子版本区分:v1.0 是 GPIO48,v1.1 是 GPIO38。引脚表里的标注是 GPIO38, FSPIWP, SUBSPIWP, RGB LED。
例子路径:ESP-IDF: Show Examples Projects → peripherals/rmt/led_strip。
注意 GPIO 号是源码里的宏,不在 menuconfig 里:
// main/led_strip_example_main.c
#define RMT_LED_STRIP_GPIO_NUM 0 // 改成 38(v1.1)或 48(v1.0)
同文件的 EXAMPLE_LED_NUMBERS 默认 8,板载只有 1 颗,改成 1。
跑通这个例子等于摸到了 RMT 外设 + 编码器模型,是驱动 WS2812、红外、DAC 的通用路子。
型号差异
| 订货码 | Flash | PSRAM | SPI 电压 |
|---|---|---|---|
| N8 | 8MB QD | — | 3.3V |
| N8R2 | 8MB QD | 2MB QD | 3.3V |
| N8R8 | 8MB QD | 8MB OT | 3.3V |
| N16R8V | 16MB OT | 8MB OT | 1.8V |
| N32R8V | 32MB OT | 8MB OT | 1.8V |
- N8R8 需要在 menuconfig 里把 Flash size 改成 8MB、PSRAM 开 Octal,否则按 4MB/无 PSRAM 跑
- Octa 型号的 GPIO35/36/37 被 flash/PSRAM 占用,接外设避开
- GPIO19/20 是 USB D-/D+,插了原生 USB 就不能当 GPIO 用
速查
Ctrl + ] 退出 monitor
idf.py set-target esp32s3 设目标芯片
idf.py -p COMx flash monitor
idf.py menuconfig 配置
idf.py create-project NAME 建新工程
eim list / select / remove / fix / shell / run
烧不进去时:按住 BOOT → 点 RESET → 松 BOOT 进下载模式。烧成功一次之后通常不用再按。
报端口占用 = 上一个 monitor 没退出。
扩展面板的 Build/Flash/Monitor 按钮由扩展自己管环境变量,绕开终端 PATH 和编码问题,比命令行省事。
当天完成
两个例子跑通:hello_world 串口输出正常;led_strip 点亮板载 WS2812。