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

进阶 Agent 工作流

一旦你不再“一块板子,一份固件“地想问题,espctl 就能解锁两个会改变 ESP32 工作台用法的模式。两个模式都完全由 agent 驱动 —— 用户用日常 语言说意图,AI 助手把 MCP 工具调用编排好。用户负责的事只剩两件: 插好板子、看仪表盘。

模式维度它给你什么
一块板子,多种角色时间一块 ESP32-S3 随当天的任务在多份固件之间轮换。
多块板子,一个操作员空间一台 mini-PC 或 Pi 同时驱动四块板,每块跑不同角色。

时间维度:一块板子,多种角色

带 USB 的 ESP32-S3,flash 和 RAM 都够装下面任意一个:

  • USB 逻辑分析仪(sigrok 兼容固件 —— 见 PulseView 与 Sigrok 固件)
  • Wi-Fi 网关 + MQTT/HTTP 遥测
  • USB 转串口调试桥 —— 用来探另一颗目标芯片的 UART
  • 当下正在迭代的项目

espctl 把“换角色“压成一句对话。Agent 取项目、build、烧到对应串口、 确认新固件起来 —— 一个已经在构建服务器有缓存的小项目,端到端通常 60–120 秒。

对话过程

用户: 把 /dev/cu.usbmodem1101 上的板子换成逻辑分析仪。

Agent: → build  { target: "esp32s3",
                  repo_url: "https://github.com/.../sigrok-fx2lafw-esp",
                  git_ref: "v0.2",
                  build_type: "release" }
       ← { task_id: "8a2f...", status: "running" }

       → build.status { task_id: "8a2f..." }   (每 2 秒轮询一次)
       ← { status: "succeeded", firmware_path: ".../firmware.bin" }

       → flash.run  { firmware_path: ".../firmware.bin",
                      port:          "/dev/cu.usbmodem1101",
                      baud:          460800 }
       ← { success: true, chip: "ESP32-S3", flashed_bytes: 487424 }

       → monitor.run { port:          "/dev/cu.usbmodem1101",
                       duration_sec:  10,
                       filter:        "fx2lafw" }
       ← { lines_captured: 4, output: "I (321) fx2lafw: ready, 24 MHz max\n..." }

Agent: ✓ /dev/cu.usbmodem1101 上的逻辑分析仪已就绪,最高 24 MHz 采样率。
       打开 PulseView,把它指向这个串口就能开始抓信号。

Agent 用到的工具

步骤MCP 工具为什么
取项目 + 编译build不需要本地工具链;构建服务器缓存了所有 IDF 版本。
跟踪进度build.status返回 pendingrunningsucceeded/failed
写新固件flash.run纯 Rust 的 espflash,不依赖 Python。
验证启动monitor.run可选但便宜 —— 10 秒抓取就能证实角色切换生效。

同一块 ESP32-S3 一个工作日里可以连续轮换好几个角色,完全不用动接线。 如果你给 agent 一份固定的项目仓库列表(sigrok-fx2lafw-espyour-esphome-gatewayusb-uart-bridgemy-current-project), 它在第一次提示后就能学会这套轮换;后续可以简写:

用户: 现在换成串口桥。
Agent: [回顾上下文,挑出 bridge 项目,build,flash]

什么时候适合这种模式

  • 硬件吃紧的环境(工坊、教室、单板实验室)。
  • 现场演示,只带一块板子,按需切换。
  • 调试上电序列 —— 先用逻辑分析仪固件一段行为,然后再把 同一块板子切回生产角色。

什么时候适合

  • 任何需要两个角色同时存在的场景 —— 用下面的空间维度模式。
  • 生产现场固件 —— 烧录会破坏前一个角色的状态。时间维度模式是 开发用,不是部署用。

空间维度:多块板子,一个操作员

一台 mini-PC 或 Raspberry Pi 4/5 的 USB 口数量足够挂四块 ESP 板子。 每块板子在系统里枚举成独立的串口(Linux 上是 /dev/ttyUSB0..3、 Windows 上是 COM3..6、macOS 上是 /dev/cu.usbmodem*)。因为每个 碰板子的 espctl 工具都接受显式 port 参数,agent 把每个串口当作 一个独立目标对待。

一个实用的四角色工作台:

槽位角色固件项目
/dev/ttyUSB0USB 逻辑分析仪sigrok 兼容(见 PulseView 与 Sigrok 固件)
/dev/ttyUSB1待测目标(最新构建)你的项目,main 分支
/dev/ttyUSB2待测目标(上一发版,A/B 对比)你的项目,release-1.4 分支
/dev/ttyUSB3网关 + 载波采样ESPHome Wi-Fi sink + ADC 采样

对话过程

用户: 把四板工作台搭起来。最新 main 在 USB1,上一版本在 USB2,
      网关在 USB3,逻辑分析仪在 USB0。

Agent: → espctl ports                              (通过 shell 调命令行)
       ← /dev/ttyUSB0..3 全部枚举到,均为 ESP32-S3

       (并行 —— 同时下两个构建)
       → build  { target: "esp32s3", git_ref: "main",        ... }
       → build  { target: "esp32s3", git_ref: "release-1.4", ... }
       ← { task_id: "main-9c1a...", status: "running" }
       ← { task_id: "rel--7b22...", status: "running" }

       → build.status { task_id: "main-9c1a..." }
       → build.status { task_id: "rel--7b22..." }
       ← 两个都 succeeded

       (并行 —— 四个 flash,每个绑定到自己的串口)
       → flash.run { firmware_path: <sigrok>,    port: "/dev/ttyUSB0" }
       → flash.run { firmware_path: <main.bin>,  port: "/dev/ttyUSB1" }
       → flash.run { firmware_path: <rel.bin>,   port: "/dev/ttyUSB2" }
       → flash.run { firmware_path: <gateway>,   port: "/dev/ttyUSB3" }
       ← 全部成功

       (并行 —— 四个监视器,各抓 30 秒启动日志)
       → monitor.run { port: "/dev/ttyUSB0", duration_sec: 30, filter: "fx2lafw" }
       → monitor.run { port: "/dev/ttyUSB1", duration_sec: 30, filter: "heartbeat" }
       → monitor.run { port: "/dev/ttyUSB2", duration_sec: 30, filter: "heartbeat" }
       → monitor.run { port: "/dev/ttyUSB3", duration_sec: 30, filter: "wifi" }

Agent: ✓ 工作台就绪。
       USB0: 逻辑分析仪,24 MHz 就位
       USB1: main @ a4f2c1e,1 Hz 心跳
       USB2: release-1.4 @ 9d20b5a,1 Hz 心跳
       USB3: 网关上线,Wi-Fi 入网名为 "lab-bench-gw"

       PulseView 已配好读 USB0;监听 USB3 的 Wireshark 能看到 USB1
       和 USB2 发出的 MQTT 上报。

Agent 用到的工具

步骤MCP 工具 / CLI为什么
枚举板子espctl ports确认哪些口真的接了 ESP,以及它们的 VID:PID。
并行编译两份固件两个并发的 build各自返回自己的 task_id;agent 同时轮询两个。构建服务器在独立沙箱里跑。
并行烧四块板四个 flash.run,每个 port: 独立绑定本地 USB 串口按口分流;只要口不同就互不干扰。
验证每个角色四个 monitor.run,各自带 filter:同样的并行规则 —— 不同口,不打架。

操作员的工作

监控,不是驱动:

  1. 把四块板插好。
  2. 告诉 agent 每个口的角色。
  3. 看 agent 流式回报的进度。
  4. 打开消费工作台输出的应用界面 —— PulseView、Wireshark/MQTT 浏览器、 ADC 波形可视化。

用户从头到尾没有自己敲过 espctl buildflashmonitor

什么时候适合这种模式

  • 固件 A/B 回归测试(USB1 vs USB2)。
  • 上电调试时同时跑分析仪和 DUT。
  • 单人维护的实验台,一个操作员协调多个测试夹具。
  • CI 跑批 —— 一台 Pi 把这个模式做成定时任务,可以无人值守地 把每个 PR 都在多块板上跑一遍。

什么时候适合

  • 量产烧录 —— 那个场景应该把 espctl 横向扩展到多台主机加队列, 而不是一台 Pi 同时驱四口。
  • 需要板间亚毫秒同步的场景。USB 串口不是实时通信。

为什么 espctl 能这么用

两个模式都依赖 espctl MCP 接口的三个性质:

  1. 工具是无状态的。 flash.runmonitor.runport 作为显式参数,agent 历史里没有任何东西把某次工具调用绑死到 某块板上。同一个工具用两个不同的 port 调两次就是干净的并发。
  2. 构建任务彼此独立。 build 立即返回 task_id,在后台运行。 两次 build 调用产生两个 task_id,agent 各自轮询自己的 build.status
  3. 碰硬件的部分跑在本地。 flash.runmonitor.run 跑在 用户的机器上(或操作员的 mini-PC/Pi 上),直接走 USB。构建走 远程,硬件走本地。Agent 在两端之间无感切换。

另见