用 AI 开发 Zephyr-IoT 应用

在持续改进基于 AI 的嵌入式软件工作流的过程中,本文将同样的 AI 智能体协作纪律应用到了 Zephyr 上。在本文的实验中,乐鑫仍使用同一套来自 M5Stack 的 ESP DualKey 套件,沿用相同的规格说明、集成规则、计划 → 执行 → 提交 → 测试流程、可复用模块、开发日志以及 Git 管理方式------但并未重新定义产品本身。

引言

上一篇文章中,乐鑫带着来自 M5Stack 的 ESP DualKey 走过了一段 Rust 固件开发之旅。目标是什么?将乐鑫的统一配网(Unified Provisioning)功能从 ESP-IDF 移植到 BLE 和 SoftAP 上,并构建出一个能够联网的产品。尽管项目按预期完成,产出了预期的成果,但真正重要的是这套工作流本身。

本文正是围绕这套工作流展开,这次应用到了 Zephyr 上。这里想阐述的重点并不是如何成为 Zephyr 专家。作为乐鑫内部 Zephyr 的产品经理------同时也偶尔提供技术支持------作者虽有一定经验积累,但本文的核心目标并非 Zephyr 本身,而是如何借助 AI 以不同的方式开展工作,抛开表面的喧嚣。真正持久的收获在于如何与智能体协作------规格说明、边界、Git、证据------而不是纸面上哪种语言或哪种 RTOS 更胜一筹。如今写代码的速度很快,难点转变为管理代码及其周边的一切:架构设计、组织结构、测试验证,以及知道何时该让模型停止敲代码。写代码这件事本身正在淡出,但用"代码"的方式去思考问题依然至关重要。

这一次,ESP DualKey 仍然需要以和之前一样的方式,与乐鑫 ESP BLE Provisioning 应用通信。这次只是把实现迁移到了 Zephyr C,并且在与智能体协作的方式上变得更加严格------更清晰的规格说明、更明确的边界、在 Git 管理和实测台验证方面更强的纪律性。若尚未阅读过 Rust 那篇文章,建议先从那里读起再回到本文,这样对产品脉络的理解会更加清晰。

最后,也是更重要的一点:本文重点在于应用已经学到的经验,这次不再堆砌大量原始日志内容。

工具与目标:Cursor 与产品定义

这次实验并非从零开始。在智能体协作方面,依然延续了此前熟悉的 Cursor 使用习惯------工作量较大时启用 Plan(计划)模式;编码风格、项目边界、结构规范等约定也一并沿用。

乐鑫仍然沿用了 Rust 项目中的产品规格说明 ,并以 Rust 代码树作为行为参照------按钮该做什么、配网体验该是什么样、实测台上"完成"意味着什么。ESP-IDF 和 Rust 阶段积累的成果被尽可能地复用。

真正全新的部分,是一个新操作系统的引入:Zephyr。常规的 Zephyr 项目活动、任务、方法和流程,这次全部结合 AI 来完成。由此便要谈到 Zephyr 本身......

关于 Zephyr

ZephyrZephyr 项目旗下的一款开源实时操作系统,由 Linux 基金会(The Linux Foundation)负责管理。它远不止是一个内核,而是一个庞大的树内目录,包含驱动程序、协议栈和各类服务,通过 Kconfig 和**设备树(devicetree)**进行配置,并通过 west 元工具完成构建。它将 RTOS、板级支持、库文件与应用程序整合在一起。板卡和 SoC 的集成工作在上游完成;产品固件和可复用模块通常存放在独立的代码仓库中,而不是放在 OS 代码树内部。

Zephyr 在乐鑫芯片上的支持: 乐鑫为 ESP32 系列 SoC 提供并支持 Zephyr。在乐鑫内部,Zephyr 被视为一种可运行在乐鑫芯片平台上的 OS 发行版,是一套成熟可靠的解决方案,相关工作自 2020 年以来持续推进。Zephyr 的当前支持状态可在开发者门户的状态页面中查阅。

为什么选择 Zephyr

如前所述,乐鑫已经支持 Zephyr。Zephyr 在乐鑫用户中的采用率一直保持稳步增长,越来越多的开发者开始考虑采用"乐鑫 + Zephyr"这一组合来推进项目。作为芯片厂商,乐鑫始终致力于为用户提供优质的开发体验和完善的资源支持。然而,也观察到部分客户反馈从零搭建新项目存在一定困难。

这恰好是验证新方法的一个良好契机。此前已有类似设计的实践经验可供参考:手机 App 和参考 C 代码定义了何为"正确",Zephyr 代码负责实现协议,而不是重新定义协议。正因如此,选择 Zephyr 来继续演进这套工作流程,并非出于对固件开发本身认知的欠缺,也不是因为 Zephyr 要在所有场景中取代 Rust------它们都是优秀的解决方案,正如 ESP-IDF 一样出色。

就 Zephyr 而言,作者此前已完成安装与配置。Zephyr 的安装过程并不复杂,相关文档也相当完善。理论上 AI 也能够完成这项安装工作,不过这将是后续测试的内容。

以产品定义为目标

乐鑫沿用了 docs/spec/product_spec.md,并在 Zephyr 相关之处做了针对性调整------手势操作、LED 指示、配网流程的进入与退出(包括重启策略)、MQTT 和 HID,以及明确划定的范围外内容。这并非一次全新的产品定义,而是将同一份契约迁移到了新的技术栈上。

在借助 AI 开展开发时,那份文档正是目标 所在,正如前一篇文章中提到的那样,以规格说明的形式呈现:SSID 与 BLE 名称、超时时间设置、产品层面的取消逻辑,以及实测台上"完成"具体意味着什么。当规格说明与代码出现分歧时,需要有意识地修正其中一方------绝不能让两者同时各自漂移。

依然是同一套 ESP DualKey 套件,只是这次贴上了标识贴纸。

设计原则

已有的习惯

这次实验并非重新开始,基于常识总结出的一些整体性规则,已经足以支撑起点。以下是经过补充和重新措辞后的内容:

  • 一份书面规格说明胜过临场发挥的提示词。
  • 在协议相关的工作中,参考代码(ESP-IDF 示例、protocomm)胜过纯文字描述。
  • Git 是实验出错时的撤销手段;每个里程碑都值得一次提交。
  • 有边界的任务胜过"实现整个子系统"这类大而全的任务。
  • 追踪记录与实测台证据胜过仅凭源代码进行推断。
  • 架构设计与评估工作由开发者主导;打字(编码)交给智能体完成。
  • 一旦交付出现问题,责任由开发者本人承担
  • 一旦方向明确,编码工作应交由智能体完成。
  • 遇到卡壳时,开发者与智能体共同调试

除此之外,还积累了其他经验和改进之处。

Rust 文章带来的启示(主要是命名与体系化)

上述清单源自常识的沉淀;撰写那篇文章时,为其赋予了便于记忆的表述方式。此后又总结出一些经验,这里值得原汁原味地保留下来:

  • Agent 模式只有在双方就下一步方向达成一致后才启用。
  • 当需要按顺序执行大量步骤时才使用 Plan(计划)模式,而不是依靠一场持续、重复的"修复"对话。
  • 每份计划都对应一次提交------细粒度的检查点,确保当协作伙伴打字(编码)时,二分查找 (bisect) 和回滚依然可行。
  • 关注结构与组织------即"打扫房间"------熵 (entropy) 不仅体现在低质量代码上,也体现在重复的文档、散落的脚本以及放错位置的文件中。

这篇文章中真正全新的内容,而非仅仅是换个说法重复,包括:将"熵"明确定义为一种失败模式;一张常见坑点清单(虚构出的 API、协议漂移、混杂代码 、过度重构、时序意外);脚手架式的工作习惯(生成器、编辑器规则、启动提示词、精选参考示例);尽可能引入仿真与 CI 验证;以及"清理"过程中误删调试日志的教训------当时双方都未将实验记录本视为值得保留的资产。

Zephyr 这条线的实验,建立在假定该契约依然有效的基础上:评估者与编码者分工明确、规格说明先行、控制熵、以 Git 作为安全网。但一套更完整的流程正在逐渐成形,下文将详细展开:计划-编码-测试-提交循环。

Zephyr 集成原则

既然采用了 Zephyr,它也自带一套需要遵循的规则。虽然本次设计聚焦于 Zephyr,但这些规则完全可以被推广到任何操作系统上。具体如下:

  1. 尽可能充分使用 Zephyr------优先采用该 RTOS 及其原生支持的流程,而非在应用层并行搭建"迷你操作系统"层。
  2. 尽可能充分使用 OS 服务------网络管理、Wi-Fi 管理、BLE host、settings/NVS 模式、日志记录、工作队列:只要子系统已经存在,就应接入 Zephyr 子系统,而非自行搭建临时垫片方案。
  3. 尽可能充分使用 Zephyr 发行版------在自建私有副本之前,优先利用树内模块和 Kconfig 可选的构建模块;当功能缺失时,向上游贡献代码,而自行 forking 维护。
  4. 尽可能无缝地融入 Zephyr 生态 ------west 工作区、树外模块布局、设备树、prj.conf、示例代码,以及外观上与常规 Zephyr 集成方式一致的命名规范和文档风格。

这四条原则是极为出色的提示词约束条件:它们能够在模型凭空构想出一种"外来"项目形态之前,先行明确"好"的标准。后文会展示这些原则是如何直接体现在代码仓库和构建产物中的,而无需在每次使用时重新推导一遍。

三个层次: 产品行为(product_spec.md)、Zephyr 集成原则(上述清单),以及新的代码仓库与构建布局(详见后文)。


Zephyr 集成原则的新经验(本次实验)

如果说上述原则相当于"宪法",即以往固件开发经验与 Rust 实验教训的沉淀,那么接下来要讲述的就是 Zephyr 配网实验之后新增的"修正案"。这次实践揭示了更多层面的问题,其中一些是全新发现,另一些则是对已有认知的修正。经验的价值正在于此。

这一次也保留了一份完整的开发日志。所有的经验积累都呈现出叠加效应,能够携带更多的知识继续向前推进。

代码提速,判断仍需慎重

模型敲代码的速度远超人类,但这并不会减轻评估者的工作量,反而加重了这一角色的负担。速度提升是实际的;而真正的价值,始终体现在批判性思考、架构设计、测试验证、调试排错和组织协调这些环节上,始终得由人类亲自把关。

快(智能体) 慢(开发者)
起草模块、执行重构、编写样板代码 判断该架构是否应该存在
提出 API 形态建议 判断某个公开 API 是否稳定、精简
快速阅读源代码 判断追踪记录能证明芯片上实际发生了什么
生成大规模代码差异 判断该里程碑阶段是否允许提交如此大的差异

这次实验进一步印证了这一点:当配网出现异常行为时,真正有效的做法依然是"贴出日志、说明预期结果、询问哪个分支出了问题",而不是"持续生成代码,直到编译通过为止"。

组织工作是开发者的职责

Rust 那篇文章中提到的"熵"问题在这次实践中依然成立:如果没有人主动强制维护结构,用完即弃的脚本、重复的文档,以及放错文件夹的"顺手生成"文件就会不断累积。规格说明与日志的区分、模块与应用程序的边界、.gitignore 配置、子模块 (submodule) 版本锁定,以及哪些内容该删除、哪些该提交------这些始终是开发者本人需要把关的职责范围。

去重同样是这项工作的一部分:当某个模块已有独立手册时,就不应在产品代码仓库中重复导出相同的说明文字。这次实践中,这一纪律得到了保持(与 Rust 项目"清理"过程中日志丢失的教训形成鲜明对比)。

别再让 AI 拥有整棵代码树------拆分出独立组件

此前曾经历过一个"AI 在同一棵代码树中包揽一切"的阶段。这次实践转向了可复用组件验证用应用程序精简产品三层结构------与具体技术栈无关,并且明确考虑到了可复用性需求。

混杂代码(通用性修正方案): Rust 那篇文章中已经点出了这一常见坑点------逻辑全部堆积在同一棵代码树中,边界薄弱,除非强制要求,否则模型不会主动构建出清晰的层次结构。这次的应对方式从架构层面入手,而非停留在语法层面:一个正式的组件、一个正式的应用程序、一份独立于调试日志之外的规范化组件规格说明,以及一个像对待独立软件库那样接受审查的公开 API。esp-provisioning 模块与 ESP DualKey 代码仓库正是这套思路在 Zephyr 上的具体呈现;但这一经验并不局限于 Zephyr 本身。

先验证,后产品化: 在接入完整的产品应用之前,先烧录并实测对应的示例程序或测试工具 (harness)。这与计划循环中的"验证"环节直接对应。在 Zephyr 上,该测试工具正是 esp_provisioning_shell

与智能体协作的有效策略

以下策略并不局限于 Zephyr 场景:

  • 源代码本身就是一份规格说明------与具体语言无关。为确保精确性,应将智能体指向正在运行的代码树、模块 API 头文件以及 ESP-IDF 参考实现;意图表达和代码审查则使用 Markdown 文档完成。
  • 组件与验证应用先行,随后再进行产品集成。
  • 上游优先。 若需要为上游项目添加或改进功能,建议暂停手头工作,先完成上游相关工作,再回到主线继续推进。
  • API 边界: 共享模块不应吸纳产品层面的业务策略。配网模块随着时间推移被持续裁剪,正是因为其边界一直在被"污染"。产品归产品,库归库,无论该库当前是否已经存在。
  • 每次计划执行只对应一个里程碑: 一份计划、一次执行,随后完成验证与提交,再进入下一份计划。

Git 与产物管理(本次实践的观察)

频繁提交是硬性要求。 子模块版本更新、API 裁剪、传输层修复,都需要细粒度的提交记录,否则 git bisect 便失去了应有的意义。这并非形式主义,而是与打字速度极快、且缺乏自我约束的协作伙伴共事时的生存法则。

AI 不负责处理上游 Git 操作。 智能体可以准备差异对比 (diff) 和摘要说明,但上游 Pull Request 的落地工作需由人类完成。某些关键环节------如同火车与飞机的运行------始终需要人类监督,一旦失控,后果可能相当严重。

开发日志,必须坚持保留。 这次实践中,journal.md 被保留下来,作为一份局部化的"排障与防止重复走弯路"记录。同时也发现,它还能有效防止模型陷入重复推理。AI 本质上是一台统计机器;经过一段时间后,它可能会再次尝试此前已被证明无效的方案。这份日志有效阻止了这种情况的发生,其效果远超预期,且体现在诸多意想不到的方面。这一次,日志被存放在模块代码仓库中。每份计划要做的第一件事,就是更新日志

通常的做法是:在制定计划阶段就明确要求------待办事项:将上一轮循环中所有正面结果,以及已尝试但未能解决问题的方案,更新进日志。问题描述与对应的修复方案会被一并记录,也就是说,问题和修复方案会共同写入日志。很多情况下,当 AI 撤销某项改动、查阅日志、找到一条直接相关或类似的记录后,往往能在同一轮次中就完成修复。

计划 → 执行 → 提交 → 测试(核心齿轮)

这套节奏具有通用性 ------无论是 Rust、Zephyr,还是其他任何技术栈都同样适用。Zephyr 在这里仅作为具体案例出现。

图 2 - 提出的开发循环。

Rust 那篇文章曾提及针对长序列任务需要进行计划,但并未把"计划执行完成之后该如何推进"落实为具体的操作步骤。这次将其提升为独立的一个环节:

  1. 计划:在开展非平凡的智能体工作之前先制定计划(借助 Cursor 的 Plan 模式,或撰写一份明确的计划文档)。
  2. 执行:由智能体、开发者,或双方协作完成计划的执行。
  3. 提交:在测试进入较为棘手的阶段之前,先提交一个可回滚的检查点------此时改动依然容易撤回------因为在实测台验证过程中,智能体(或开发者本人)都可能不受控制地想要"再多修一处"。
  4. 测试 :在实测台上进行验证:构建、烧录、冒烟测试场景;对于组件而言,需先跑通示例测试工具,再接入完整产品。
  5. 循环 :测试完成后,基于上一阶段的结果重新启动计划环节,而不是在一次失败的运行基础上,不断堆叠没有边界的"修复"提示词。务必回到计划阶段------放任 AI 无节制地写代码、自行修复 Bug 的诱惑始终存在,但应当加以抵制。一旦如此,AI 生成的代码将变得难以维护、难以支撑。

从流程角度看,这本质上就是 PDCA(计划-执行-检查-处理,Plan-Do-Check-Act)穿上了固件开发的外衣,这一规律依然成立。在第一篇文章中曾提到,与 AI 协作在技术层面上与管理初级开发者极为相似,这次的实践进一步印证了这一 PDCA 理念。通过将整个开发过程以频繁提交的方式记录进 Git,日后无论是审查方案还是加以控制,都会变得相对容易。

Git 依然是那张安全网;提交粒度与产物管理习惯让 bisect 保持可靠;而这套循环正是让契约真正落地的运行节奏------计划不是对话中的一种"奢侈行为",每一次执行都应以验证和提交收尾。在有人质疑这样做是否会导致 Git 历史变得杂乱之前,需要说明的是:提交记录随时可以被压缩(squash),并回顾其中的提交信息------也就是那些计划------审视各次提交之间真正保留下来的内容。这在可追溯性方面是一次显著提升,虽然会带来一定程度的熵增,但这种熵增是可控的。

开发者曾一度放松了对提交之间所有 Cursor 计划的管控,导致这些计划被 AI 用于生成提交信息,甚至计划本身也被直接提交。再一次,这对协作组合中负责批判性思考的一方(即开发者本人)未能尽到应尽的职责。避免重蹈覆辙,本身就是学习的意义所在。

Zephyr 特有经验(本次在 ESP32 上的实验)

这次实践中同样积累了一些技巧,并且发现了若干与 Zephyr 相关的经验,这些经验完全可以被归纳、推广到所使用的任何基础软件平台上。

使用的 Zephyr 集成模式

具体如下:

  • 模块布局: 遵循最佳实践。这并非全新理念,但始终值得反复强调。
  • 优先尝试示例应用。 这是一项能大幅节省时间与精力的明智决定。逐个单独验证各个组件,能够节省大量后续排查成本。
  • 产品应用需保持条理清晰。 业务逻辑应与基础组件支持能力相分离。
  • 不要触碰上游代码。 AI 存在一个较为棘手的倾向:一旦发现方便之处,就想去修复问题或添加追踪代码,这其中包括对 Zephyr 主干源码的大量修改。它无法区分代码的性质,在其视角中代码就是代码。针对这一点,作者设定了一条硬性规则。
  • 除非确实需要触碰上游内容。 这正是妙处所在。当 AI 确信新增 Zephyr 代码确有必要时,它会先征求许可再进行修改。此时会采取更为传统的方式,逐行审查相关代码。当改动可能影响到整个上游项目时,开发者会变得格外谨慎。

ESP DualKey / esp-provisioning 成果(简述)

公开代码仓库:

在实测台上,shell 示例通过 BLE 和 SoftAP 传输方式与乐鑫配网 App 完成联调;集成后的应用遵循同一份产品规格说明。模块 API 经过持续裁剪,以保持其应有的"软件库"形态。

结论

Rust 那篇文章中提出的结对编程分工方式依然成立:模型负责编码,开发者负责评估------依据规格说明、参考代码树、构建输出、UART 追踪记录以及实测台验证结果进行把关。代码是廉价的,判断力不是。 随着实践的持续推进,真正发生变化的是:一整套围绕 AI 驱动开发的流程正在逐步沉淀成型,而不再将每一次对话都当作一次性行为处理。规格说明、规则体系、开发日志、可复用模块、先验证后产品化的顺序、提交粒度,以及"计划 → 执行 → 提交 → 测试"这一循环,都不是追赶潮流的附加环节------它们才是真正实现生产力提升、且不必为无法追溯的回归问题付出代价的关键所在。

这些经验是层层累积而成的。Rust 那一轮实践带来了协作契约、熵的控制方法,以及关于混杂代码树和丢失日志的警示。Zephyr 这一轮则新增了平台化的组件设计、更清晰的 API 边界、实测台上"先验证后产品化"的做法,以及一条由开发者主导的上游 Git 协作路径。这一切都没有取代此前已经建立起的纪律。PDCA 依然贯穿始终,只是换上了 Cursor,配上了一位打字速度更快的协作伙伴。Git 作为撤销手段、有边界的任务划分、UART 作为事实依据------这些原则同样一以贯之。真正的挑战,并不在于每次切换技术栈都要重新发明一套全新的"教条",而在于充分复用已经掌握的经验:将产品规格说明沿用下去,让智能体参照既有源码与参考实现,把规则一次性明确写下来,并保留好下一次协作时可供读取的产物。若每次都从零开始,只会重蹈覆辙------丢失的日志、臃肿的代码树,以及那种"应该能行"却缺乏实际依据的侥幸心理。

因此,接下来的工作既是编码,更是一种持续的梳理与沉淀:坚持原则、用项目标准训练智能体,让其在敲代码环节全面提速,同时开发者始终守住评估者、策略制定者,以及为整个项目指明方向的"舰长与领航员"这一角色。这正是速度真正能够转化为价值的地方。

相关链接

相关推荐
数智化管理手记1 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
G***技1 小时前
从电路角度看嵌入式主板IB3-771的“不死机”设计
人工智能·边缘计算·嵌入式主板
matlab代码1 小时前
基于CNN卷积神经网络交通标志识别系统 (GUI界面 数字图像处理)【源码48期】
人工智能·神经网络·cnn·交通标志识别
培之1 小时前
RTX 5090 安装 pytorch3d
人工智能·pytorch·python
大鱼>1 小时前
深入Real-ESRGAN架构:RRDBNet设计精髓与ONNX/TensorRT部署优化
人工智能·深度学习·架构
延卿2 小时前
nnDetection:基于 PyTorch 的目标检测框架
人工智能·pytorch·目标检测
电商API_180079052472 小时前
企业ERP进销存场景|京东商品详情接口自动同步方案|凭证鉴权批量调用技术实操
大数据·运维·人工智能·爬虫·数据挖掘·网络爬虫
qizayaoshuap2 小时前
# ❌ 井字棋 — 鸿蒙ArkTS Minimax AI算法与博弈系统设计
人工智能·算法·华为·harmonyos
雪隐2 小时前
个人电脑玩AI-13让5060 Ti给你打工——我用 0.9B 小模型终结了"谁来记会议纪要"这个世纪难题
前端·人工智能·后端