ESP-IDF基础
项目地址:https://github.com/espressif/esp-idf.git
文档:https://docs.espressif.com/projects/esp-idf/zh_CN/release-v5.5/esp32s3/get-started/index.html
(左上角选择硬件和版本)
安装VS code扩展:ESP-IDF,c++(cursor安装vsix版)、CMake Tools
vscode命令面板(F1):>ESP-IDF: Configure ESP-IDF extension 等待配置完成
然后选快速安装,选择一个源码文件夹,然后点安装,会需要很久
安装完,在ESP-IDF左侧插件中使用Advanced>New project Wizard创建新项目(wsl2先执行下面的转发串口)
如果需要使用arduino组件时推荐直接使用arduino模板
最终开发工程目录应该时独立于espidf目录之外,并因该单独打开新创建的工程目录为ide工作目录
启动带环境的终端:ide下方状态栏有个启动idf终端的按钮
或者F1:ESP-IDF: Open ESP-IDF Terminal
wsl2使用USBPD转发串口
PowerShell管理员启动运行(需梯子):winget install usbipd
或下下载安装包:https://github.com/dorssel/usbipd-win
在新的终端中生效,usbipd list 列出所有设备,以及查看是否被转发usbipd bind --busid <BUSID> 串口共享给usbipdusbipd attach --wsl --busid=<BUSID> 挂载到wsl默认发行版usbipd attach --wsl --busid=<BUSID> --auto-attach 自动共享usbipd wsl detach --busid <BUSID> 取消共享
wsl中执行lsusb查看,出现非Linux Foundation字符就是新识别到的设备
或者wsl中执行ls -l /dev/ttyUSB* /dev/ttyACM* 2>/dev/null 直接查看串口号
wsl一般串口为/dev/ttyACM0 或 /dev/ttyUSB0
识别到串口后ide下方的设备要选择串口
编译
ide下面有插件的快捷方式,把烧录方式从JTAG改成UART,然后点击火焰的小按钮就是一键编译+烧录+监听
构建前防止git错误,参照下面的错误解决方案idf.py set-target esp32s3 设置目标机器,执行前删除build文件夹,并且idf.py reconfigure 刷新依赖idf.py build 构建
如果提示包含idf.py flash 就是编译成功idf.py -p /dev/ttyUSB0 flash monitor 刷固件,并监听串口ctrl = ]停止监听串口
编译成功后,会出现:build/项目名.bin —— 你的应用固件(二进制)build/项目名.elf 带符号表的 ELF,可用于 gdb/回溯/size 分析build/bootloader/bootloader.bin bootloaderbuild/partition_table/partition-table.bin 分区表build/flash_args esptool 的参数清单(可直接被 esptool 引用)
groups | grep -q dialout || (sudo adduser $USER dialout && newgrp dialout)把当前用户加入 dialout 组,避免打开串口需要 sudo
idf.py -p /dev/ttyUSB0 monitor 尝试打印日志
idf.py -p /dev/ttyUSB0 flash monitor 刷机+监视
代码常识
宏定义是一种预处理器,常量和替代值#define LED =1;
添加组件
添加led等组件idf.py add-dependency "espressif/led_strip^2.4.1"
然后在main/CMakeLists.txt中添加REQUIRES led_strip driver
每次添加组件都要执行idf.py reconfigure
使用c++风格
将mian.c改成mian.cpp:
extern "C" void app_main(void) // 使用extern "C" 避免C++的命名修饰
{
//代码
}
main/CMakeLists.txt修改:idf_component_register(SRCS "main.cpp" INCLUDE_DIRS ".")
之后删除build执行idf.py reconfigure
错误排查
如果遇到git错误,将项目先设置为最小git防止出错,记得排除managed_components目录不上传
git init
git add .
git commit -m "初始化git防止出错"
esp-idf简易示范
// 目标:让板载 RGB 灯(常见为 WS2812/“霓虹灯”)循环“亮3秒 → 灭3秒”,
// 并在每次完整亮灭后,通过串口打印累计次数。
// —— 你需要知道的一点点 C/C++ 和 ESP-IDF 常识 ——
// 1) 这份文件是 .cpp(C++)源码,ESP-IDF 在启动后会回调 app_main() 作为应用入口。
// 2) #include 是“包含头文件”,让编译器知道 API 的声明(函数/类型/宏的定义位置)。
// 3) 函数通常返回 esp_err_t(错误码)。== ESP_OK 表示成功,其他值表示失败原因。
// 4) ESP_ERROR_CHECK(x) 是个“宏”:如果 x 不是 ESP_OK,就打印错误并复位(fail fast)。
// 5) FreeRTOS 是实时操作系统:vTaskDelay() 会“睡一会儿”,把 CPU 让给其他任务。
// 6) pdMS_TO_TICKS(ms) 把毫秒转换为“系统节拍数”(RTOS 的时间单位)。
// 7) “句柄”(handle)是对某个设备/对象的抽象引用(比如 led_strip_handle_t),像遥控器。
// 8) WS2812 这类 RGB 灯要“先写颜色到缓冲区,再 refresh() 发送”才会真正改变灯的状态。
// 9) C++ 的 nullptr 表示“空指针”;C 里通常是 NULL(本质类似)。这份代码用的是 C++ 风格。
// 10) extern "C" 告诉编译器:用 C 的方式导出函数名,方便 ESP-IDF 框架在 C 世界里找到 app_main。
// ===============================================================================
#include "freertos/FreeRTOS.h" // FreeRTOS 核心:任务/调度/时间等基础定义
#include "freertos/task.h" // FreeRTOS 任务 API:vTaskDelay()、xTaskCreate() 等
#include "esp_log.h" // 日志模块:ESP_LOGI / ESP_LOGW / ESP_LOGE 打印到串口
#include "esp_err.h" // 错误码枚举与工具:esp_err_to_name()、ESP_ERROR_CHECK()
#include "led_strip.h" // “可编程灯带”抽象驱动(WS2812/SK6812 等 RGB 灯)
#include "driver/rmt_tx.h" // RMT(遥控发射器)外设:用来按精确时序驱动 WS2812
// extern "C":因为我们用 C++ 编译(.cpp),但 ESP-IDF 在 C 的世界里寻找 app_main 符号;
// 加这句可避免“名字被C++修饰(mangling)”,从而确保系统能正确调用 app_main。
extern "C" void app_main(void) {
// TAG 是日志用的“模块名”,方便筛选输出
static const char *TAG = "RGB_BLINK";
// 这里写的是板载 RGB 灯的“数据引脚”编号:
// 绝大多数 ESP32-S3 DevKitC-1 是 GPIO 48;极少数修订/克隆板可能接到 38。
const int LED_GPIO = 48;
// ===================== 1) 创建设备:把“灯带驱动 + RMT”准备好 =====================
// 句柄(handle)是我们操作“灯”的遥控器,创建成功后通过它 set_pixel()/refresh()
led_strip_handle_t strip = nullptr; // 初始为空,后面 new_rmt_device 成功才会赋值
// led_strip 的通用配置体。用 {} 把所有字段“零初始化”,避免出现随机垃圾值。
led_strip_config_t cfg{};
cfg.strip_gpio_num = LED_GPIO; // 选择输出到哪个 GPIO 引脚
cfg.max_leds = 1; // 本板通常只有 1 颗可编程 RGB 灯
cfg.led_model = LED_MODEL_WS2812; // 指定灯的型号(内部会用对应的时序)
cfg.flags.invert_out = false; // 是否反相输出(绝大多数板为 false)
// RMT(发射端)配置体。RMT 负责“精确时序发送”WS2812 所需的脉冲波形。
led_strip_rmt_config_t rmt{};
rmt.clk_src = RMT_CLK_SRC_DEFAULT; // 使用默认时钟源
rmt.resolution_hz = 10 * 1000 * 1000; // 10MHz 分辨率:1 tick = 100ns
rmt.mem_block_symbols = 64; // RMT 内部缓冲的深度(1 颗灯够用)
rmt.flags.with_dma = false; // 1 颗灯无需 DMA
// 创建设备:如果失败(GPIO 被占用/参数错误/内存不足等),ESP_ERROR_CHECK 会打印并复位
ESP_ERROR_CHECK(led_strip_new_rmt_device(&cfg, &rmt, &strip));
// 上电先关灯(把内部像素缓冲区清零),再 refresh() 把“全灭”状态真正发出去
ESP_ERROR_CHECK(led_strip_clear(strip));
ESP_ERROR_CHECK(led_strip_refresh(strip));
// ======================== 2) 主循环:亮3秒 → 灭3秒 → 计数 ========================
uint32_t loop_cnt = 0; // 记录完成了多少次“亮+灭”的完整循环
while (true) { // 无限循环:在 FreeRTOS 下,任务会一直运行,直到你主动退出
// ------- 点亮:先写入像素颜色(缓冲区),再 refresh() 发送到灯 -------
// WS2812 是“RGB 三色”,范围 0~255。这里设置为纯蓝色 (R=0,G=0,B=255)。
ESP_ERROR_CHECK(led_strip_set_pixel(strip, /*index=*/0, /*R=*/0, /*G=*/0, /*B=*/255));
ESP_ERROR_CHECK(led_strip_refresh(strip)); // 真正把颜色发给灯(不 refresh 看不到效果)
vTaskDelay(pdMS_TO_TICKS(3000)); // 任务延时 3000ms:保持亮 3 秒
// ------- 熄灭:把像素缓冲清零,再 refresh() 发送 -------
ESP_ERROR_CHECK(led_strip_clear(strip)); // 清零像素(相当于 R=G=B=0)
ESP_ERROR_CHECK(led_strip_refresh(strip)); // 发送“全灭”
vTaskDelay(pdMS_TO_TICKS(3000)); // 保持灭 3 秒
// 完成一个“亮+灭”的周期后,打印累加次数。I = info(信息级)日志。
ESP_LOGI(TAG, "循环次数=%lu", (unsigned long)++loop_cnt);
// 提示:你若频繁打印,会看到“靠近 USB 口的小蓝灯”闪一下,那是串口指示灯,和 RGB 大灯不是同一个。
}
// 注意:一般不会走到这里。若你要退出任务,可以 return 或 vTaskDelete(nullptr)。
}