利用WSL + VSCode + ESP-IDF6开发ESP32系列单片机指南

利用WSL + VSCode + ESP-IDF6开发ESP32系列单片机指南

前言

我是在是接受不了沟槽的Windows了,开发体验太差,文件系统的性能不太行(可能跟我不会设置很大关系)导致当时编译ESP的工程够我出去溜一圈在回来的,比起来,Linux上开发程序显然性价比好的多。属于是一次开发之后一直爽了。

这篇博客的主要目的就是为了在WSL上开发单片机,之后开发其他的单片机就可以使用PlatformIO来解决问题,这里记录一下笔者的开发过程。

准备WSL

笔者是WSL常客,这里呢,笔者提醒一些设置,因为WSL会默认的Append上Windows的Path变量,笔者之前写前端被坑过,所以如果您希望您的WSL是专门用来开发单片机的话,建议在/etc/wsl.conf写上最后两行配置,这是告诉WSL加载环境变量的时候不要带上Windows的PATH,当然,如果这是您的意图,不要做。

复制代码
❯ cat /etc/wsl.conf 
[boot]
systemd=true

[automount]
enabled = true
options = "metadata"
mountFsTab = true

[interop]
appendWindowsPath = false

笔者喜欢使用的是Arch WSL,这里给想试试的朋友Github链接,或者是直接下默认的Arch WSL就好。

Arch WSL Github

其余的WSL设置,没有任何新鲜的东西了

安装ESP-IDF prerequisites

笔者有代理,建议有代理的朋友,特别是使用Clash代理加速Github下载的朋友开启TUN模式,这个时候,Clash会虚拟化一个网卡,将整台电脑上任意网络活动都转发到虚拟网卡上,这样会让那些不使用环境变量走代理的脚本也用上代理,加速下载。

没有代理的朋友,单独参考一下ESP乐鑫官方教你咋下载:

(Tips:笔者建议您单独在一个独立的venv环境配置IDF开发所用的Python,这样不会炸整个WSL的Python环境)

【图文】使用 WSL + VSCode 搭建 ESP32/ESP32-S2 开发环境_wsl esp32-CSDN博客

如何使用 WSL + VSCode 搭建 ESP32 开发环境 - 哔哩哔哩

下载好前提之后,按照思路下载ESP-IDF工具链就好了。这里不再重复。笔者建议你设置一个指向自己ESP-IDF工具链的export.sh,用在VSCode出问题看日志看不懂的时候,自己在命令行调试的时候可以方便的起起来环境的指令

复制代码
alias export_esp_env="source /path/to/your/esp-idf/export.sh"

检查一下:

复制代码
❯ export_esp_env # 测试一下我们的环境搭建好没有
Checking "python3" ...
Python 3.13.7
"python3" has been detected
Activating ESP-IDF 6.1
Setting IDF_PATH to '/home/charliechen/esp-idf'.
* Checking python version ... 3.13.7
* Checking python dependencies ... OK
* Deactivating the current ESP-IDF environment (if any) ... OK
* Establishing a new ESP-IDF environment ... OK
* Identifying shell ... zsh
* Detecting outdated tools in system ... OK - no outdated tools found
* Shell completion ... Autocompletion code generated

Done! You can now compile ESP-IDF projects.
Go to the project directory and run:

  idf.py build

❯ idf.py --version # OK,很新,是6.1
ESP-IDF v6.1-dev-786-g130fdc7ce7-dirty

VSCode安装插件

VSCode安装的是ESP-IDF插件,下载好了之后,摁下Ctrl + Shift + P呼出命令面板,输入Configure ESP-IDF,这个时候,我们选择点击"ADVANCED"进入高级配置界面,选择我们刚刚下好的ESP-IDF工具链所在位置,和勾选所使用的Python环境(笔者是专门准备了一个独立的venv Python环境处理它),记得继续挂代理,这样的话配置的快。

笔者还额外建议你安装CMake的插件(CMake + CMake Tools)

准备工程

USB预备

下一步,我们就是要准备USB接入。WSL中的USB接入很麻烦。好在有一个好用的工具叫usbipd,这个工具的下载,很不幸,还是要翻墙的。

Releases · dorssel/usbipd-win

下载好这个工具并且安装结束后,打开管理员模式的CMD或者是Powershell,先插上你的ESP32开发板,准备设置,输入usbipd list,可以看到找到我们的ESP32端口的USB了,一般而言是COM打头的,记住BUSID是2-2

复制代码
➜  usbipd list
Connected:
BUSID  VID:PID    DEVICE                                                        STATE
...
2-2    303a:1001  USB 串行设备 (COM5), USB JTAG/serial debug unit               Shared
...

Persisted:
GUID                                  DEVICE

下一步,我们要让他绑定到WSL上,以笔者的BUSID查出来是2-2为例子

复制代码
# 不要复制粘贴,看你自己的BUSID操作!!!
usbipd bind -b 2-2 # 这个只用操作一次就好了,之后插拔的时候不用再输入它
usbipd attach --wsl --busid  2-2 # 这个指令会让USB Attach到WSL上,注意,此时的USB就没法在Windows主机上使用了,除非你断开它!

输入结束之后,最后一个指令的输出:

复制代码
usbipd: info: Using WSL distribution 'simplechip' to attach; the device will be available in all WSL 2 distributions.
usbipd: info: Loading vhci_hcd module.
usbipd: info: Detected networking mode 'mirrored'.
usbipd: info: Using IP address 127.0.0.1 to reach the host.

这就是成功了

检查一下自己的WSL上有没有新的设备接入,一般而言,接入的映射设备是/dev/ttyACM*或者是/dev/ttyUSB*。笔者的就是/dev/ttyACM0

复制代码
❯ ls /dev/ttyACM0
/dev/ttyACM0

❯ ls -lh /dev/ttyACM0
crw-rw---- 1 root uucp 166, 0 Nov 24 12:52 /dev/ttyACM0

Ctrl Shift P呼出命令面板,然后咱们就创建工程了,New Project设置自己所用的ESP32开发板的芯片型号,刚刚接入的USB端口等等信息。现在开始,模板中没有sample_project了,只有Hello World,现在勾选一下。

下一步是一个大坑------ESP-IDF在6.1版本的时候,支持最小化构建,如果你需要其他模块,比如说笔者的片子上有PSRAM,这就要关掉最小化构建。

复制代码
idf_build_set_property(MINIMAL_BUILD OFF)

原本这里是ON,改成OFF,然后刷新一下就好了。(idf.py menuconfig或者是点击VSCode下侧状态栏,注意,不是VSCode的设置,而是显示自己芯片右侧的一个小齿轮),然后就可以开始设置自己芯片的属性了。注意,如果你有设置找不到,有可能是名称变更或者是MINIMAL_BUILD开启了,记得检查一下文档。

c 复制代码
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_psram.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"

void app_main(void)
{
    esp_err_t ret;
    uint32_t flash_size;
    esp_chip_info_t chip_info;
    ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES
        || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    esp_flash_get_size(NULL, &flash_size); /* 获取 FLASH 大小 */
    esp_chip_info(&chip_info);
    printf("cup%d\n", chip_info.cores); /* 获取 CPU 内核数并显示 */
    /* 获取 FLASH 大小并显示 */
    printf("FLASH size:%ld MB flash\n", flash_size / (1024 * 1024));
    /* 获取 PARAM 大小并显示 */
    printf("PSRAM size: %d bytes\n", esp_psram_get_size());
    while (1) {
        vTaskDelay(1000);
    }
}            

笔者玩的是ESP32S3,带PSRAM的,笔者建议您找您自己芯片的示例程序,拷贝一份编译一下试试看,编译也可以在下侧的状态工具栏中找到,这里不再教授了。编译成功后,会显示类似这样的东西:

复制代码
                            Memory Type Usage Summary                             
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Memory Type/Section ┃ Used [bytes] ┃ Used [%] ┃ Remain [bytes] ┃ Total [bytes] ┃
┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ Flash Code          │        96206 │          │                │               │
│    .text            │        96206 │          │                │               │
│ DIRAM               │        55167 │    16.14 │         286593 │        341760 │
│    .text            │        39947 │    11.69 │                │               │
│    .data            │        12820 │     3.75 │                │               │
│    .bss             │         2400 │      0.7 │                │               │
│ Flash Data          │        41428 │          │                │               │
│    .rodata          │        41172 │          │                │               │
│    .appdesc         │          256 │          │                │               │
│ IRAM                │        16384 │    100.0 │              0 │         16384 │
│    .text            │        15356 │    93.73 │                │               │
│    .vectors         │         1028 │     6.27 │                │               │
│ RTC SLOW            │           36 │     0.44 │           8156 │          8192 │
│    .force_slow      │           36 │     0.44 │                │               │
│ RTC FAST            │           24 │     0.29 │           8168 │          8192 │
│    .rtc_reserved    │           24 │     0.29 │                │               │
└─────────────────────┴──────────────┴──────────┴────────────────┴───────────────┘
Total image size: 206821 bytes (.bin may be padded larger)

下一步,就是准备烧录了。

烧录和调试ESP32

烧录

烧录和调试会比较麻烦了,

复制代码
ls -lh /dev/ttyACM0                 
crw-rw---- 1 root uucp 166, 0 Nov 24 12:52 /dev/ttyACM0

可以看到默认的情况下,我们是没有权限读写这个USB设备的,办法就是将自己加入到uucp中

复制代码
sudo usermod -aG uucp $USER
newgrp uucp

groups # 检查一下自己的组
charliechen uucp wheel

现在就OK了,可以试一试Flash烧录

复制代码
Connected to ESP32-S3 on /dev/ttyACM0:
Chip type:          ESP32-S3 (QFN56) (revision v0.2)
Features:           Wi-Fi, BT 5 (LE), Dual Core + LP Core, 240MHz, Embedded PSRAM 8MB (AP_3v3)
Crystal frequency:  40MHz
USB mode:           USB-Serial/JTAG
MAC:                98:88:e0:00:86:f4

Stub flasher running.
Changing baud rate to 460800...
Changed.

Configuring flash size...
Flash will be erased from 0x00000000 to 0x00005fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x00010000 to 0x00042fff...
SHA digest in image updated.
Wrote 22720 bytes (14489 compressed) at 0x00000000 in 0.4 seconds (429.3 kbit/s).
Hash of data verified.
Wrote 3072 bytes (132 compressed) at 0x00008000 in 0.1 seconds (485.9 kbit/s).
Hash of data verified.
Wrote 206944 bytes (114556 compressed) at 0x00010000 in 1.9 seconds (885.1 kbit/s).
Hash of data verified.

Hard resetting via RTS pin...

点击一下显示屏小按钮:

复制代码
I (24) boot: ESP-IDF v6.1-dev-786-g130fdc7ce7-dirty 2nd stage bootloader
I (25) boot: compile time Nov 24 2025 10:36:40
I (25) boot: Multicore bootloader
I (27) boot: chip revision: v0.2
I (30) boot: efuse block revision: v1.3
I (33) qio_mode: Enabling default flash chip QIO
I (38) boot.esp32s3: Boot SPI Speed : 80MHz
I (41) boot.esp32s3: SPI Mode       : QIO
I (45) boot.esp32s3: SPI Flash Size : 16MB
I (49) boot: Enabling RNG early entropy source...
I (53) boot: Partition Table:
I (56) boot: ## Label            Usage          Type ST Offset   Length
I (62) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (69) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (75) boot:  2 factory          factory app      00 00 00010000 001f0000
I (82) boot:  3 vfs              Unknown data     01 81 00200000 00a00000
I (88) boot:  4 storage          Unknown data     01 82 00c00000 00400000
I (95) boot: End of partition table
I (98) esp_image: segment 0: paddr=00010020 vaddr=3c020020 size=09fach ( 40876) map
I (112) esp_image: segment 1: paddr=00019fd4 vaddr=3fc91d00 size=03214h ( 12820) load
I (115) esp_image: segment 2: paddr=0001d1f0 vaddr=40374000 size=02e28h ( 11816) load
I (123) esp_image: segment 3: paddr=00020020 vaddr=42000020 size=169b0h ( 92592) map
I (142) esp_image: segment 4: paddr=000369d8 vaddr=40376e28 size=0ae18h ( 44568) load
I (151) esp_image: segment 5: paddr=000417f8 vaddr=50000000 size=00024h (    36) load
I (158) boot: Loaded app from partition at offset 0x10000
I (158) boot: Disabling RNG early entropy source...
I (169) octal_psram: vendor id    : 0x0d (AP)
I (169) octal_psram: dev id       : 0x02 (generation 3)
I (169) octal_psram: density      : 0x03 (64 Mbit)
I (171) octal_psram: good-die     : 0x01 (Pass)
I (176) octal_psram: Latency      : 0x01 (Fixed)
I (180) octal_psram: VCC          : 0x01 (3V)
I (184) octal_psram: SRF          : 0x01 (Fast Refresh)
I (189) octal_psram: BurstType    : 0x01 (Hybrid Wrap)
I (194) octal_psram: BurstLen     : 0x01 (32 Byte)
I (198) octal_psram: Readlatency  : 0x02 (10 cycles@Fixed)
I (203) octal_psram: DriveStrength: 0x00 (1/1)
I (208) MSPI Timing: Enter psram timing tuning
I (213) esp_psram: Found 8MB PSRAM device
I (216) esp_psram: Speed: 80MHz
I (219) cpu_start: Multicore app
I (654) esp_psram: SPI SRAM memory test OK
I (663) cpu_start: GPIO 44 and 43 are used as console UART I/O pins
I (663) cpu_start: Pro cpu start user code
I (663) cpu_start: cpu freq: 240000000 Hz
I (665) app_init: Application information:
I (669) app_init: Project name:     demo0
I (672) app_init: App version:      1
I (676) app_init: Compile time:     Nov 24 2025 10:33:56
I (681) app_init: ELF file SHA256:  b624351c6...
I (685) app_init: ESP-IDF:          v6.1-dev-786-g130fdc7ce7-dirty
I (691) efuse_init: Min chip rev:     v0.0
I (695) efuse_init: Max chip rev:     v0.99 
I (699) efuse_init: Chip rev:         v0.2
I (703) heap_init: Initializing. RAM available for dynamic allocation:
I (709) heap_init: At 3FC95860 len 00053EB0 (335 KiB): RAM
I (714) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM
I (719) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (725) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM
I (730) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator
I (737) spi_flash: detected chip: boya
I (740) spi_flash: flash io: qio
I (743) sleep_gpio: Configure to isolate all GPIO pins in sleep state
I (749) sleep_gpio: Enable automatic switching of GPIO sleep configuration
I (756) main_task: Started on CPU0
I (759) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (767) main_task: Calling app_main()
cup2
FLASH size:16 MB flash
PSRAM size: 8388608 bytes

OK,程序正常跑起来了,没毛病!

调试

调试也是一个大麻烦,笔者发现半天自己的OpenOCD跑不起来。

复制代码
Info : esp_usb_jtag: VID set to 0x303a and PID to 0x1001
Info : esp_usb_jtag: capabilities descriptor set to 0x2000

Info : Listening on port 6666 for tcl connections

Info : Listening on port 4444 for telnet connections

❌ Error: libusb_open() failed with LIBUSB_ERROR_ACCESS

❌ Error: esp_usb_jtag: could not find or open device!
/home/charliechen/.espressif/tools/openocd-esp32/v0.12.0-esp32-20250707/openocd-esp32/share/openocd/scripts/target/esp_common.cfg:9: Error: 
Traceback (most recent call last):
  File "/home/charliechen/.espressif/tools/openocd-esp32/v0.12.0-esp32-20250707/openocd-esp32/share/openocd/scripts/target/esp_common.cfg", line 9, in script

For assistance with OpenOCD errors, please refer to our Troubleshooting FAQ: https://github.com/espressif/openocd-esp32/wiki/Troubleshooting-FAQ
OpenOCD Exit with non-zero error code 1
[Stopped]

说明对于OpenOCD而言,权限还是有点问题。这个时候,我们检查一下

复制代码
ls -l /dev/bus/usb/001/002

crw-rw-r-- 1 root root 189, 1 Nov 24 12:08 /dev/bus/usb/001/002

ls /dev/ttyACM*
/dev/ttyACM0

当前 USB 设备属于 root:root,这麻烦了,难怪OpenOCD挂了,我们要告诉一下USB子系统正确的权限

复制代码
sudo nano /etc/udev/rules.d/99-espressif.rules

写入如下内容

提示一下,ATTR{idVendor}=="303a", ATTR{idProduct}=="1001"的内容不要乱写 ,需要你看OpenOCD的这里的提示:esp_usb_jtag: VID set to 0x303a and PID to 0x1001

复制代码
# Espressif USB JTAG/Serial
SUBSYSTEM=="usb", ATTR{idVendor}=="303a", ATTR{idProduct}=="1001", MODE="0666", GROUP="wheel", TAG+="uaccess"

重启一下,然后记得拔掉USB设备重新插入

复制代码
sudo udevadm control --reload-rules
sudo udevadm trigger

注意,这个时候还需要重新的USB WSL Attach,这样设备才会正确的出现。

重新测试一下,现在可以进行调试了!

相关推荐
oshan20122 小时前
小华HC32L136K8TA 单片机通用定时器(五)
单片机·嵌入式硬件
qq_401700413 小时前
单片机电源电路设计常用芯片
单片机·嵌入式硬件
醇氧3 小时前
Idea防止工具栏自动隐藏
java·ide·intellij-idea
大侠课堂4 小时前
单片机经典面试题50道
arm开发·单片机·嵌入式硬件·mongodb
_infinite_5 小时前
STM32常用外设配置
stm32·单片机·嵌入式硬件
0欧姆5 小时前
VScode 创建 QNX 模板工程
ide·vscode·编辑器
普中科技6 小时前
【普中STM32F1xx开发攻略--标准库版】-- 第 17 章 STM32 中断系统
stm32·单片机·嵌入式硬件·arm·中断系统·普中科技
路人甲ing..6 小时前
用 Android Studio 自带的模拟 Android Emulator 调试
android·java·ide·ubuntu·kotlin·android studio
路人甲ing..6 小时前
Android Studio 模拟器报错 The emulator process for AVD xxxxx has terminated.
android·java·ide·kotlin·android studio