STM32 / Cortex-M 工作流(预览)
STM32(以及泛 Cortex-M)在 esphome.cloud 是 Champion 档预览 功能。flash / monitor / 调试探头的本地表面已经在 espctl 里; 云端的三条构建路径按档位逐步开放。
现状(2026-05-24)。 本地已经能跑的:发现并占用 USB 调试探头、 用
probe-rs烧录预先打好的 Cortex-M flash bundle、走同一个探头 流 RTT 日志。还在开放中的:产出这些 flash bundle 的三条远程构建 路径(Path A / B / C)。Path C(本地 ELF + 云端打包)先落地。 NUCLEO-G431RB 是首个支持的板子家族,F7 / H7 / L4 后续扩展。
档位分级
| 套餐 | STM32 访问 |
|---|---|
| Hobby / Maker / Master | 仅 ESP32 |
| Champion + | STM32 构建执行器解锁 |
| Self-hosted | 全部目标(客户自带 runner) |
esphome.cloud/pricing 有当前的 执行器矩阵。
三条路径
| 路径 | 源 | 在哪里构建 | 云端干什么 |
|---|---|---|---|
| A · CMake + arm-gcc | CMake 项目(结构与 CubeMX 输出对齐;CubeMX GUI 在你本地跑) | 远端 agent | 用 arm-gcc-none-eabi + STM32CubeG4 HAL 配置 + 构建,签名,打包 flash bundle |
| B · Cargo + embassy-rs | Cargo 项目,#[no_std],embassy-rs | 远端 agent | 针对目标 triple 跑 cargo build --release,签名,打包 |
| C · 本地 ELF + 云端打包 | 你本地编 ELF | ELF 本地;打包远程 | arm-none-eabi-objcopy 出原始 flash image,签名,打包 |
esphome.cloud 明确不集成桌面 IDE / GUI。CubeMX、CubeIDE、Keil、 PlatformIO 全部在你本地。云端只接收:
- CMake / Cargo 项目(Path A / B),或者
- 已编好的 ELF(Path C)。
三种路径产出的都是同一种文件:flash_bundle.tar.gz,可以直接
喂给 espctl flash。
内部三条路径在 espctl skills(落地后)里以三个具名 recipe 出现
—— cortexm_cube_build(Path A)、cortexm_embassy_build(Path B)、
cortexm_rust_elf(Path C),agent 可以按名字精确选,不用做文本
匹配。
三条路径分别产出什么
不管走哪条路径,云端在 flash_bundle.tar.gz 里都吐同一套产物:
| 产物 | 用途 |
|---|---|
firmware.elf | 未 strip 的应用 ELF。保留是为了让你这边用 espctl elf <build_id> 取回 + RTT 调试。 |
firmware.bin | 链接脚本基址的原始 flash image。 |
firmware.hex | 同样 image 的 Intel HEX 表示。 |
manifest.json | flash-bundle 清单 —— chip、probe_rs_chip、flash segment 与 offset、SHA-256 哈希、build_provenance。 |
sbom.json | 工具链 pin(Path A / B)或空工具链段(Path C —— ELF 是你给的)加上声明的项目依赖。 |
整个 bundle 在交付前签名。espctl flash 和 espctl monitor --bundle
对三条路径的产出一视同仁。
工具链 pin 与可重复性目标
远端构建 store 在每次 store 发布时统一钉死工具链版本,在该次发布 内的所有 Cortex-M 构建都复用同一组 pin。你不在单次构建时挑 版本 —— 选择发生在 store 维护侧,粒度是 store 发布。
| 组件 | 当前 store 发布的 pin |
|---|---|
arm-none-eabi-gcc(Path A) | 13.2.0 |
Rust + cargo(Path B) | 1.85.0 |
| STM32CubeG4 HAL(Path A,NUCLEO-G431RB) | 跟 store 发布一起打包 |
probe-rs(本地侧,espctl flash / monitor --rtt 用) | 跟随你本地 espctl 打包进去的版本 |
可重复性目标:
- L1 —— text 段字节一致:同一 source tree + 同一工具链 pin 下重复构建,L1 一致是强制的。
- L2 —— 完整 flash image 字节一致:目标是生产场景 90% 的覆盖。
SOURCE_DATE_EPOCH全程尊重;剩下 10% 通常是第三方 crate 内嵌 时间戳或非确定性配置生成。
pin 漂移只发生在跨 store-manifest 修订时,且每次 pin 漂移都会先在 store changelog 公告。
CubeMX 项目布局 —— CMakeLists.txt 零改动
Path A 下,CubeMX 生成的标准 CMakeLists.txt 直接可用。云端
注入的 toolchain 文件用 CMake 的 CMAKE_PROJECT_INCLUDE_BEFORE
钩子,自动给每个可执行 target 挂上 firmware.elf → firmware.bin / firmware.hex + 签名 这条流水线。你不需要加任何
add_cortexm_firmware(...) 调用。
如果你的项目已经自定义了 CMAKE_PROJECT_INCLUDE 或
CMAKE_PROJECT_INCLUDE_BEFORE,云端是追加而不是覆盖 —— 你
已有的 hook 不被破坏。
净效果:CubeMX 的 CubeG4 模板新导出的项目,直接进云端 build 就 能编通过 + 打包,零改动。
今天就能用的(本地 Cortex-M 表面)
下面的命令现在就能跑,只要你手上有一个预先打好的 Cortex-M flash bundle,不管这个 bundle 是怎么来的。
1. 配置本地主机
让 probe-rs 不用 root 就能和调试探头通信:
espctl provision-host --dry-run # 打印计划
espctl provision-host # 实际执行(Phase 3+;Phase 0 仅 dry-run)
Linux 上写 udev 规则;Windows 打印 Zadig 提示;macOS 开箱即用。
看 CLI 实用工具 — espctl provision-host。
2. 列出当前连接的探头
espctl probes list # 表格
espctl probes list --json # 给 agent 用的管道格式
返回 vendor / product / serial / VID:PID 和给 --probe 用的
probe-rs 标识。看
CLI 实用工具 — espctl probes。
3. 烧 Cortex-M bundle
espctl flash flash_bundle.tar.gz \
--probe auto # 或 VID:PID[:SERIAL]
--chip STM32G431RBTx # 板子变种和 bundle 的 probe_rs_chip 不一致时覆盖
# --no-verify # 跳过 probe-rs verify
espctl flash 读 bundle 的 FlashTarget:
Esp32 { .. }→ 走纯 Rust 的espflash(USB-serial,要--port)。CortexM { probe_rs_chip, elf, .. }→ 走probe-rs(USB,不要 serial port)。--probe/--chip/--no-verify生效,--port/--baud忽略。
ADR-007 定义 FlashTarget 枚举。看 固件与烧录。
4. RTT 监听
espctl monitor --bundle flash_bundle.tar.gz # 模式从 bundle 自动判定 RTT
espctl monitor --rtt --chip STM32G431RBTx # 不带 bundle 显式走 RTT
带 --bundle 时,espctl monitor 读 bundle 的 FlashTarget:
CortexM→ 默认 RTT 模式,自动从 bundle 抽 ELF 给probe-rs attach。Esp32→ 默认 UART 模式(原有串口流)。
要强制某种模式用 --rtt 或 --uart,两者互斥。
在开放中(云端构建路径)
三条路径按档位逐步落地。三条远程构建端点共用同一个鉴权流、
同一个签名管线、同一个 flash-bundle 输出 schema,与 ESP32 / ESP-IDF
完全一致;唯一区别是工具链镜像和 bundle 的 FlashTarget 字段。
路径落地后,公开表面长这样:
# Path A 或 B —— 远程构建(规划中)
espctl build --target stm32g431 --remote # cwd 是 CMake 或 Cargo 项目
# → flash_bundle.tar.gz
# Path C —— 本地 ELF + 云端打包(规划中)
espctl build --rust-elf path/to/local.elf \
--target stm32g431 # objcopy + 签名 + 打包
# → flash_bundle.tar.gz
今天的 espctl build --rust-elf 产出 ESP32-S3 bundle
(espflash save-image --merge);Path C 落地后,这个标志会接受
--target stm32*。
在那之前,任何能产生 ELF 的工具链都可以手工打成 Cortex-M flash
bundle,然后用 espctl flash 烧。
flash_bundle crate 定义清单 schema。
Path C 细节 —— espctl build --rust-elf 用于 Cortex-M(开放中)
Path C 对 Cortex-M 落地后,线上合同长这样(落地后可通过
espctl skills --format json 看):
| 字段 | 值 / 限制 | 说明 |
|---|---|---|
elf_blob 最大 | 16 MiB | 超限在提交时即拒。 |
| ELF 校验 | ELF magic + 32-bit class + little-endian + e_machine == EM_ARM(40) | 上传非 ELF / 64-bit ELF / 大端 ELF / 非 ARM ELF 立即返结构化错误。注意:e_flags 里的 VFP / soft-float ABI 标志不校验 —— thumbv6m soft-float 和 thumbv7em-eabihf 都能通过。 |
| 内存上限(archive-only) | 512 MiB | 够 arm-none-eabi-objcopy + Intel HEX + 签名 + 打包用。 |
| 墙钟超时 | 60 s | archive-only 任务超时直接 kill。 |
Manifest build_provenance | "client_supplied" | 让买家 / 校验者一眼看出 ELF 不是云端工具链产出的。 |
SBOM toolchain 段 | 空 | 云端只知道自己跑过 objcopy。要把源头工具链记录进 SBOM,自己用 build_meta 字段(自由文本,≤ 64 KiB)填。 |
bundle 完整性(签名 + 哈希)和 Path A / B 一致。区别在来源:
Path C bundle 说“Aegis 处理过这个 ELF“,Path A / B bundle 说
“Aegis 用钉死的工具链从源码构建了这个”。下游验证者和 deposit
台账会分别记下这两种状态。
一等公民的硬件支持
Champion 预览的一等公民:
- STM32G4 家族 —— 从 NUCLEO-G431RB 起步。Path A 模板 对齐 STM32CubeG4 HAL。
路线图:
- STM32F7 / STM32H7 / STM32L4 —— 顺序按用户需求。
支持列表外的板子走 Path C(本地 ELF + 云端打包) ——
arm-none-eabi-objcopy 对 ARM 目标家族无所谓,bundle 格式由
probe_rs_chip 驱动,所以只要 probe-rs 支持的芯片都能烧 + 监听。
构建 → 入账 → 复盘 同样适用
典型工作流 的三段循环对 Cortex-M 与 ESP32 完全一致:
- 构建 → 产出
flash_bundle.tar.gz(三条路径任意一条)。 - 入账 →
espctl deposit add <build_id>把(需求, 代码, 物理验证)三元组写到~/maker-assets/。STM32 构建和 ESP32 构建落在同一份三元组台账里。 - 复盘 → push 到
/ops/funnel-review的MakerStatsSnapshot有一个hardwareMix[]字段 —— STM32 平台和esp32-s3一起 出现在那里。看 周度制作者数据资产复盘。