Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

固件与烧录

四个工具处理构建流水线的末端——列出可用固件、下载固件、烧录到 真实设备,以及烧录后从设备捕获串口输出。

工具做什么
firmware.list列出已完成的、有固件可下载的构建。
firmware.download获取某次构建固件的下载元数据。
flash.run通过串口把固件烧录到本地连接的 ESP 设备。
monitor.run在限定时间内,从本地设备的串口捕获输出。

firmware.list

显示哪些构建已成功完成、有固件可以下载。

输入:

{}

可选:按特定构建过滤:

{ "job_id": "0abf...e2" }
字段必需说明
job_id只看一次构建。不填则列出所有成功的构建。

返回:

{
  "builds": [
    {
      "task_id": "0abf...e2",
      "target": "esp32s3",
      "status": "succeeded",
      "build_type": "release"
    }
  ]
}

无副作用。 随时可以调用。


firmware.download

获取下载构建固件所需的元数据。实际的二进制传输走 firmware WebRTC DataChannel——这个工具给你制品信息。

输入:

{
  "job_id": "0abf...e2"
}
字段必需说明
job_id一次成功构建的 task ID。
output_dir固件文件保存位置。

返回:

{
  "job_id": "0abf...e2",
  "status": "succeeded",
  "artifact_lines": [
    "build/flash_bundle.tar.gz",
    "build/bootloader.bin",
    "build/partition_table/partition-table.bin",
    "build/<project>.bin"
  ],
  "output_dir": "/tmp/firmware"
}

构建必须是 succeeded 状态。对失败或运行中的构建调用会报错。

主制品是 flash_bundle.tar.gz 远程构建会打包出一个带签名的 自描述烧录包(manifest.json + files/,内含 bootloader、分区表、 应用三段),在同一会话里原子返回给客户端——没有单独的 fetch 步骤flash.run 和命令行 espctl flash 都消费这个文件。单独的 .bin 文件还列在 artifact_lines 里方便检查,但你几乎不会再直接 把它们传给烧录器。


flash.run

把固件烧录到连接在电脑 USB 口的 ESP 设备。底层直接用纯 Rust 的 espflash 库——不依赖 Python esptool.py。你不需要 pip install esptool,不需要 venv,也不 需要 PATH 里有 Python。

输入:

{
  "firmware_path": "/path/to/build/flash_bundle.tar.gz",
  "port": "/dev/ttyUSB0",
  "baud": 460800
}
字段必需说明
firmware_path一个 flash_bundle.tar.gz(由 build + firmware.download 产出)、一个已解压的烧录包目录,或者原始的 .bin / .elf 文件。推荐用烧录包形式,它在一个文件里带齐 bootloader、分区表、应用和签名清单。
port串口。只连了一个 ESP 设备时自动检测。
baud烧录波特率。默认 460800。

返回: 烧录操作的状态(成功或错误详情)。

传入烧录包时,flash.run 会读取 manifest.json,校验每一段的 sha256,然后在一次 espflash 会话里把所有段一次性写进 flash (关键——如果逐段写,芯片会在第一段后重启、第二段就永远 hang)。 芯片只在最后重启一次。

政策:绝不安装 esptool.py 如果 flash.run 或命令行 espctl flash 失败,按 docs/infra-bugs-2026-04-11.md 的格式在 aegis 仓库里建一份 docs/espctl-flash-bugs-YYYY-MM-DD.md 提 bug。 不要通过装 Python esptool 来绕开故障——那只会把真正的 build-to-flash 流水线 bug 悄悄藏起来。

仅限本地。 这个工具跑在你的电脑上,不是构建服务器上。只在 本地/stdio MCP 模式下可用——浏览器里不行。浏览器烧录用 MCP 控制台的 Flash 标签。


monitor.run

在限定时间内从连接好的 ESP 设备捕获串口输出。一般紧跟在 flash.run 之后用,验证板子启动、固件确实在跑——例如观察 向导 Phase-0 验证固件每秒发出的 heartbeat 日志行。

输入:

{
  "port": "/dev/cu.usbmodem1101",
  "baud": 115200,
  "duration_sec": 30,
  "filter": "heartbeat",
  "reset_on_connect": true
}
字段必需说明
port串口(如 /dev/ttyUSB0/dev/cu.usbmodem14101COM3)。只连了一个 ESP 设备时自动检测。
baud波特率。默认 115200(ESP-IDF 控制台默认值——和 flash.run 的 460800 不同)。
duration_sec捕获时长。默认 30 秒,上限 600 秒。
filter子串。只有包含它的行会出现在 output 里。"heartbeat" 验证时很有用。
reset_on_connect默认 true——打开串口后拉一次 DTR/RTS 脉冲,让芯片在捕获窗口内重启进入应用。没有自动复位电路的板子,或者已经被别的工具复位过的情况,设为 false。比 espctl probe 更克制——不会进 bootloader。

返回:

{
  "success": true,
  "port": "/dev/cu.usbmodem1101",
  "baud": 115200,
  "duration_ms": 30024,
  "bytes_read": 18432,
  "lines_captured": 32,
  "output": "I (123) heartbeat: tick 0\nI (1234) heartbeat: tick 1\n...",
  "truncated": false,
  "message": "Captured 18432 byte(s) over 30024 ms from /dev/cu.usbmodem1101 at 115200 baud."
}

捕获缓冲区上限约 512 KB;如果设备在窗口内输出更多,truncated 会变成 true,末尾会被截断。

仅限本地。flash.run 一样,只在本地/stdio MCP 模式下 可用——浏览器里不行。浏览器监视器用 MCP 控制台 — Monitor 标签 的 Web Serial。

没有 panic 解码器。 这是一份 UTF-8 lossy 的原始字节转储。 没有 idf.py monitor 那种基于 ELF 的 backtrace 解码。需要长时间 交互式监视的话,用命令行 espctl monitor


CLI: espctl ports

列出操作系统看得到的所有串口。USB 串口还会带上 VID:PID。烧录或 开监视器之前,先用这个命令找到你的板子。

espctl ports

没有任何标志。如果列表为空会打印 No serial ports found.

输出

Human 模式(表格):

PORT                           TYPE            USB VID:PID
----------------------------------------------------------------------
/dev/cu.usbmodem1101           USB             303A:1001
/dev/cu.Bluetooth-Incoming-... Bluetooth       -

JSON(--json):一个端口对象数组。USB 项还带 vidpidvid_pidmanufacturerproductserial_number

# 过滤出 USB 串口适配器
espctl --json ports | jq '.[] | select(.vid_pid != null)'

CLI: espctl probe

对真实设备打开 bootloader 握手,报告芯片型号(含 revision)、MAC 地址和 flash 大小。底层用和 espctl flash 一样的纯 Rust espflash 连接 —— 不依赖 Python esptool.py

espctl probe --port <port>

输入

标志说明
--port必填。不知道端口的话先跑 espctl ports

输出

Human 模式:

Port:       /dev/cu.usbmodem1101
Chip:       ESP32-S3 (revision v0.2)
MAC:        7c:df:a1:00:11:22
Flash size: 8MB

JSON(--json):

{
  "port": "/dev/cu.usbmodem1101",
  "chip_type": "ESP32-S3 (revision v0.2)",
  "mac_address": "7c:df:a1:00:11:22",
  "flash_size": "8MB"
}

失败模式

  • 端口不在操作系统端口列表里 → 退出 1(消息会提示先跑 espctl ports)。
  • bootloader 握手失败 → 退出 1。

CLI: espctl flash

把烧录包烧到连着的设备。MCP 等价工具是 flash.run —— 同一个引擎,同一次会话内写完。

espctl flash <bundle_path> --port <port> [--baud <rate>]

标志矩阵

参数默认说明
bundle_path(位置参数)必填一个已解压的烧录包目录,或 flash_bundle.tar.gz
--port必填串口。
--baud460800烧录波特率。

示例

# 默认波特率(460800)
espctl flash ./build/flash_bundle.tar.gz --port /dev/cu.usbmodem1101

# 更快 —— 前提是 USB↔串口 转换板和数据线撑得住
espctl flash ./build/flash_bundle.tar.gz --port /dev/ttyUSB0 --baud 921600

烧录包形式(由 espctl build 产出)带一个 manifest.json(含 sha256 校验)和所有段(bootloader、分区表、应用)。烧录器在 一次 espflash 会话里把所有段写完 —— 逐段写会让芯片在第一段后 重启,第二段就 hang 死。


CLI: espctl monitor

打开串口监视器,把输出实时打到你的终端。默认情况下断开会自动 重连。

espctl monitor --port <port> [--baud <rate>] \
               [--no-reconnect] [--no-reset-on-connect]

标志矩阵

标志默认说明
--port必填串口。
--baud115200监视器波特率(IDF 控制台默认值)。
--no-reconnectfalse断开时直接退出,而不是等设备回来。
--no-reset-on-connectfalse打开端口时跳过 DTR/RTS 复位脉冲。

关于 --no-reset-on-connect

默认行为下,monitor 会在打开端口时拉一次 RTS 复位脉冲,让芯片 在监视器下重新启动到应用。在没有自动复位电路的板子上,或者已经 有别的工具复位过、你不想再被打断的情况下,加 --no-reset-on-connect

典型流程

远程构建 + 本地烧录 + 本地监视:

espctl build . --target esp32s3
espctl flash ./build/flash_bundle.tar.gz --port /dev/cu.usbmodem*
espctl monitor --port /dev/cu.usbmodem*

构建一步是默认远程的 —— 不需要 --remote 参数。服务器地址来自 espctl login 或默认 https://esphome.cloud。用 --remote <url> 可覆盖,用 --local 切换到仅计划模式。


另见