esp-idf-cy — ESP-IDF 编译/构建/烧录(agent 能力为主,脚本为辅)
你在处理什么
用户可能用 Mac 也可能用 Windows,项目在任何位置,ESP-IDF 装没装、怎么装的、装的哪个版本都不确定。
你的职责:用户一句"编译烧录",你负责搞清环境 → 缺什么补什么 → 编译 → 烧录 → 验证。
环境差异和故障靠你自己的侦察和判断吸收,不指望用户懂环境,也不指望脚本枚举了所有情况。
宿主边界:本 skill 运行在已经能执行 Agent 工具的会话里。macOS/Linux 需要可调用 Bash;
Windows 需要 Agent 宿主提供 Git Bash,或由 Agent 直接用系统 PowerShell 调用
scripts/eim-windows.ps1。它会自动补齐 ESP-IDF 所需的 EIM、Python、Git 和工具链,
但不能在自己尚无法执行时“先安装自己的 shell/Agent 宿主”。不要把这个边界甩给新手;
若当前 Windows 会话没有 Bash,就由你改走已有 PowerShell,而不是要求用户学习 EIM。
新手第一次使用的体验目标
默认把用户当作可能完全没装过、也不需要先理解 ESP-IDF 安装体系的新手:
- 用户只需要提供能知道的业务信息:板子/芯片型号、已有项目路径,以及想编译还是烧录。
能从已连接板子、项目
sdkconfig/README 自动判断的就自己判断;只有判断不出且会改变 GB 级
下载目标或烧录安全性时,才问一个简短问题。
- EIM、
export.sh、Python 虚拟环境、工具链包和镜像都是内部实现细节。默认只报告结果,
不把安装教程、官网链接或一串依赖清单甩给用户;只有失败定位或用户主动追问时才解释。
doctor.sh 若证明现有环境 READY=yes,就复用探测到的精确版本和位置,继续用户的任务。
不要因为有更新的 stable 就静默升级、重装或切换版本。
- 确认没有可用环境时才自动安装:已有项目优先服从项目锁定的精确版本;新项目用
stable;
从板子推导并显式传 --targets,避免默认下载全部芯片工具链。安装完成必须真实执行
idf.py --version;若用户要“从零开始/跑起来”,再用对应 target 构建官方最小示例完成端到端验证。
- 正常情况下不要让用户暂停去手工装 Python/Git/SDK 或访问网页。只在系统权限/系统弹窗
(如 macOS Command Line Tools)、缺少无法探测的板型信息,或真实烧录确认时请用户介入。
- 完成后用新手听得懂的三件事收口: 装好了什么版本和位置、验证到了哪一步、下一句可以怎么说。
不要用 EIM 等实现名词作为成功摘要的主角。
Agent-first 动态决策,不要把示例路径当事实
- 先看当前 Agent 实际拥有的能力(shell、PowerShell、Computer Use、网络、文件权限),再选路线。
脚本提供确定性检查和安全门禁,但脚本没覆盖的机器差异由你继续侦察、读日志和处理,不能把
“不在默认目录”直接等同于“没有安装”。
- 路径来自用户上下文和机器证据:项目
build/project_description.json、IDF_PATH、EIM 登记、
IDE 设置、进程环境和经过边界限制的文件搜索。~/esp、C:\esp 只是最后的惯例候选;
找到候选后必须验证 tools/idf.py 和真实 idf.py --version。
- GUI 不是默认依赖。CLI 能完成就由 Agent 直接做;用户明确要求 GUI 且当前宿主有 Computer Use 时,
Agent 可操作 EIM 的普通按钮、版本选择和日志页面。密码、Touch ID、UAC、macOS 安全确认及许可
接受属于系统信任边界,必须让用户本人处理。没有 Computer Use 时自动回到 CLI,不要把一串点击
教程丢给新手。
ACTION_REQUIRED/rc=20 表示可恢复的人机交接:说明用户只需完成哪个系统动作,完成后由 Agent
重跑原命令并续上。不要让用户重新理解整套安装流程。
三层职责不要混淆:
- **你(Agent)**负责侦察证据、选择路线、解释异常、向用户取得烧录确认并持续推进。
- **
SKILL.md**只给决策顺序、安全红线、完成标准和按需读取 reference 的路由。
- **
references/**保存会随 ESP-IDF/EIM/平台变化的命令与排错知识;执行前以实机和当前官方能力复核。
- **
scripts/**提供确定性探针、可重入默认安装动作,以及验签、argv传递、身份解析、精确验证、
超时等机械边界。它们可以执行你已选定的路线,但不是决策者或用户授权门禁;输出与实机冲突时
继续调查,不能盲信或无脑重跑。
按任务渐进读取,不要一次把全部知识塞进上下文:
| 当前阶段 |
先读 |
| 安装、修复、EIM、PowerShell-only |
references/install-playbook.md |
| 国内网络或镜像失败 |
references/china-mirrors.md |
| target、idf.py、esptool、EIM 命令 |
references/idf-commands.md |
| 已有报错、驱动、BOOT/RESET、串口异常 |
references/troubleshooting.md |
| 设备命名、收录、改名、版本快照 |
references/device-registry.md |
五条物理事实,决定了下面所有做法:
export.sh 只对当前 shell 生效,而你每次 Bash 调用都是新 shell
→ 每条 idf 命令都要在同一条命令里先装配环境。
- 你没有交互终端 → menuconfig(TUI)和官方
idf.py monitor(强制要求 TTY)
对你不可用;配置改 defaults,日志验证用有界的 pyserial 采集器。
- 烧录/复位会让 USB 重枚举 → 端口名不能跨步骤缓存,每次烧录/监视前重扫。
- 烧录 = 覆盖用户硬件上的固件,板上可能跑着用户其他项目
→ 未验明设备身份并经用户确认,不烧。这条不因任何理由松动。
- flash 退出 0 ≠ 新固件已经运行 → 手动下载模式可能仍停在 ROM;只有目标身份已重验、
应用专用健康标志命中且没有 ROM 下载证据,才能报告烧录闭环完成。
设备长期记录再加两条硬边界:
- 名称只负责可读性:底层始终按 fresh MAC 关联。多设备时先由 Agent 扫描并逐块验身,
再把当前端口、芯片、名称和 MAC 尾号列给用户选;只有无法安全逐块连接时才请用户暂时拔掉一块。
- 版本必须带来源:存储 schema 分开保存 environment/project/firmware,不得只有一个通用
idf_version。界面可把证据充分的“当前固件 IDF”作为摘要,但不能丢掉另外两类观测或把摘要
当设备永久属性。
工作方式:侦察 → 判断 → 行动 → 验证
以你自己的能力为主:自己跑命令探测、自己读报错、自己决定下一步。
脚本和文档是工具不是流程——脚本覆盖不了的情况,你直接上手。
侦察:环境线索在哪
省事的话跑 bash <skill>/scripts/doctor.sh 一次拿全景(KEY=VALUE 输出)。
自己侦察时的线索地图:
- IDF 在哪:项目已有 build 时先读
build/project_description.json 的 idf_path,再看 $IDF_PATH;
EIM 登记文件 eim_idf.json(POSIX 在
~/.espressif/tools/,Windows 在 C:\Espressif\tools\);惯例路径 ~/esp/esp-idf*。
仍没有时检查项目 .vscode/settings.json/CI/终端配置,再只对用户目录、已知 SDK 根目录或实际磁盘
做有界搜索,不要无界扫描整盘。版本用 git -C <目录> describe --tags 或真命令拿,别信目录名。
- 工具链健康度:
~/.espressif/python_env/idf<IDF版本>_py<py版本>_env——目录名本身
编码了它绑定的 python 版本,系统 python 升级过它就失效(经典坑)。
- Python 闸门:优先读取所选 IDF 自带的版本检查/当前 EIM 前置结果;reference 里的版本表只是
已验证快照。遇到未知的新 major 必须查当前证据或 fail closed,不能默认回落到旧 Python 下限。
- 串口:mac 看
/dev/cu.usbmodem*(乐鑫原生口)和 /dev/cu.usbserial*/SLAB*/wch*(桥接);
linux 看 /dev/ttyACM*//dev/ttyUSB*;Windows 用 PowerShell
Get-CimInstance Win32_PnPEntity -Filter "PNPClass='Ports'",乐鑫原生口 DeviceID 含
VID_303A&PID_1001。
- 网络:GitHub 不畅但国内可达 → IDF仓库、工具资产和pip优先走乐鑫官方机制
(
references/china-mirrors.md)。EIM本体自举是否有国内官方资产要按当期来源另查;不要先把VPN甩给用户,
也不要为“能下载”绕过hash/签名。
- 探到多个 IDF 版本或奇怪位置 → 把候选列给用户选,别猜。
环境装配:每条 idf 命令的写法
安装或修复(先判断状态再做)
- 先以
doctor.sh 的真实命令结果和你的现场证据做三态分流:
READY=yes:原样复用,不安装、不升级。
- 已发现精确安装但真命令/工具损坏:EIM管理的优先
eim fix -p <精确路径>,legacy才重跑该仓库
的官方安装工具;修完仍对同一路径严格复检。
- 确认不存在兼容环境:才新安装。多个候选或证据冲突时继续调查或只问一个会改变结果的问题。
- 省事路径:
bash <skill>/scripts/install.sh [--version stable|v5.5|v5.5.4] [--targets esp32s3]
——幂等、可安全重跑、国内自动镜像。首次 clone 走同级受管暂存区,完整后才原子落位;
子模块与工具下载可重复执行。下载量以 GB 计,后台跑或放宽超时。
- macOS 空白机:
install.sh 先侦察再动态选路。已有可用 EIM 时直接复用;已有 Homebrew 且
未指定精确仓库路径时,按 ESP-IDF v6.0+ 官方默认路线补齐乐鑫列出的前置、安装/复用 EIM CLI
并非交互安装。EIM 与 Homebrew 都没有或用户传了 --path 时,不静默引入包管理器,改走仍受官方支持的
bootstrap-macos.sh + ESP-IDF install.sh 路线。缺 Command Line Tools 时主动
拉起苹果系统安装窗口;缺兼容 Python 时,已有 Homebrew 就自动安装,没有 Homebrew 就从
Python.org 下载固定版本的 universal2 官方包,同时校验 SHA256 和 Python Software Foundation
签名后才打开系统安装器。系统 UI 必须由用户本人确认;脚本返回 ACTION_REQUIRED/rc=20 时
不是安装失败,等用户完成后重跑原命令即可续上。不要静默安装 Homebrew,也不要用 curl|sh
引入第三方 Python。EIM 在 POSIX 只检查前置:有 Homebrew 时 install-eim-macos.sh 先安装官方
清单;只有 EIM 时允许它先做真实检查,失败后 Agent 根据缺项换官方脚本路线或向用户说明选择。
不能把“EIM 已装”误当成“所有依赖已齐”。
- 脚本失败或不适用(特殊版本/公司代理/非常规位置):别无脑重跑。读它的输出定位卡点,
按
references/install-playbook.md 的配方自己一步步来——每一步都能单独重试和替换。
- 版本选择:先看项目 README/CI/容器配置;已有项目跟它的精确版本。新项目默认
stable;--version v5.5 表示“5.5 系列最新 patch”,要复现才传 v5.5.4 这类精确 tag。
--targets 从板子/项目推导并显式传入(C6→esp32c6);候选必须以所选 IDF 的
idf.py --list-targets/当前官方元数据验证,reference中的有限列表不是完整真相;真不知道才用 all。
- Windows 优先用
winget 精确包/官方源/用户 scope 安装 EIM;fallback 动态读官方 GitHub release,
必须同时通过 release asset SHA256 与 Espressif Authenticode 签名才落盘和执行。EIM 的安装/运行
都默认 --do-not-track true;不要直接执行未验签的下载 exe。
- 装完或修复后必须经过严格门禁:先从EIM登记/安装输出锁定本次实际路径,清除ambient
IDF_PATH干扰,再真实运行 idf.py --version并要求READY=yes;仓库元数据版本与
idf.py --version 实际报告版本都必须和请求精确一致;
macOS/Linux 还要与请求安装路径一致。任一不满足都算安装失败,不能因安装器退出 0 就报成功。
用户的目标是开始开发时,继续做目标芯片的最小示例构建,
不把“安装脚本退出 0”当作完成。最后用人话告诉用户版本、位置、验证结果和下一步即可。
编译
- 编译前先运行
doctor.sh --project <项目目录> 或做等价检查。ESP-IDF 官方不支持 IDF 路径或
项目路径包含空白;shell 引号只能保留参数边界,不能修复上游构建限制。发现空白时不要直接移动
用户原项目:先检查相对依赖/符号链接,再由 Agent 选择无空格的实际路径建立受管工作副本或经用户
确认迁移;完成后重新体检并明确产物位置。
- 芯片型号从用户话里拿("S3 板子"→ esp32s3),或看项目 sdkconfig 的
CONFIG_IDF_TARGET,
都没有就问。首次或换芯片先 set-target(会重新生成 sdkconfig)。
- 首次 build 编译几百个文件,几分钟正常,后台跑。
- 报错不要瞎试:先看第一个
error:,对照 references/troubleshooting.md,没匹配再自己分析。
- 编译完把关键信息报给用户:固件大小、分区余量(build 输出末尾就有)。
烧录(硬安全规约,agent-first 不改变这条)
- 重扫端口(线索见"侦察";或
bash <skill>/scripts/find-port.sh)。
- 只读验明设备:
bash <skill>/scripts/identify-device.sh -p <口> 拿芯片型号+MAC
(内部兼容 esptool v4/v5 命令名;连接会复位板子,但不写 flash)。
- 向用户报告"将烧录到 <口> 的 <型号>(MAC xx:xx)",用户确认才烧;
多设备把候选全列出来让用户挑;芯片型号和项目 target 不符,拦下来问。
- 烧录显式
-p。只要经历复位/掉线/USB 重枚举,必须在新口上重跑
identify-device.sh;MAC 与用户刚确认的设备一致才能续烧。同一 MAC 无需再问,
不同/读不出则立即停。
可选设备收录与命名
把设备身份、显示名称和软件版本严格分层:
identify-device.sh 本次真实读取的标准化 MAC 才是设备键;用户名称只是当前电脑上的显示标签。
名称允许重复,不能单独选择硬件、证明重枚举前后是同一设备或授权烧录。
- fresh 识别成功后先按 MAC 查询本机目录。命中时显示
名称 · 芯片 · MAC尾号;真正烧录确认仍显示本次读取的完整 MAC、端口与 target。
- 首次见到且用户未主动要求时,最多提供一次非阻塞选择:“要给它起一个只保存在这台电脑上的名称吗?”
用户跳过就不创建持久记录,不影响当前任务,本会话也不重复追问。只有明确同意收录/命名才调用
scripts/device-registry.py remember;改名/忘记同样需要明确目标和用户意图。
- 多块设备同名时全部列出芯片、当前端口和 MAC 尾号让用户消歧,不静默覆盖、不自动选一个,
也不把别名重新绑定到新 MAC。用户说“烧到桌面板”后仍先扫描并逐块 fresh 验身;能自动列全时
不要求用户先拔板,只有 USB/驱动/板级限制导致无法分别连接时才用逐块插拔作为退路。
- IDF 版本不是设备永久属性。分别记录:
- 当前实际激活环境的
idf.py --version;
- 当前 build metadata 的项目构建版本;
- 刚烧录 app image 的
esp_app_desc_t.idf_ver,或运行应用主动报告的版本。
三者可能不同,缺哪项就保持未知,绝不能用当前环境版本冒充板上固件版本。只识别硬件时不要为了补版本
额外读 flash、复位或进入下载模式。设备目录底层不得提供一个含糊的总 idf_version/“主版本”
字段;外部存储只有一个通用版本格时先扩展 schema,不能三选一或把三类证据塞进同一字符串。
UI 可把证据充分的 firmware 域标成“当前固件 IDF”作摘要,其余来源仍保留在详情。版本不同只证明
观测不同,不足以自行断言哪个“旧了”、哪个已经烧到板上。详细证据和命令见
references/device-registry.md。
- 设备目录只保存在当前用户数据目录,不进项目/Git/云同步;普通列表掩码 MAC,不保存项目绝对路径、
串口日志、网络或业务数据。别名不进入文件名和 shell 命令。
板子没进下载模式 / 连不上时的标准应对(不要报错了事,把用户带过去):
Failed to connect、等待包头超时或端口暂时消失,不足以单独证明板子没进下载模式;
也可能是端口选择、USB 重枚举、数据线或驱动问题。芯片型号相同不代表开发板下载/复位电路相同,
先按本轮历史和具体板子的按键、原理图/说明、USB 接口与供电方式判断:
- 自动连接尚未真实尝试时,先让用户不碰按键,由所选
idf.py/esptool 的默认复位机制尝试。
- 已能与 ROM 通信、刚完成一次手动进入动作或正在等 USB 重枚举时,板子已经在下载模式;
不要再次要求按 BOOT,直接重扫、验身并按既有授权继续。
- 只有自动连接已失败且证据表明尚未进入下载模式时,才选择这块板真实具备的一种手动动作:
- 有
BOOT + RESET/EN:按住 BOOT → 点按一次 RESET/EN → 松开 BOOT。
- 只有
BOOT:按住 BOOT → 真正断电再上电 → 松开 BOOT。只有 USB 供电时可拔插 USB;
有电池或外部供电时拔 USB 不等于断电。
- 没有 BOOT 按键:查当前芯片资料与板型说明/原理图,确认这块板真实的下载 strap 焊盘、跳线
或厂商恢复方式;不要让用户寻找不存在的按键,也不要把所有 ESP32 系列的 BOOT 都硬编码成 GPIO0。
普通断电重上电本身不能保证进入下载模式。
- 端口不见时跑
bash <skill>/scripts/wait-port.sh -t 90;它只返回 CANDIDATE_PORT,
多口时拒绝猜 BEST_PORT。拿到候选口后重跑 identify-device.sh,匹配原 MAC 才续烧。
等特定口用 -p <端口>,但即使名字没变也要重验身份。
- 只有用户/跳线实际参与进入下载模式才记
manual;完全由控制线/原生 USB 自动进入记
automatic,无法确认才记 unknown。手动/不明方式烧完必须继续下面的恢复门禁。
- wait-port 超时(90s 没动静):按它输出的 HINT 引导用户检查数据线、板级进入方式和驱动,再等一轮;
连接不稳的板子换
-b 115200 低速烧。
烧录后闭环(prepare → 物理恢复 → verify)
identify-device.sh 内部使用 esptool,会主动连接 ROM 并改变复位状态。因此 MAC 的最后一次重验
必须发生在最终物理恢复之前;用户按 RESET/重新上电后严禁再跑 identify/esptool 来判断
“是否还在下载模式”。使用两阶段门禁:
# flash 刚结束,仍在刚才确认过的烧录口上
bash <skill>/scripts/post-flash-check.sh prepare \
-p <烧录口> -m <用户已确认的MAC> \
--download-entry <automatic|manual|unknown>
# 记下 prepare 输出的 POST_FLASH_SESSION。rc=20 时按板级电路恢复正常启动;
# 完成后用同一个 session 继续
bash <skill>/scripts/post-flash-check.sh verify --session <POST_FLASH_SESSION> \
-C <proj> -e '<能证明应用健康的正则>' [-p <已明确的恢复后端口>]
--download-entry 由你根据本轮历史显式传 automatic|manual|unknown,不能从端口日志猜。
manual/unknown 的 prepare 会输出 ACTION_REQUIRED=restore_normal_boot_for_board 和 rc=20;
这不是固定按键命令。manual 时只释放本轮实际使用且仍被拉低的下载条件(松开 BOOT/移除下载
strap 跳线),恢复正常启动绝不再次按 BOOT;随后有 RESET/EN 就点按一次,没有则真正断电再上电。
unknown 时先确保下载 strap 未被拉低,不清楚就只问一个板型/供电问题。用户完成后拿同一个
session 续跑 verify,不要把模式改写成 automatic。
automatic 的 prepare 返回 rc=0 和 NORMAL_BOOT_RECOVERY_REQUIRED=no;直接 verify,
不要求用户按或松 BOOT,也不预先要求实体 RESET。只有新鲜 ROM 下载证据出现后才转入板级恢复。
- 重新上电必须切断这块板的全部供电;有电池、调试器或外部 5V/3V3 时只拔 USB 可能没有产生
上电复位。板上既无按键又无法确认安全断电方式时,查板型说明/原理图,不要编造统一手势。
- prepare 会把已匹配的 MAC、进入方式和烧录口写入权限受限、限时的 session;verify 必须校验它,
不能靠调用方自报“已经验过身份”。成功后 session 自动消费。
- verify 阶段只做 fresh rescan + 尝试串口控制线 reset/采集,零次调用 identify/esptool。
控制线 API 调用成功不能证明具体板子已把 RTS 接到 EN;脚本会把 reset 效果保持为
unverified,
仍以应用证据为准。
prepare只证明最终恢复前的MAC匹配。原端口消失后,脚本只能返回恢复后候选;由你结合刷新前后
清单、USB topology/serial、用户明确选择等证据传入 -p。当前只有一个新口也不等于同一设备;
证据不足保持未验证。macOS /dev/cu.*、Windows COM号都不是永久身份。
- 只有应用
-e 命中时才输出 POST_FLASH_READY=yes/rc=0。DOWNLOAD_BOOT(...)、
DOWNLOAD(USB/UART0...)、waiting for download 是仍在 ROM 下载模式的强证据,输出 rc=20;
无日志、端口消失、普通 ESP-ROM/rst: 文本只是不确定,不能冒充 READY 或下载模式确诊。
- 用户已明确收录设备时,只有
POST_FLASH_READY=yes 后才能把刚烧录 app image 的内嵌 IDF 版本
记录为该 MAC 的 flash_verified 固件快照。只有本地 image 但尚未烧录/验证时不得绑定到设备;
未收录或用户跳过命名时不因烧录成功自动创建长期记录。
- 原始串口内容不混入机器 KV,而是写到
CAPTURE_LOG 指向的权限受限系统临时文件;需要排错时读取,
任务结束后删除。恢复后的 -p 只是本轮明确定位符,多口或无法唯一关联时必须停下来让用户选择。
- 不默认尝试
--after watchdog-reset:它不是全芯片通用恢复路径,C6 原生 USB 场景甚至可能需要
断电恢复。跨 ESP32 系列的保守兜底是保持所有下载 strap 释放,再用该板实际具备的 RESET/EN、
全部断电再上电或厂商恢复方式,不是统一要求某个按键。
- 应用健康证据若是蜂鸣、灯光、继电器等只能由用户观察的物理现象,最终只给当前板适用的一条
正常启动动作并让用户报告观察结果。例如板上无 RESET/EN 时就只要求保持 BOOT 释放并真正重新上电;
自动下载且已正常运行时直接观察,两个键都不碰。用户或任务一旦声明“不再运行设备识别或写入命令”,
严格停在这个观察边界,不得为了确认再调用 identify/esptool 或追加写入。
监视(只采集日志或作为上述 verify 的底层)
官方 esp-idf-monitor 在 stdin 非 TTY 时会拒绝启动;它没有 expect/reset/exit 文本 DSL。
agent 验证固件用有界的直接串口采集:
bash <skill>/scripts/monitor.sh -p <口> -C <proj> -t 50 -R -e '<能证明正常的正则>'
脚本会从 build/project_description.json 读波特率(也可 -b 覆盖),匹配则 0,
超时未匹配则 1,开口失败 2,读取失败 3,缺 pyserial 4,参数错误 64;输出
DATA_SEEN/CAPTURE_BYTES 区分“完成了纯采集”和“真的看到了数据”。它不做官方 monitor 的符号化地址解码;
用户给定的 expect 正则会在可终止的独立进程里匹配,并受采集总 deadline、64 KiB 窗口和
4096 字符模式上限约束;量词嵌套等高风险模式直接 rc=64,不能用复杂回溯绕过 -t 超时。
如需人类交互调试,告诉用户在真终端里跑 idf.py monitor。agent 不裸跑它。
单独 monitor 等不到输出时不要直接断言仍在下载模式;回到上面的 post-flash 两阶段门禁。
配置修改
改项目的 sdkconfig.defaults(没有就创建)→ set-target 重新生成。
不直接编辑生成的 sdkconfig(会被覆盖),不跑 menuconfig。常用 CONFIG 项见 references/idf-commands.md。
辅助工具箱(可选加速器,不是必经之路)
| 脚本 |
值得用的时候 |
别依赖它的时候 |
scripts/doctor.sh |
开场一次拿全景,省多轮探测 |
输出和你实际观察冲突时,以实际为准 |
scripts/install.sh |
动态安装(mac 有 EIM/Homebrew→EIM,否则官方脚本 / win EIM) |
非标场景 → install-playbook 手动配方 |
scripts/install-eim-macos.sh |
复用 Mac EIM;有 Homebrew 时先补官方前置 |
EIM/Homebrew 都没有;不要借此静默安装包管理器 |
scripts/bootstrap-macos.sh |
Mac 缺 CLT/Python 时自动补到系统 UI 边界 |
非 macOS;系统安装窗口仍需用户本人确认 |
scripts/idf-env.sh |
需要统一免激活入口且其现场假设成立时 |
它不是授权门禁;特殊环境/调试export时由Agent原生装配 |
scripts/find-port.sh |
快速扫口;单口给候选,多口报需选择 |
它不代替芯片+MAC 验身 |
scripts/identify-device.sh |
烧前及重枚举后读芯片+MAC |
读不出就停,不用端口名替代身份 |
scripts/device-registry.py |
用户同意后保存名称和分域 IDF 证据;按 fresh MAC 查询 |
别名不代替验身、target、授权或 post-flash session |
scripts/wait-port.sh |
提示用户操作后等候选端口 |
多口不自动选;返回后必须重验 MAC |
scripts/monitor.sh |
无 TTY 时定时采集日志/正则验证 |
需要地址符号化或人类交互时用真终端的官方 monitor |
scripts/post-flash-check.sh |
flash 后做 prepare/verify 闭环,处理手动下载模式 |
verify 后不再跑 identify;无应用 expect 不能判 READY |
同一个脚本失败两次 → 停止重试脚本,读输出、换你自己的手来。
references(知识库,按需读)
references/install-playbook.md — 按平台安装/修复、PowerShell-only原语和每步失败处理
references/idf-commands.md — 当前命令速查、动态target发现、破坏性操作前置和版本差异
references/china-mirrors.md — 国内镜像机制底账(只有网络/镜像问题时读)
references/troubleshooting.md — 已知报错、驱动、BOOT/RESET和串口排错剧本
references/device-registry.md — 本机设备命名、重复名称消歧、IDF版本证据与隐私边界
反模式
- ❌ 不装配环境裸跑
idf.py(必失败)
- ❌ 裸跑
idf.py monitor / menuconfig(交互式,卡死会话)
- ❌ 缓存串口名跨步骤复用,或把新端口名当设备身份(重枚举后要对 MAC)
- ❌ 把用户名称当设备身份或烧录授权,或因别名相同跳过 fresh MAC 复验
- ❌ 把当前
idf.py --version、项目期望版本或本地未烧录 image 写成设备正在运行的固件版本
- ❌ 为设备设置单一 IDF“主版本”,或仅凭三个版本不同推断固件新旧和烧录历史
- ❌ 未经用户同意持久保存 MAC/别名,或把本机设备目录写进项目、Git、云同步和公开日志
- ❌ 把 flash 退出 0、普通 boot log 或无 expect 的 monitor rc=0 当作应用已运行
- ❌ 用户最终恢复正常启动后再跑 identify/esptool“验身”(它会重新扰动启动状态)
- ❌ 把 BOOT/RESET 当成所有开发板的固定步骤,或在恢复正常启动时再次要求按 BOOT
- ❌ 跨芯片默认使用 watchdog reset;应按具体板子的 RESET/EN、完整断电或厂商方式恢复
- ❌ 不验明设备、未经用户确认就烧录(多设备烧错板子;板上可能是用户其他项目)
- ❌ 把
idf-env.sh 当成烧录授权门禁,或把未确认的 flash/erase/write 命令交给它执行
- ❌ 把用户/仓库提供的路径、正则或参数直接拼进
eim run 命令字符串
- ❌ 被脚本框死:脚本失败无脑重跑、脚本没覆盖就说做不了(你的能力才是主体)
- ❌ 假设用户平台、假设项目在固定路径
- ❌ 只因默认目录没找到就判定“未安装”,或无界扫描整块磁盘
- ❌ 认为给带空格的 IDF/项目路径加引号就能让 ESP-IDF 支持它
- ❌ 把 EIM GUI/Computer Use 设成公开 Skill 的强依赖,或代用户处理密码/Touch ID/UAC
- ❌ 国内装不动就先让用户开 VPN,或为绕过网络问题执行未验签的 EIM
- ❌ 直接编辑生成的 sdkconfig
环境变量
| 变量 |
用途 |
默认 |
ESP_IDF_CY_IDF_PATH |
显式指定用哪个 IDF(多版本/非常规位置) |
自动探测 |
ESP_IDF_CY_EIM_BIN |
显式指定 EIM 二进制(直下/非 PATH 安装) |
PATH 或 ~/.esp-idf-cy/bin/eim(.exe) |
ESP_IDF_CY_EIM_JSON |
显式指定已发现的 eim_idf.json;探测、run、fix、install统一使用其目录 |
平台官方默认登记 |
ESP_IDF_CY_EIM_VERSION |
Windows fallback 要求的精确 EIM tag;不设则动态取最新正式版 |
最新正式版 |
ESP_IDF_CY_INSTALL_MODE |
macOS 安装路线:auto/eim/official-script;Agent 按实机覆盖 |
auto |
ESP_IDF_CY_PROJECT_DIR |
提供项目上下文,允许从 build 元数据发现非常规 IDF |
从 idf.py -C/doctor 参数提取 |
ESP_IDF_CY_DEVICE_REGISTRY_DIR |
覆盖用户明确启用的本机设备目录;不要指向项目/Git/共享目录 |
当前用户私有数据目录 |
ESP_IDF_CY_PYTHON_BIN |
显式指定兼容 Python;Mac bootstrap 成功后也用它续传 |
自动探测 |
ESP_IDF_CY_MAC_PYTHON_VERSION |
无 Homebrew 时下载的 Python.org 版本 |
3.13.14 |
ESP_IDF_CY_MAC_PYTHON_SHA256 |
对应 Python.org pkg 的 SHA256 |
内置官方发布值 |
ESP_IDF_CY_MAC_PYTHON_URL |
公司镜像/后续维护时覆盖官方 pkg URL |
python.org 官方 URL |
ESP_IDF_CY_CURL_BIN |
覆盖网络探针和 macOS 下载所用 curl(测试/非标准环境) |
curl |
IDF_TOOLS_PATH |
工具链位置(透传给官方脚本) |
~/.espressif |
1---2name: esp-idf-cy3description: 面向第一次接触 ESP-IDF 的新手及已有工程用户的一站式 skill——用户只需说板子/芯片和想做什么,skill 自动检测或安装 macOS/Windows 开发环境(国内网络走乐鑫官方镜像),并完成编译、烧录、串口验证、设备命名/收录和排错;EIM、Python、工具链与环境激活默认由 skill 内部处理。支持 ESP32 全系芯片(ESP32/S2/S3/C3/C6 等)。当用户提到 "ESP-IDF"、"idf.py"、"ESP32 编译"、"烧录固件"、"flash 到板子"、"串口 monitor/日志"、"装 ESP-IDF"、"IDF 环境配置"、"menuconfig"、"sdkconfig"、"hello_world 跑不起来"、"找不到串口/COM口"、"esptool"、"给开发板命名"、"收录/记住这个设备"、"设备别名" 时必须触发;即使用户只说"我刚买了 ESP32-S3,电脑什么都没装,帮我开始"、"帮我把这个 ESP32 项目编译烧录一下"、"看看板子输出了什么",也应触发。不做:Arduino-ESP32 / PlatformIO 工作流,具体外设驱动业务代码的编写。4license: GPL-3.0-only5---67# esp-idf-cy — ESP-IDF 编译/构建/烧录(agent 能力为主,脚本为辅)89## 你在处理什么1011用户可能用 Mac 也可能用 Windows,项目在任何位置,ESP-IDF 装没装、怎么装的、装的哪个版本都不确定。12你的职责:用户一句"编译烧录",你负责搞清环境 → 缺什么补什么 → 编译 → 烧录 → 验证。13环境差异和故障靠**你自己的侦察和判断**吸收,不指望用户懂环境,也不指望脚本枚举了所有情况。1415宿主边界:本 skill 运行在已经能执行 Agent 工具的会话里。macOS/Linux 需要可调用 Bash;16Windows 需要 Agent 宿主提供 Git Bash,或由 Agent 直接用系统 PowerShell 调用17`scripts/eim-windows.ps1`。它会自动补齐 ESP-IDF 所需的 EIM、Python、Git 和工具链,18但不能在自己尚无法执行时“先安装自己的 shell/Agent 宿主”。不要把这个边界甩给新手;19若当前 Windows 会话没有 Bash,就由你改走已有 PowerShell,而不是要求用户学习 EIM。2021### 新手第一次使用的体验目标2223默认把用户当作可能完全没装过、也不需要先理解 ESP-IDF 安装体系的新手:2425- 用户只需要提供能知道的业务信息:板子/芯片型号、已有项目路径,以及想编译还是烧录。26 能从已连接板子、项目 `sdkconfig`/README 自动判断的就自己判断;只有判断不出且会改变 GB 级27 下载目标或烧录安全性时,才问一个简短问题。28- EIM、`export.sh`、Python 虚拟环境、工具链包和镜像都是**内部实现细节**。默认只报告结果,29 不把安装教程、官网链接或一串依赖清单甩给用户;只有失败定位或用户主动追问时才解释。30- `doctor.sh` 若证明现有环境 `READY=yes`,就复用探测到的精确版本和位置,继续用户的任务。31 **不要因为有更新的 stable 就静默升级、重装或切换版本。**32- 确认没有可用环境时才自动安装:已有项目优先服从项目锁定的精确版本;新项目用 `stable`;33 从板子推导并显式传 `--targets`,避免默认下载全部芯片工具链。安装完成必须真实执行34 `idf.py --version`;若用户要“从零开始/跑起来”,再用对应 target 构建官方最小示例完成端到端验证。35- 正常情况下不要让用户暂停去手工装 Python/Git/SDK 或访问网页。只在系统权限/系统弹窗36 (如 macOS Command Line Tools)、缺少无法探测的板型信息,或真实烧录确认时请用户介入。37- 完成后用新手听得懂的三件事收口: **装好了什么版本和位置、验证到了哪一步、下一句可以怎么说**。38 不要用 EIM 等实现名词作为成功摘要的主角。3940### Agent-first 动态决策,不要把示例路径当事实4142- 先看当前 Agent 实际拥有的能力(shell、PowerShell、Computer Use、网络、文件权限),再选路线。43 脚本提供确定性检查和安全门禁,但脚本没覆盖的机器差异由你继续侦察、读日志和处理,不能把44 “不在默认目录”直接等同于“没有安装”。45- 路径来自用户上下文和机器证据:项目 `build/project_description.json`、`IDF_PATH`、EIM 登记、46 IDE 设置、进程环境和经过边界限制的文件搜索。`~/esp`、`C:\esp` 只是最后的惯例候选;47 找到候选后必须验证 `tools/idf.py` 和真实 `idf.py --version`。48- GUI 不是默认依赖。CLI 能完成就由 Agent 直接做;用户明确要求 GUI 且当前宿主有 Computer Use 时,49 Agent 可操作 EIM 的普通按钮、版本选择和日志页面。密码、Touch ID、UAC、macOS 安全确认及许可50 接受属于系统信任边界,必须让用户本人处理。没有 Computer Use 时自动回到 CLI,不要把一串点击51 教程丢给新手。52- `ACTION_REQUIRED`/rc=20 表示可恢复的人机交接:说明用户只需完成哪个系统动作,完成后由 Agent53 重跑原命令并续上。不要让用户重新理解整套安装流程。5455三层职责不要混淆:5657- **你(Agent)**负责侦察证据、选择路线、解释异常、向用户取得烧录确认并持续推进。58- **`SKILL.md`**只给决策顺序、安全红线、完成标准和按需读取 reference 的路由。59- **`references/`**保存会随 ESP-IDF/EIM/平台变化的命令与排错知识;执行前以实机和当前官方能力复核。60- **`scripts/`**提供确定性探针、可重入默认安装动作,以及验签、argv传递、身份解析、精确验证、61 超时等机械边界。它们可以执行你已选定的路线,但不是决策者或用户授权门禁;输出与实机冲突时62 继续调查,不能盲信或无脑重跑。6364按任务渐进读取,不要一次把全部知识塞进上下文:6566| 当前阶段 | 先读 |67|---|---|68| 安装、修复、EIM、PowerShell-only | `references/install-playbook.md` |69| 国内网络或镜像失败 | `references/china-mirrors.md` |70| target、idf.py、esptool、EIM 命令 | `references/idf-commands.md` |71| 已有报错、驱动、BOOT/RESET、串口异常 | `references/troubleshooting.md` |72| 设备命名、收录、改名、版本快照 | `references/device-registry.md` |7374五条物理事实,决定了下面所有做法:75761. **`export.sh` 只对当前 shell 生效**,而你每次 Bash 调用都是新 shell77 → 每条 idf 命令都要在**同一条命令里**先装配环境。782. **你没有交互终端** → menuconfig(TUI)和官方 `idf.py monitor`(强制要求 TTY)79 对你不可用;配置改 defaults,日志验证用有界的 pyserial 采集器。803. **烧录/复位会让 USB 重枚举** → 端口名不能跨步骤缓存,每次烧录/监视前重扫。814. **烧录 = 覆盖用户硬件上的固件**,板上可能跑着用户其他项目82 → 未验明设备身份并经用户确认,不烧。这条不因任何理由松动。835. **flash 退出 0 ≠ 新固件已经运行** → 手动下载模式可能仍停在 ROM;只有目标身份已重验、84 应用专用健康标志命中且没有 ROM 下载证据,才能报告烧录闭环完成。8586设备长期记录再加两条硬边界:87881. **名称只负责可读性**:底层始终按 fresh MAC 关联。多设备时先由 Agent 扫描并逐块验身,89 再把当前端口、芯片、名称和 MAC 尾号列给用户选;只有无法安全逐块连接时才请用户暂时拔掉一块。902. **版本必须带来源**:存储 schema 分开保存 environment/project/firmware,不得只有一个通用91 `idf_version`。界面可把证据充分的“当前固件 IDF”作为摘要,但不能丢掉另外两类观测或把摘要92 当设备永久属性。9394## 工作方式:侦察 → 判断 → 行动 → 验证9596以你自己的能力为主:自己跑命令探测、自己读报错、自己决定下一步。97脚本和文档是工具不是流程——脚本覆盖不了的情况,你直接上手。9899### 侦察:环境线索在哪100101省事的话跑 `bash <skill>/scripts/doctor.sh` 一次拿全景(KEY=VALUE 输出)。102自己侦察时的线索地图:103104- **IDF 在哪**:项目已有 build 时先读 `build/project_description.json` 的 `idf_path`,再看 `$IDF_PATH`;105 EIM 登记文件 `eim_idf.json`(POSIX 在106 `~/.espressif/tools/`,Windows 在 `C:\Espressif\tools\`);惯例路径 `~/esp/esp-idf*`。107 仍没有时检查项目 `.vscode/settings.json`/CI/终端配置,再只对用户目录、已知 SDK 根目录或实际磁盘108 做有界搜索,不要无界扫描整盘。版本用 `git -C <目录> describe --tags` 或真命令拿,别信目录名。109- **工具链健康度**:`~/.espressif/python_env/idf<IDF版本>_py<py版本>_env`——目录名本身110 编码了它绑定的 python 版本,系统 python 升级过它就失效(经典坑)。111- **Python 闸门**:优先读取所选 IDF 自带的版本检查/当前 EIM 前置结果;reference 里的版本表只是112 已验证快照。遇到未知的新 major 必须查当前证据或 fail closed,不能默认回落到旧 Python 下限。113- **串口**:mac 看 `/dev/cu.usbmodem*`(乐鑫原生口)和 `/dev/cu.usbserial*`/`SLAB*`/`wch*`(桥接);114 linux 看 `/dev/ttyACM*`/`/dev/ttyUSB*`;Windows 用 PowerShell115 `Get-CimInstance Win32_PnPEntity -Filter "PNPClass='Ports'"`,乐鑫原生口 DeviceID 含116 `VID_303A&PID_1001`。117- **网络**:GitHub 不畅但国内可达 → IDF仓库、工具资产和pip优先走乐鑫官方机制118 (`references/china-mirrors.md`)。EIM本体自举是否有国内官方资产要按当期来源另查;不要先把VPN甩给用户,119 也不要为“能下载”绕过hash/签名。120- 探到多个 IDF 版本或奇怪位置 → 把候选列给用户选,别猜。121122### 环境装配:每条 idf 命令的写法123124- **mac / linux 官方脚本安装**(同一条 Bash 里串起来):125 ```bash126 source <IDF_PATH>/export.sh >/dev/null 2>&1 && idf.py -C <项目目录> build127 ```128- **mac / linux EIM 管理的安装**:可用 `idf-env.sh`;wrapper 通过固定 argv relay 进入原生129 `eim --do-not-track true run`,不会把项目路径、正则或用户数据直接拼成 shell 命令。130- **Windows(你跑在 Git Bash 里)**:131 ```bash132 bash <skill>/scripts/idf-env.sh idf.py -C <项目目录> build133 ```134 为什么清 `MSYSTEM`:乐鑫官方脚本检测到 MSYS 环境直接拒跑,而 Git Bash 会把135 `MSYSTEM=MINGW64` 遗传给一切子进程。wrapper 内部经固定 PowerShell 边界验 EIM 的136 Authenticode 签名,关闭 EIM telemetry 后再 `eim run`;免激活执行是 Windows 首选;137 没有 EIM 的旧式官方安装才考虑 `env -u MSYSTEM cmd.exe //c "call <IDF>\export.bat && ..."`(实验性)。138- **Windows 没有 Git Bash**:不要先要求新手安装 Bash。先用固定 `CheckPlatform` 检查原生架构;官方 EIM 当前为139 Windows x64,不支持的架构不得盲下 x64资产。x64用当前 Agent 的PowerShell调固定 helper:140 `CheckPlatform`/`DownloadVerified`/`InstallIdf`/`FixIdf`/`RunIdf`;`RunIdf`接收argv而不是任意命令字符串。141 具体PowerShell原语见 `references/install-playbook.md`。142- `idf-env.sh` 是可选环境执行原语,不是唯一正确写法。能在同一调用中可靠激活所选 IDF并保持143 argv边界的原生命令同样可用;不要为了走wrapper而放弃Agent对现场的判断。144- 项目位置永远来自用户上下文(用户说的路径/当前目录),用 `idf.py -C` 传,不确定就问。145146### 安装或修复(先判断状态再做)147148- 先以 `doctor.sh` 的真实命令结果和你的现场证据做三态分流:149 1. `READY=yes`:原样复用,不安装、不升级。150 2. 已发现精确安装但真命令/工具损坏:EIM管理的优先 `eim fix -p <精确路径>`,legacy才重跑该仓库151 的官方安装工具;修完仍对同一路径严格复检。152 3. 确认不存在兼容环境:才新安装。多个候选或证据冲突时继续调查或只问一个会改变结果的问题。153- 省事路径:`bash <skill>/scripts/install.sh [--version stable|v5.5|v5.5.4] [--targets esp32s3]`154 ——幂等、可安全重跑、国内自动镜像。首次 clone 走同级受管暂存区,完整后才原子落位;155 子模块与工具下载可重复执行。下载量以 GB 计,后台跑或放宽超时。156- **macOS 空白机**:`install.sh` 先侦察再动态选路。已有可用 EIM 时直接复用;已有 Homebrew 且157 未指定精确仓库路径时,按 ESP-IDF v6.0+ 官方默认路线补齐乐鑫列出的前置、安装/复用 EIM CLI158 并非交互安装。EIM 与 Homebrew 都没有或用户传了 `--path` 时,不静默引入包管理器,改走仍受官方支持的159 `bootstrap-macos.sh` + ESP-IDF `install.sh` 路线。缺 Command Line Tools 时主动160 拉起苹果系统安装窗口;缺兼容 Python 时,已有 Homebrew 就自动安装,没有 Homebrew 就从161 Python.org 下载固定版本的 universal2 官方包,同时校验 SHA256 和 Python Software Foundation162 签名后才打开系统安装器。系统 UI 必须由用户本人确认;脚本返回 `ACTION_REQUIRED`/rc=20 时163 不是安装失败,等用户完成后重跑原命令即可续上。不要静默安装 Homebrew,也不要用 `curl|sh`164 引入第三方 Python。EIM 在 POSIX 只检查前置:有 Homebrew 时 `install-eim-macos.sh` 先安装官方165 清单;只有 EIM 时允许它先做真实检查,失败后 Agent 根据缺项换官方脚本路线或向用户说明选择。166 不能把“EIM 已装”误当成“所有依赖已齐”。167- **脚本失败或不适用**(特殊版本/公司代理/非常规位置):别无脑重跑。读它的输出定位卡点,168 按 `references/install-playbook.md` 的配方自己一步步来——每一步都能单独重试和替换。169- 版本选择:先看项目 README/CI/容器配置;已有项目跟它的精确版本。新项目默认170 `stable`;`--version v5.5` 表示“5.5 系列最新 patch”,要复现才传 `v5.5.4` 这类精确 tag。171- `--targets` 从板子/项目推导并显式传入(C6→`esp32c6`);候选必须以所选 IDF 的172 `idf.py --list-targets`/当前官方元数据验证,reference中的有限列表不是完整真相;真不知道才用 `all`。173- Windows 优先用 `winget` 精确包/官方源/用户 scope 安装 EIM;fallback 动态读官方 GitHub release,174 必须同时通过 release asset SHA256 与 Espressif Authenticode 签名才落盘和执行。EIM 的安装/运行175 都默认 `--do-not-track true`;不要直接执行未验签的下载 exe。176- 装完或修复后必须经过严格门禁:先从EIM登记/安装输出锁定**本次实际路径**,清除ambient177 `IDF_PATH`干扰,再真实运行 `idf.py --version`并要求`READY=yes`;仓库元数据版本与178 `idf.py --version` 实际报告版本都必须和请求精确一致;179 macOS/Linux 还要与请求安装路径一致。任一不满足都算安装失败,不能因安装器退出 0 就报成功。180 用户的目标是开始开发时,继续做目标芯片的最小示例构建,181 不把“安装脚本退出 0”当作完成。最后用人话告诉用户版本、位置、验证结果和下一步即可。182183### 编译184185- 编译前先运行 `doctor.sh --project <项目目录>` 或做等价检查。ESP-IDF 官方不支持 IDF 路径或186 项目路径包含空白;shell 引号只能保留参数边界,不能修复上游构建限制。发现空白时不要直接移动187 用户原项目:先检查相对依赖/符号链接,再由 Agent 选择无空格的实际路径建立受管工作副本或经用户188 确认迁移;完成后重新体检并明确产物位置。189- 芯片型号从用户话里拿("S3 板子"→ esp32s3),或看项目 sdkconfig 的 `CONFIG_IDF_TARGET`,190 都没有就问。首次或换芯片先 `set-target`(会重新生成 sdkconfig)。191- 首次 build 编译几百个文件,几分钟正常,后台跑。192- 报错不要瞎试:先看第一个 `error:`,对照 `references/troubleshooting.md`,没匹配再自己分析。193- 编译完把关键信息报给用户:固件大小、分区余量(build 输出末尾就有)。194195### 烧录(硬安全规约,agent-first 不改变这条)1961971. 重扫端口(线索见"侦察";或 `bash <skill>/scripts/find-port.sh`)。1982. 只读验明设备:`bash <skill>/scripts/identify-device.sh -p <口>` 拿芯片型号+MAC199 (内部兼容 esptool v4/v5 命令名;连接会复位板子,但不写 flash)。2003. 向用户报告"将烧录到 <口> 的 <型号>(MAC xx:xx)",**用户确认才烧**;201 多设备把候选全列出来让用户挑;芯片型号和项目 target 不符,拦下来问。2024. 烧录显式 `-p`。只要经历复位/掉线/USB 重枚举,必须在新口上重跑203 `identify-device.sh`;**MAC 与用户刚确认的设备一致**才能续烧。同一 MAC 无需再问,204 不同/读不出则立即停。205206### 可选设备收录与命名207208把设备身份、显示名称和软件版本严格分层:209210- `identify-device.sh` 本次真实读取的标准化 MAC 才是设备键;用户名称只是当前电脑上的显示标签。211 名称允许重复,不能单独选择硬件、证明重枚举前后是同一设备或授权烧录。212- fresh 识别成功后先按 MAC 查询本机目录。命中时显示213 `名称 · 芯片 · MAC尾号`;真正烧录确认仍显示本次读取的完整 MAC、端口与 target。214- 首次见到且用户未主动要求时,最多提供一次非阻塞选择:“要给它起一个只保存在这台电脑上的名称吗?”215 用户跳过就不创建持久记录,不影响当前任务,本会话也不重复追问。只有明确同意收录/命名才调用216 `scripts/device-registry.py remember`;改名/忘记同样需要明确目标和用户意图。217- 多块设备同名时全部列出芯片、当前端口和 MAC 尾号让用户消歧,不静默覆盖、不自动选一个,218 也不把别名重新绑定到新 MAC。用户说“烧到桌面板”后仍先扫描并逐块 fresh 验身;能自动列全时219 不要求用户先拔板,只有 USB/驱动/板级限制导致无法分别连接时才用逐块插拔作为退路。220- IDF 版本不是设备永久属性。分别记录:221 1. 当前实际激活环境的 `idf.py --version`;222 2. 当前 build metadata 的项目构建版本;223 3. 刚烧录 app image 的 `esp_app_desc_t.idf_ver`,或运行应用主动报告的版本。224 三者可能不同,缺哪项就保持未知,绝不能用当前环境版本冒充板上固件版本。只识别硬件时不要为了补版本225 额外读 flash、复位或进入下载模式。设备目录底层不得提供一个含糊的总 `idf_version`/“主版本”226 字段;外部存储只有一个通用版本格时先扩展 schema,不能三选一或把三类证据塞进同一字符串。227 UI 可把证据充分的 firmware 域标成“当前固件 IDF”作摘要,其余来源仍保留在详情。版本不同只证明228 观测不同,不足以自行断言哪个“旧了”、哪个已经烧到板上。详细证据和命令见229 `references/device-registry.md`。230- 设备目录只保存在当前用户数据目录,不进项目/Git/云同步;普通列表掩码 MAC,不保存项目绝对路径、231 串口日志、网络或业务数据。别名不进入文件名和 shell 命令。232233**板子没进下载模式 / 连不上时的标准应对**(不要报错了事,把用户带过去):234235`Failed to connect`、等待包头超时或端口暂时消失,不足以单独证明板子没进下载模式;236也可能是端口选择、USB 重枚举、数据线或驱动问题。**芯片型号相同不代表开发板下载/复位电路相同**,237先按本轮历史和具体板子的按键、原理图/说明、USB 接口与供电方式判断:2382391. 自动连接尚未真实尝试时,先让用户不碰按键,由所选 `idf.py`/esptool 的默认复位机制尝试。2402. 已能与 ROM 通信、刚完成一次手动进入动作或正在等 USB 重枚举时,板子已经在下载模式;241 **不要再次要求按 BOOT**,直接重扫、验身并按既有授权继续。2423. 只有自动连接已失败且证据表明尚未进入下载模式时,才选择这块板真实具备的一种手动动作:243 - 有 `BOOT` + `RESET/EN`:按住 BOOT → 点按一次 RESET/EN → 松开 BOOT。244 - 只有 `BOOT`:按住 BOOT → 真正断电再上电 → 松开 BOOT。只有 USB 供电时可拔插 USB;245 有电池或外部供电时拔 USB 不等于断电。246 - 没有 BOOT 按键:查当前芯片资料与板型说明/原理图,确认这块板真实的下载 strap 焊盘、跳线247 或厂商恢复方式;不要让用户寻找不存在的按键,也不要把所有 ESP32 系列的 BOOT 都硬编码成 GPIO0。248 普通断电重上电本身不能保证进入下载模式。2494. 端口不见时跑 `bash <skill>/scripts/wait-port.sh -t 90`;它只返回 `CANDIDATE_PORT`,250 多口时拒绝猜 `BEST_PORT`。拿到候选口后重跑 `identify-device.sh`,匹配原 MAC 才续烧。251 等特定口用 `-p <端口>`,但即使名字没变也要重验身份。2525. 只有用户/跳线实际参与进入下载模式才记 `manual`;完全由控制线/原生 USB 自动进入记253 `automatic`,无法确认才记 `unknown`。手动/不明方式烧完必须继续下面的恢复门禁。2546. wait-port 超时(90s 没动静):按它输出的 HINT 引导用户检查数据线、板级进入方式和驱动,再等一轮;255 连接不稳的板子换 `-b 115200` 低速烧。256257### 烧录后闭环(prepare → 物理恢复 → verify)258259`identify-device.sh` 内部使用 esptool,会主动连接 ROM 并改变复位状态。因此 MAC 的最后一次重验260必须发生在**最终物理恢复之前**;用户按 RESET/重新上电后严禁再跑 identify/esptool 来判断261“是否还在下载模式”。使用两阶段门禁:262263```bash264# flash 刚结束,仍在刚才确认过的烧录口上265bash <skill>/scripts/post-flash-check.sh prepare \266 -p <烧录口> -m <用户已确认的MAC> \267 --download-entry <automatic|manual|unknown>268269# 记下 prepare 输出的 POST_FLASH_SESSION。rc=20 时按板级电路恢复正常启动;270# 完成后用同一个 session 继续271bash <skill>/scripts/post-flash-check.sh verify --session <POST_FLASH_SESSION> \272 -C <proj> -e '<能证明应用健康的正则>' [-p <已明确的恢复后端口>]273```274275- `--download-entry` 由你根据本轮历史显式传 `automatic|manual|unknown`,不能从端口日志猜。276 `manual/unknown` 的 prepare 会输出 `ACTION_REQUIRED=restore_normal_boot_for_board` 和 rc=20;277 这不是固定按键命令。`manual` 时只释放本轮实际使用且仍被拉低的下载条件(松开 BOOT/移除下载278 strap 跳线),**恢复正常启动绝不再次按 BOOT**;随后有 RESET/EN 就点按一次,没有则真正断电再上电。279 `unknown` 时先确保下载 strap 未被拉低,不清楚就只问一个板型/供电问题。用户完成后拿同一个280 session 续跑 verify,不要把模式改写成 automatic。281- `automatic` 的 prepare 返回 rc=0 和 `NORMAL_BOOT_RECOVERY_REQUIRED=no`;直接 verify,282 不要求用户按或松 BOOT,也不预先要求实体 RESET。只有新鲜 ROM 下载证据出现后才转入板级恢复。283- 重新上电必须切断这块板的全部供电;有电池、调试器或外部 5V/3V3 时只拔 USB 可能没有产生284 上电复位。板上既无按键又无法确认安全断电方式时,查板型说明/原理图,不要编造统一手势。285- prepare 会把已匹配的 MAC、进入方式和烧录口写入权限受限、限时的 session;verify 必须校验它,286 不能靠调用方自报“已经验过身份”。成功后 session 自动消费。287- verify 阶段只做 fresh rescan + 尝试串口控制线 reset/采集,**零次调用 identify/esptool**。288 控制线 API 调用成功不能证明具体板子已把 RTS 接到 EN;脚本会把 reset 效果保持为 `unverified`,289 仍以应用证据为准。290 prepare只证明最终恢复前的MAC匹配。原端口消失后,脚本只能返回恢复后候选;由你结合刷新前后291 清单、USB topology/serial、用户明确选择等证据传入 `-p`。当前只有一个新口也不等于同一设备;292 证据不足保持未验证。macOS `/dev/cu.*`、Windows COM号都不是永久身份。293- 只有应用 `-e` 命中时才输出 `POST_FLASH_READY=yes`/rc=0。`DOWNLOAD_BOOT(...)`、294 `DOWNLOAD(USB/UART0...)`、`waiting for download` 是仍在 ROM 下载模式的强证据,输出 rc=20;295 无日志、端口消失、普通 `ESP-ROM`/`rst:` 文本只是不确定,不能冒充 READY 或下载模式确诊。296- 用户已明确收录设备时,只有 `POST_FLASH_READY=yes` 后才能把刚烧录 app image 的内嵌 IDF 版本297 记录为该 MAC 的 `flash_verified` 固件快照。只有本地 image 但尚未烧录/验证时不得绑定到设备;298 未收录或用户跳过命名时不因烧录成功自动创建长期记录。299- 原始串口内容不混入机器 KV,而是写到 `CAPTURE_LOG` 指向的权限受限系统临时文件;需要排错时读取,300 任务结束后删除。恢复后的 `-p` 只是本轮明确定位符,多口或无法唯一关联时必须停下来让用户选择。301- 不默认尝试 `--after watchdog-reset`:它不是全芯片通用恢复路径,C6 原生 USB 场景甚至可能需要302 断电恢复。跨 ESP32 系列的保守兜底是保持所有下载 strap 释放,再用该板实际具备的 RESET/EN、303 全部断电再上电或厂商恢复方式,不是统一要求某个按键。304- 应用健康证据若是蜂鸣、灯光、继电器等只能由用户观察的物理现象,最终只给**当前板适用的一条**305 正常启动动作并让用户报告观察结果。例如板上无 RESET/EN 时就只要求保持 BOOT 释放并真正重新上电;306 自动下载且已正常运行时直接观察,两个键都不碰。用户或任务一旦声明“不再运行设备识别或写入命令”,307 严格停在这个观察边界,不得为了确认再调用 identify/esptool 或追加写入。308309### 监视(只采集日志或作为上述 verify 的底层)310311官方 esp-idf-monitor 在 stdin 非 TTY 时会拒绝启动;它没有 `expect/reset/exit` 文本 DSL。312agent 验证固件用有界的直接串口采集:313314```bash315bash <skill>/scripts/monitor.sh -p <口> -C <proj> -t 50 -R -e '<能证明正常的正则>'316```317318脚本会从 `build/project_description.json` 读波特率(也可 `-b` 覆盖),匹配则 0,319超时未匹配则 1,开口失败 2,读取失败 3,缺 pyserial 4,参数错误 64;输出320`DATA_SEEN`/`CAPTURE_BYTES` 区分“完成了纯采集”和“真的看到了数据”。它不做官方 monitor 的符号化地址解码;321用户给定的 expect 正则会在可终止的独立进程里匹配,并受采集总 deadline、64 KiB 窗口和3224096 字符模式上限约束;量词嵌套等高风险模式直接 rc=64,不能用复杂回溯绕过 `-t` 超时。323如需人类交互调试,告诉用户在真终端里跑 `idf.py monitor`。**agent 不裸跑它**。324单独 monitor 等不到输出时不要直接断言仍在下载模式;回到上面的 post-flash 两阶段门禁。325326### 配置修改327328改项目的 `sdkconfig.defaults`(没有就创建)→ `set-target` 重新生成。329不直接编辑生成的 sdkconfig(会被覆盖),不跑 menuconfig。常用 CONFIG 项见 `references/idf-commands.md`。330331## 辅助工具箱(可选加速器,不是必经之路)332333| 脚本 | 值得用的时候 | 别依赖它的时候 |334|---|---|---|335| `scripts/doctor.sh` | 开场一次拿全景,省多轮探测 | 输出和你实际观察冲突时,以实际为准 |336| `scripts/install.sh` | 动态安装(mac 有 EIM/Homebrew→EIM,否则官方脚本 / win EIM) | 非标场景 → install-playbook 手动配方 |337| `scripts/install-eim-macos.sh` | 复用 Mac EIM;有 Homebrew 时先补官方前置 | EIM/Homebrew 都没有;不要借此静默安装包管理器 |338| `scripts/bootstrap-macos.sh` | Mac 缺 CLT/Python 时自动补到系统 UI 边界 | 非 macOS;系统安装窗口仍需用户本人确认 |339| `scripts/idf-env.sh` | 需要统一免激活入口且其现场假设成立时 | 它不是授权门禁;特殊环境/调试export时由Agent原生装配 |340| `scripts/find-port.sh` | 快速扫口;单口给候选,多口报需选择 | 它不代替芯片+MAC 验身 |341| `scripts/identify-device.sh` | 烧前及重枚举后读芯片+MAC | 读不出就停,不用端口名替代身份 |342| `scripts/device-registry.py` | 用户同意后保存名称和分域 IDF 证据;按 fresh MAC 查询 | 别名不代替验身、target、授权或 post-flash session |343| `scripts/wait-port.sh` | 提示用户操作后等候选端口 | 多口不自动选;返回后必须重验 MAC |344| `scripts/monitor.sh` | 无 TTY 时定时采集日志/正则验证 | 需要地址符号化或人类交互时用真终端的官方 monitor |345| `scripts/post-flash-check.sh` | flash 后做 prepare/verify 闭环,处理手动下载模式 | verify 后不再跑 identify;无应用 expect 不能判 READY |346347同一个脚本失败两次 → 停止重试脚本,读输出、换你自己的手来。348349## references(知识库,按需读)350351- `references/install-playbook.md` — 按平台安装/修复、PowerShell-only原语和每步失败处理352- `references/idf-commands.md` — 当前命令速查、动态target发现、破坏性操作前置和版本差异353- `references/china-mirrors.md` — 国内镜像机制底账(只有网络/镜像问题时读)354- `references/troubleshooting.md` — 已知报错、驱动、BOOT/RESET和串口排错剧本355- `references/device-registry.md` — 本机设备命名、重复名称消歧、IDF版本证据与隐私边界356357## 反模式358359- ❌ 不装配环境裸跑 `idf.py`(必失败)360- ❌ 裸跑 `idf.py monitor` / `menuconfig`(交互式,卡死会话)361- ❌ 缓存串口名跨步骤复用,或把新端口名当设备身份(重枚举后要对 MAC)362- ❌ 把用户名称当设备身份或烧录授权,或因别名相同跳过 fresh MAC 复验363- ❌ 把当前 `idf.py --version`、项目期望版本或本地未烧录 image 写成设备正在运行的固件版本364- ❌ 为设备设置单一 IDF“主版本”,或仅凭三个版本不同推断固件新旧和烧录历史365- ❌ 未经用户同意持久保存 MAC/别名,或把本机设备目录写进项目、Git、云同步和公开日志366- ❌ 把 flash 退出 0、普通 boot log 或无 expect 的 monitor rc=0 当作应用已运行367- ❌ 用户最终恢复正常启动后再跑 identify/esptool“验身”(它会重新扰动启动状态)368- ❌ 把 BOOT/RESET 当成所有开发板的固定步骤,或在恢复正常启动时再次要求按 BOOT369- ❌ 跨芯片默认使用 watchdog reset;应按具体板子的 RESET/EN、完整断电或厂商方式恢复370- ❌ 不验明设备、未经用户确认就烧录(多设备烧错板子;板上可能是用户其他项目)371- ❌ 把 `idf-env.sh` 当成烧录授权门禁,或把未确认的 flash/erase/write 命令交给它执行372- ❌ 把用户/仓库提供的路径、正则或参数直接拼进 `eim run` 命令字符串373- ❌ 被脚本框死:脚本失败无脑重跑、脚本没覆盖就说做不了(你的能力才是主体)374- ❌ 假设用户平台、假设项目在固定路径375- ❌ 只因默认目录没找到就判定“未安装”,或无界扫描整块磁盘376- ❌ 认为给带空格的 IDF/项目路径加引号就能让 ESP-IDF 支持它377- ❌ 把 EIM GUI/Computer Use 设成公开 Skill 的强依赖,或代用户处理密码/Touch ID/UAC378- ❌ 国内装不动就先让用户开 VPN,或为绕过网络问题执行未验签的 EIM379- ❌ 直接编辑生成的 sdkconfig380381## 环境变量382383| 变量 | 用途 | 默认 |384|---|---|---|385| `ESP_IDF_CY_IDF_PATH` | 显式指定用哪个 IDF(多版本/非常规位置) | 自动探测 |386| `ESP_IDF_CY_EIM_BIN` | 显式指定 EIM 二进制(直下/非 PATH 安装) | PATH 或 `~/.esp-idf-cy/bin/eim(.exe)` |387| `ESP_IDF_CY_EIM_JSON` | 显式指定已发现的 `eim_idf.json`;探测、run、fix、install统一使用其目录 | 平台官方默认登记 |388| `ESP_IDF_CY_EIM_VERSION` | Windows fallback 要求的精确 EIM tag;不设则动态取最新正式版 | 最新正式版 |389| `ESP_IDF_CY_INSTALL_MODE` | macOS 安装路线:`auto`/`eim`/`official-script`;Agent 按实机覆盖 | `auto` |390| `ESP_IDF_CY_PROJECT_DIR` | 提供项目上下文,允许从 build 元数据发现非常规 IDF | 从 `idf.py -C`/doctor 参数提取 |391| `ESP_IDF_CY_DEVICE_REGISTRY_DIR` | 覆盖用户明确启用的本机设备目录;不要指向项目/Git/共享目录 | 当前用户私有数据目录 |392| `ESP_IDF_CY_PYTHON_BIN` | 显式指定兼容 Python;Mac bootstrap 成功后也用它续传 | 自动探测 |393| `ESP_IDF_CY_MAC_PYTHON_VERSION` | 无 Homebrew 时下载的 Python.org 版本 | 3.13.14 |394| `ESP_IDF_CY_MAC_PYTHON_SHA256` | 对应 Python.org pkg 的 SHA256 | 内置官方发布值 |395| `ESP_IDF_CY_MAC_PYTHON_URL` | 公司镜像/后续维护时覆盖官方 pkg URL | python.org 官方 URL |396| `ESP_IDF_CY_CURL_BIN` | 覆盖网络探针和 macOS 下载所用 curl(测试/非标准环境) | `curl` |397| `IDF_TOOLS_PATH` | 工具链位置(透传给官方脚本) | `~/.espressif` |