02. 创建第一个标准 ESP-IDF 工程
这节课要完成什么
这一课先不写 CAN。我们要使用 ESP-IDF 自带的标准命令,从一个空目录创建工程,然后完成(如果以前已经学会下面的操作,可以跳过该章):
创建工程 → 设置ESP32-C3目标 → 编译 → 烧录 → 查看串口日志

图里的六步就是本课顺序。后面每一份 ESP32 实验也会重复这条路径,因此现在应当逐步执行,不要只记住最后一条 flash monitor 命令。
只有这条基础链路通过,后面出现问题时才能把“开发环境故障”和“CAN 故障”分开。
开始前检查
# 在当前终端加载 ESP-IDF 环境;新开终端后需要重新执行。
source "$HOME/esp/esp-idf-v5.5.4/export.sh"
echo "$IDF_PATH"
idf.py --version
必须确认:
IDF_PATH=<你的主目录>/esp/esp-idf-v5.5.4
ESP-IDF v5.5.4
1. 先认识标准工程的最小结构
一个最小 ESP-IDF 工程通常是:
can_hello/
├── CMakeLists.txt
└── main/
├── CMakeLists.txt
└── can_hello.c
三个文件分别负责:
| 文件 | 用途 |
|---|---|
顶层 CMakeLists.txt | 声明 ESP-IDF 工程和工程名称 |
main/CMakeLists.txt | 告诉构建系统需要编译哪些源文件 |
main/can_hello.c | 包含 ESP-IDF 应用入口 app_main() |

初学时最容易混淆的是两个 CMakeLists.txt:顶层文件声明整个工程,main/ 中的文件声明应用组件要编译哪些源码。新增 .c 文件后若忘记修改后者,代码写得再正确也不会进入固件。
2. 查看命令帮助
# 创建一个全新的练习工程,并进入工程目录核对生成结果。
idf.py create-project --help
在 ESP-IDF v5.5.4 中,标准语法是:
idf.py create-project [OPTIONS] NAME
-p 或 --path 可以指定目标目录。如果目标目录非空,命令会拒绝覆盖。
3. 创建课程工作目录
课程建议把阶段工程放在独立目录,不修改最终 demo:
# 创建课程工作目录并进入该目录。
mkdir -p "$HOME/esp/can_course_workspace"
cd "$HOME/esp/can_course_workspace"
检查目标工程是否存在:
# 先确认目标路径不存在,避免覆盖之前的工程或源码。
test -e "$HOME/esp/can_course_workspace/can_hello"
echo $?
只有返回 1,表示路径不存在,才继续创建。
4. 用 idf.py 创建标准工程
# 创建一个全新的练习工程,并进入工程目录核对生成结果。
idf.py create-project \
-p "$HOME/esp/can_course_workspace/can_hello" \
can_hello
预期输出:
Executing action: create-project
The project was created in .../can_hello
列出文件:
# 列出实际生成的文件,名称和层级应与正文给出的结构一致。
find "$HOME/esp/can_course_workspace/can_hello" \
-maxdepth 3 -type f | sort
应该看到三个最小文件。
标准工程与示例工程的区别:
create-project创建空白标准骨架;create-project-from-example从组件注册表下载并复制一个官方示例。后面创建 CANopenNode heartbeat 工程时会使用第二种命令。
5. 看懂生成的入口
生成的 main/can_hello.c 是:
// ESP-IDF 工程不会使用桌面程序常见的 main(),而是由系统启动流程调用 app_main()。
// 这个空入口暂时不做任何事情,只用于确认 CMake、工具链和工程目录能够正常工作。
#include <stdio.h>
void app_main(void)
{
// 第一轮先保持为空。能够编译通过,就说明最小工程骨架没有问题。
}
ESP-IDF 应用入口叫 app_main(),不是普通桌面 C 程序中的 main()。ESP-IDF 启动系统和 FreeRTOS 后,会创建主任务并调用它。
6. 加入第一条日志
把 main/can_hello.c 修改为:
// esp_log.h 提供 ESP_LOGI() 等日志宏,比 printf() 多出级别、时间和 TAG 信息。
#include "esp_log.h"
// TAG 会显示在每条日志前面。后续日志变多时,可以用它判断输出来自哪个模块。
static const char *TAG = "can_course";
void app_main(void)
{
// app_main() 被调用且能看到这两行,才能证明固件已经真正运行,而不只是编译成功。
ESP_LOGI(TAG, "ESP-IDF environment is ready");
ESP_LOGI(TAG, "Next step: learn generic CAN before using TWAI");
}
第一行日志验证 ESP-IDF 应用运行成功。第二行提醒:TWAI 是后续 ESP32 实现课的内容,不是 CAN 标准本身。
7. 设置目标芯片
# 进入课程统一使用的 ~/esp 工作目录,后续相对路径都从这里计算。
cd "$HOME/esp/can_course_workspace/can_hello"
idf.py set-target esp32c3
这一步会:
- 把构建目标设置为 ESP32-C3;
- 重新生成项目配置;
- 创建或更新
sdkconfig; - 清理与旧目标不兼容的构建配置。
验证:
# 从 sdkconfig 读回目标芯片,结果应为 esp32c3。
rg 'CONFIG_IDF_TARGET=' sdkconfig
预期:
CONFIG_IDF_TARGET="esp32c3"
8. 编译
# 编译当前工程,先处理出现的第一条错误。
idf.py build
编译成功后会生成:
build/bootloader/
build/partition_table/
build/can_hello.bin
build/can_hello.elf
看到 Project build complete 只证明代码和环境能够生成固件,还没有验证开发板、USB 或硬件连接。
9. 烧录和监视串口
连接 ESP32-C3 后,直接执行:
# 烧录固件并打开串口监视器;按 Ctrl+] 退出监视器。
idf.py flash monitor
含义:
| 部分 | 作用 |
|---|---|
flash | 自动寻找串口,把固件写入 ESP32-C3 Flash |
monitor | 烧录完成后继续在当前终端查看串口日志 |
电脑只连接一块开发板时,一般不需要填写串口设备名。ESP-IDF Monitor 打开后会显示它实际使用的端口和波特率,例如:
--- idf_monitor on /dev/ttyACM0 115200 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
以后只想重新查看已经烧录好的程序,可以执行:
# 只重新打开串口监视器,不会再次烧录固件。
idf.py monitor
预期应用日志:
I (...) can_course: ESP-IDF environment is ready
I (...) can_course: Next step: learn generic CAN before using TWAI
退出 monitor:按 Ctrl+]。如果已经错过启动日志,依次按 Ctrl+T、Ctrl+R 复位开发板。
10. 常见问题
idf.py: command not found
当前终端没有加载 export.sh。
目标不是 esp32c3
重新确认当前工程目录,再执行 idf.py set-target esp32c3。
自动识别不到串口,或者电脑连接了多个串口设备
先检查 USB 线、开发板供电和 Linux 串口设备:
# 列出实际生成的文件,名称和层级应与正文给出的结构一致。
ls -l /dev/ttyUSB* /dev/ttyACM* 2>/dev/null
确定实际设备后再通过 -p 指定,例如:
# 烧录固件并打开串口监视器;按 Ctrl+] 退出监视器。
idf.py -p /dev/ttyACM0 flash monitor
-p 用于自动识别失败或多个设备无法区分的情况,不是每次烧录都必须填写。
Permission denied
当前用户没有串口访问权限,或串口被另一个 monitor 占用。不要同时打开多个串口监视器。
回头看一下刚才做了什么
我们先用 create-project 得到一个标准空白工程,再用 set-target 选择 ESP32-C3。build 负责生成固件,flash 把固件写入开发板,monitor 则用来查看程序实际输出。
这条链路跑通以后,下一课再加入 CAN。这样后面即使通信失败,也已经知道 ESP-IDF 环境、编译、烧录和串口本身可以正常工作。
离开这一课前
逐项确认;在 Markdown 编辑器中可以把已经完成的 [ ] 改成 [x]。
-
idf.py --version显示ESP-IDF v5.5.4。 -
sdkconfig中存在CONFIG_IDF_TARGET="esp32c3"。 -
idf.py build结束时出现Project build complete,并生成build/can_hello.bin。 -
idf.py flash monitor已自动打开开发板串口,或在多设备时通过-p指定了正确端口。 - 串口中出现
ESP-IDF environment is ready和Next step: learn generic CAN before using TWAI。
这五项都能确认,就可以继续下一课。
资料来源
- ESP-IDF v5.5.4
idf.py create-project --help实际输出。 - ESP-IDF 标准工程生成结果:顶层 CMake、main component 和
app_main()。