这一篇先把 ESP32 开发环境装好:用 EIM(ESP-IDF Installation Manager) 安装 ESP-IDF 和工具链,再让 VS Code 的 ESP-IDF 插件识别它,最后创建一个项目并编译验证。这里的 Ubuntu 26 指 Ubuntu 26.04 桌面版;命令适用于常见的 amd64 和 arm64 架构。实际刷机还需要一块 ESP32 开发板和数据线。
下文按 2026-09-23 查阅的乐鑫和 VS Code 官方文档编写。EIM 的界面和最新稳定版号会变化,所以安装时选择界面显示的稳定版,不把某个版本号写死。文章中的 Ubuntu 命令尚未在 Ubuntu 26.04 实机上逐条执行;每一步都给出可以在目标机器上核对的结果。
先弄清三个东西
| 名称 | 作用 |
|---|---|
| ESP-IDF | 乐鑫的开发框架,包含项目模板、组件、idf.py 等 |
| EIM | 安装和管理 ESP-IDF、Python 环境及交叉编译工具链 |
| VS Code + ESP-IDF 插件 | 编辑项目、选择芯片与串口、编译、烧录和查看日志 |
ESP-IDF 6.0 起,乐鑫的 Linux 入门文档把 EIM 列为默认安装方式。下面先用 EIM 安装一次,VS Code 只连接这套现有环境,避免重复下载工具链。参见乐鑫 Linux 安装说明。
1. 确认 Ubuntu 和架构
打开终端:
1cat /etc/os-release
2dpkg --print-architecture
3python3 --version
检查 VERSION_ID 是否为 26.04,架构是否为 amd64 或 arm64。如果机器还没有装 Git,可先运行 sudo apt update && sudo apt install git。使用下面的 EIM APT 包时,乐鑫文档说明可跳过手动准备 EIM 前置依赖;若安装器报告缺少组件,再按报错补装,不必先把整套 ESP-IDF 依赖装两遍。
2. 安装 EIM
乐鑫提供 Debian/Ubuntu 的 APT 仓库。下面安装带图形界面的 eim,它也包含 CLI:
1echo "deb [trusted=yes] https://dl.espressif.com/dl/eim/apt/ stable main" | sudo tee /etc/apt/sources.list.d/espressif.list
2sudo apt update
3sudo apt install eim
4eim --help
eim --help 能输出命令帮助,就说明可执行文件已经进入 PATH。只在纯命令行服务器上使用时,可以把 sudo apt install eim 换成 sudo apt install eim-cli。
上面的仓库配置照录自乐鑫官方安装文档。其中 trusted=yes 会让 APT 对这个仓库跳过通常的包签名校验;先确认域名确实是 dl.espressif.com,并只在接受此信任方式时使用。若不愿添加此仓库,可从乐鑫 EIM 下载页获取适合自己架构的安装包,按下载页说明安装。
3. 用 EIM 安装 ESP-IDF
图形界面
在终端运行 eim,或从应用菜单打开 ESP-IDF Installation Manager。依次选择 New Installation → Start Installation → Easy Installation → Start Easy Installation,使用当前稳定版和默认目录。通过前置检查后点击 Start Installation。等到 Installation Complete,再到安装管理页面确认版本已出现。若下载或安装失败,先打开界面底部的 Logs 看具体错误。
命令行
在有网络的终端运行:
1eim install
这会按默认设置安装当前稳定版。希望交互选择版本、镜像、特性或安装位置时,改用:
1eim wizard
例如项目确实需要某个特定版本,可先用 eim --help 核对本机参数,再运行 eim install -i v5.4.2。不要同时启动 GUI 和 CLI 对同一个安装目录进行安装。EIM 成功时会报告 Successfully installed IDF;还应检查生成的安装记录:
1test -f "$HOME/.espressif/tools/eim_idf.json" && echo 'EIM 安装记录存在'
2find "$HOME/.espressif/tools" -maxdepth 1 -name 'activate_idf_*.sh' -print
EIM 默认在 ~/.espressif/tools/eim_idf.json 写入供 VS Code 识别的配置;使用自定义目录时,应以安装器显示的实际路径为准。上面的激活脚本按安装版本命名。它必须通过 source 加载到当前终端:
1source "$HOME/.espressif/tools/activate_idf_版本号.sh"
2idf.py --version
把 版本号 替换为刚才 find 输出的真实文件名,例如 activate_idf_v6.0.sh;不要直接执行脚本,也不要随意把某个版本的激活命令永久写进 ~/.bashrc。每次新开终端需要使用 idf.py 时,重新 source 对应脚本。EIM 的激活脚本说明也说明它同时支持 Bash 和 Zsh。
4. 安装 VS Code
这里使用微软官方 .deb 包,避免 Snap 沙箱给 EIM 图形界面和串口访问带来额外问题。打开 VS Code 下载页,选择与你的 dpkg --print-architecture 对应的 Debian/Ubuntu .deb 包,下载后在包所在目录执行:
1sudo apt install ./code_*.deb
2code --version
若当前目录中有多个 code_*.deb,请把通配符改成要安装的准确文件名。官方说明 .deb 安装过程会询问是否添加 VS Code 的 APT 软件源,选择添加后,以后可以通过系统更新获取新版。具体步骤见VS Code Linux 安装文档。
5. 安装并配置 ESP-IDF 插件
打开 VS Code,按 Ctrl+Shift+X 进入扩展市场,搜索 ESP-IDF,确认发布者是 Espressif Systems 后安装。也可以在终端执行:
1code --install-extension espressif.esp-idf-extension
安装完成后,按 F1 打开命令面板,运行 ESP-IDF: Select Current ESP-IDF Version,从列表选择刚才 EIM 安装的版本。再运行 ESP-IDF: Doctor Command,检查插件、Python 环境和工具链路径。插件会从 EIM 的 eim_idf.json 自动发现安装记录;如果列表为空,先确认文件存在,再在设置里指定 idf.eimIdfJsonPath 为真实的 eim_idf.json 绝对路径。参见插件安装与配置文档。
如果已经用 EIM 安装 ESP-IDF,插件提示打开 Installation Manager 时,无需再安装一遍。先执行 Select Current ESP-IDF Version 选中现有环境。VS Code 集成终端若要手动运行 idf.py,同样需要在该终端 source 对应的激活脚本。
6. 建立项目并验证
按 F1 运行 ESP-IDF: New Project,选择 EIM 安装的版本,再选择 get-started/hello_world 示例。填写项目名和保存位置,Target 要与开发板的实际芯片型号一致,例如经典 ESP32 选 esp32,ESP32-C3 选 esp32c3;不确定时看开发板或芯片丝印。点击 Create Project,随后打开新项目。
运行 ESP-IDF: Build your Project。编译完成并显示 Building done,才算软件环境的第一轮验证通过;安装器完成或 Doctor 无报错都不能代替一次真实编译。新建项目和编译的界面步骤可对照官方文档。
有开发板时,用支持数据传输的 USB 线连接,运行 ESP-IDF: Select Port to Use,选择对应的 /dev/ttyUSB* 或 /dev/ttyACM*,再运行 ESP-IDF: Flash your Project,一般选 UART。烧录成功后用插件的 Monitor 查看启动输出。也可在已经激活 ESP-IDF 的终端、项目根目录执行:
1idf.py -p /dev/ttyUSB0 flash monitor
/dev/ttyUSB0 仅为例子,应换成设备实际端口。flash 会自动编译;看到 Hello world! 之类的示例日志才说明编译、串口、烧录和程序运行都走通。相关命令见乐鑫的第一个项目指南。
上面这些界面操作的命令行对应版本,就是下一节。
7. 用 idf.py 从命令行开发
idf.py 是 ESP-IDF 的命令行前端:它在背后调用 CMake 生成构建文件、Ninja 编译、esptool 烧录。上一节用 VS Code 插件做的事都能在这里用命令完成,而且更适合远程登录(比如 SSH 到 Ubuntu 主机、没有图形界面)和写脚本。
两条前提:
- 当前终端已经用
source激活过 ESP-IDF(见第 3 节),因为idf.py和交叉编译器都在激活脚本设置的PATH里。 - 命令在工程根目录执行,也就是含有顶层
CMakeLists.txt的目录。只有Makefile的老式工程idf.py不支持。
1source "$HOME/.espressif/tools/activate_idf_v版本号.sh"
2cd "$HOME/esp/你的工程"
3idf.py --version
4idf.py --help
idf.py --version 输出当前激活的 ESP-IDF 版本;idf.py --help 列出全部子命令;idf.py --list-targets 列出这个版本支持的芯片目标。子命令自己的选项用 idf.py monitor --help 这种形式查。
本节按乐鑫 idf.py 文档整理,尚未在 Ubuntu 26.04 上逐条执行。每条命令都给出可以在目标机器上核对的结果;串口名和权限问题与第 6 节相同。
7.1 创建工程
1mkdir -p "$HOME/esp"
2cd "$HOME/esp"
3idf.py create-project hello_cli
4cd hello_cli
5find . -maxdepth 2 -type f | sort
create-project 只搭骨架,不管芯片型号。关键是这三个文件:
1hello_cli/
2├── CMakeLists.txt # 顶层:cmake_minimum_required / include project.cmake / project(hello_cli)
3└── main/
4 ├── CMakeLists.txt # 用 idf_component_register 注册 main 组件
5 └── hello_cli.c # 一个空的 app_main(),直接返回
build/ 目录和 sdkconfig 要到第一次编译才生成。此时直接 idf.py build 就能编过,得到的是一个什么都不做就退出的固件;但这时目标芯片还是默认的 esp32,要用自己的芯片得先 set-target(见下一小节)。
创建工程相关的子命令:
| 命令 | 作用 |
|---|---|
idf.py create-project 名字 | 在当前目录创建新工程 |
idf.py create-project 名字 --path 目录 | 指定创建到哪个目录 |
idf.py create-project 名字 --cpp | 生成 .cpp,并自动用 extern "C" 包住 app_main |
idf.py create-component 组件名 | 新建组件骨架,-C 目录 指定创建位置 |
idf.py create-project-from-example "命名空间/组件名=版本:示例名" | 从 ESP Component Registry 的示例创建工程 |
create-project-from-example 的参数格式是 namespace/name=1.0.0:example,双引号不能省,否则 shell 会先把 = 和 : 解释掉;这个命令需要联网下载组件。
从空工程一路改到能点亮 LED 的完整过程,包括改 main/CMakeLists.txt、写 sdkconfig.defaults、分层成 BSP,见下一篇。
7.2 选择目标芯片
1idf.py set-target esp32s3
一条命令完成“清空 build → 备份 sdkconfig → 以新目标重新配置”。核对方式:命令结束后 sdkconfig 里出现 CONFIG_IDF_TARGET="esp32s3"。
set-target 会清掉已有配置官方文档明确说明 idf.py set-target 等价于 idf.py fullclean + mv sdkconfig sdkconfig.old + 以新目标 reconfigure。也就是说 build 目录被清空、sdkconfig 从零重建,之前在 menuconfig 里改过的选项全部失效(旧文件备份成 sdkconfig.old)。所以先定芯片,再调配置。
如果希望工程 clone 下来就默认是正确的芯片,把目标写进工程的 sdkconfig.defaults:
1CONFIG_IDF_TARGET="esp32s3"
官方文档给出的关系是:只要用了 idf.py set-target、-DIDF_TARGET=...(CMake 变量)或环境变量 IDF_TARGET,就以它们为准;这三者都没有时,才用 sdkconfig.defaults 里的 CONFIG_IDF_TARGET;连它也没有,则默认编译 esp32。
7.3 配置工程:menuconfig 与 sdkconfig
1idf.py menuconfig
菜单里的常用按键:方向键移动,Enter 或 Space 进入菜单/切换取值,ESC 返回上一级,? 查看当前选项的帮助,/ 按名字搜索选项,Q 退出(会先问是否保存)。不同版本的菜单界面不完全一样,以界面底部的按键提示为准。
两个配置文件的分工:
| 文件 | 谁写 | 用途 |
|---|---|---|
sdkconfig | menuconfig 自动生成 | 本工程的实际配置,不要手改 |
sdkconfig.defaults | 你手写 | 工程基线,sdkconfig 重建时作为默认值 |
「不要手改 sdkconfig」的原因是选项之间有依赖关系,手工拼出来的组合可能自相矛盾;要改就通过 menuconfig。改完之后,如果想在工程里留一份可版本管理的最小配置,用:
1idf.py save-defconfig
它把「与默认值不同的项」写进 sdkconfig.defaults,比手工抄一堆 CONFIG_... 可靠。删掉 sdkconfig 再 idf.py build,构建系统会先按 sdkconfig.defaults 生成新的 sdkconfig,这条路径正好可以用来验证默认值有没有写对。
7.4 编译
1idf.py build
核对:结尾出现 Project build complete,命令返回 0,build/ 下出现三类产物:
1ls build/*.bin build/bootloader/*.bin build/partition_table/*.bin
build/hello_cli.bin:应用固件,文件名来自顶层CMakeLists.txt里的project()。build/bootloader/bootloader.bin:二级引导程序。build/partition_table/partition-table.bin:分区表。
编译是增量的:源码和配置都没变时什么都不会重做。几个常用开关:
| 命令 | 作用 |
|---|---|
idf.py app / bootloader / partition-table | 只编译应用 / 引导程序 / 分区表 |
idf.py -j 6 build | 限制并行编译任务数(等价于 IDF_PY_BUILD_JOBS=6 idf.py build) |
idf.py -v build | 打印真正的编译命令行,排查编译错误时有用 |
idf.py --ccache build | 启用 ccache 加速重复编译 |
idf.py build --no-hints | 关掉出错时自动给出的修复提示 |
清理相关命令的区别值得记住:
| 命令 | 删掉什么 | 什么时候用 |
|---|---|---|
idf.py clean | build 里编译产物,保留 CMake 配置 | 怀疑增量编译结果不对 |
idf.py fullclean | 整个 build 目录 | 换目标、换工具链、CMake 报奇怪的错 |
idf.py reconfigure | 不删文件,强制重跑 CMake | 增删了源文件或组件、改了 CMake 变量 |
fullclean 会递归删除 build 目录里的所有东西官方文档特别提醒这一点。它不碰 sdkconfig,但 build 目录里如果放了自己的东西,就一起没了。
固件占多大,用 size 系列命令看:
1idf.py size
2idf.py size-components
3idf.py size-files
size 报告 RAM、Flash 占用和各段大小,用来判断固件塞不塞得进分区;换成 size-components / size-files 可以定位到具体组件或源文件。加 --format csv|json2 便于脚本处理。
7.5 烧录与串口监视
1idf.py -p /dev/ttyUSB0 flash
2idf.py -p /dev/ttyUSB0 monitor
-p 指定串口,-b 指定烧录波特率(例如 -b 921600)。不想每次都敲端口,可以用环境变量给默认值:
1export ESPPORT=/dev/ttyUSB0
2export ESPBAUD=921600
flash 会先自动编译,所以日常改完代码只需要一条命令。它默认是增量烧写:如果 build/ 里留着上次烧录的 *_flashed.bin,就只写变化过的扇区,写完再校验 flash 内容与预期是否一致;没有这些记录时,则先比对设备 flash 里的内容,已经一致的文件跳过。换新板子或刚擦过 flash 时用 idf.py flash -a(或 --all)强制全量烧写。
monitor 是串口监视器,退出按 Ctrl+]。它还有一层以 Ctrl+T 开头的快捷键:
| 快捷键 | 作用 |
|---|---|
Ctrl+T Ctrl+H | 列出全部快捷键 |
Ctrl+T Ctrl+F | 不退出监视器,重新编译并烧录 |
Ctrl+T Ctrl+Y | 暂停/恢复屏幕上的日志输出 |
Ctrl+T Ctrl+R | 通过 RTS 复位开发板 |
日志太多时可以只打印关心的部分:
1idf.py monitor --print-filter="wifi:I esp_image:E"
规则是 标签:级别,级别取 N E W I D V * 中的一个;不写级别等于 *(Verbose,即全部打印)。默认监视器在连接时会复位芯片,只想看已有输出、不希望复位时加 --no-reset。
把 flash 擦回空白状态:
1idf.py -p /dev/ttyUSB0 erase-flash
它擦的是整片 flash,包括 NVS 里保存的数据,不只是应用分区。改分区表、做 OTA 实验、或者想从“出厂空白”状态重来时用。
7.6 组合命令与日常循环
多条子命令可以写在一次调用里,idf.py 会自动按正确顺序执行(该先编译的先编译,该先擦除的先擦除),顺序写反也没关系:
1idf.py -p /dev/ttyUSB0 erase-flash flash monitor
日常改代码的循环通常就一条:
1idf.py -p /dev/ttyUSB0 flash monitor
把这一节用到的命令汇总:
| 命令 | 作用 |
|---|---|
idf.py create-project 名字 | 新建工程 |
idf.py create-component 名字 | 新建组件 |
idf.py set-target 芯片 | 选择目标芯片(清空 build、重建 sdkconfig) |
idf.py menuconfig | 图形化配置工程 |
idf.py save-defconfig | 把改动导出成 sdkconfig.defaults |
idf.py build | 编译 |
idf.py -p 串口 flash | 编译并烧录 |
idf.py -p 串口 monitor | 打开串口监视器(Ctrl+] 退出) |
idf.py -p 串口 erase-flash | 擦除整片 flash |
idf.py size | 查看固件占用 |
idf.py clean / fullclean | 清理构建产物 / 清空 build 目录 |
idf.py reconfigure | 强制重跑 CMake |
idf.py docs | 用浏览器打开当前版本、当前芯片的文档 |
几个跨子命令的全局选项,写在 idf.py 和子命令之间:
| 选项 | 作用 |
|---|---|
-C 目录 | 操作指定工程,不必先 cd |
-B 目录 | 换 build 目录(默认 build) |
-p 串口 / -b 波特率 | 串口与烧录波特率 |
-D 变量=值 | 传 CMake 变量,例如 idf.py -DIDF_TARGET=esp32s3 reconfigure |
-v | 输出详细编译过程 |
--ccache | 启用 ccache |
--no-hints | 关掉错误修复提示 |
两点容易踩的坑:
- 连字符不是下划线。 从 ESP-IDF v5.0 起子命令统一用连字符(
erase-flash、size-components),旧的下划线写法(erase_flash)只会给一条弃用警告。看到教程里写下划线,说明那份教程比较老。 - 参数可以写进文件。 形如
idf.py @my_flash.txt monitor的写法会把文件内容当成命令行参数展开,文件里每行可以写一个参数加取值(例如flash --baud 115200),适合固定一组常用参数。
常见问题
VS Code 找不到已安装的 ESP-IDF
先检查 ~/.espressif/tools/eim_idf.json 是否存在,再用 ESP-IDF: Select Current ESP-IDF Version 选择。自定义目录安装时,把 idf.eimIdfJsonPath 指向实际 JSON 文件。最后运行 ESP-IDF: Doctor Command 查看插件报告,不要凭猜测手写 IDF_PATH。
idf.py: command not found
新终端尚未激活环境。用 find "$HOME/.espressif/tools" -maxdepth 1 -name 'activate_idf_*.sh' -print 找到脚本,source 正确版本后再试 idf.py --version。
串口 Permission denied
先用 ls -l /dev/ttyUSB0(或实际端口)查看设备所属组。Ubuntu 上常见为 dialout,若确实如此,运行:
1sudo usermod -aG dialout "$USER"
随后退出登录并重新登录,用 groups 确认当前会话已包含 dialout,再重新插拔开发板并烧录。不要用 sudo idf.py 掩盖权限问题。如果设备组不是 dialout,按实际组处理。参见乐鑫串口权限说明。
EIM 窗口在 VS Code 中打不开
先在系统终端直接运行 eim;如果通过 Snap 安装了 VS Code,留意沙箱限制。乐鑫的插件排错文档建议改用官方 .deb 版本,并可通过 ESP-IDF: Doctor Command 定位 EIM 路径问题。