摘要:VibeStick 把 M5Stack StickS3 变成桌面 AI Agent 的状态屏和语音遥控器。本文从源码出发,拆解固件、Python Bridge、Agent 观察器、ASR 与桌面粘贴的完整链路。

建议标签:ESP32-S3 Python LVGL AI Agent 物联网

很多“AI 硬件”的架构图喜欢画一朵云,再画一道闪电,仿佛多画几个箭头,智能就会自己从纸里长出来。VibeStick 的思路朴素得多:让小设备做它擅长的事,让电脑做它不得不做的事。

StickS3 有屏幕、按键、麦克风、扬声器和 Wi-Fi,但没有必要保管 Codex 会话、Claude 凭据和 ASR Key。电脑看得到本地 Agent 的活动,也有能力访问云端或本地语音模型。因此项目被拆成两大运行时:

Codex / Claude 本地活动
          |
          v
Python Bridge <----HTTP/UDP----> StickS3
      |                              |
      +--> ASR                       +--> LVGL 屏幕
      +--> 剪贴板与按键注入          +--> 麦克风/扬声器/按键
      +--> HUD 与诊断

对应源码并不捉迷藏:

  • firmware/sticks3/src/main.c:固件主流程、LVGL UI、HTTP、按键和状态渲染;
  • firmware/sticks3/src/vibe_audio.c:16 kHz、16 bit、单声道 PCM 录音与提示音;
  • firmware/sticks3/src/vibe_provisioning.c:SoftAP、Captive Portal、Wi-Fi 扫描和 NVS;
  • bridge/src/vibe_stick/server/app.py:Bridge HTTP API 和状态汇总;
  • bridge/src/vibe_stick/providers/:Codex、Claude 观察器适配层;
  • bridge/src/vibe_stick/audio/:WAV 写入磁盘、ASR 与重试;
  • bridge/src/vibe_stick/paste/input_injector.py:macOS/Windows 粘贴注入。

一、为什么不是让 StickS3 直连 AI 服务

这不是“ESP32 性能不够”一句话就能概括的问题。真正的约束至少有四层。

第一,Agent 状态来自电脑本地。Codex 的运行状态和配额来自本地进程、会话 JSONL 中的事件与 rate_limits;Claude 的状态也要结合进程和本地项目日志判断。StickS3 隔着 Wi-Fi 再努力,也不会突然长出读取电脑文件系统的超能力。

第二,凭据边界更清楚。固件只保存 Wi-Fi、Bridge 地址、端口和共享 Token,ASR Key 与 Agent 凭据都留在电脑端。这样设备丢了,损失至少不会自动升级成“云账户也跟着丢失”。

第三,跨平台能力集中在 Bridge。剪贴板注入在 macOS 使用 pbcopy/pbpaste + osascript,在 Windows 使用 PowerShell、System.Windows.Forms.SendKeys。这些差异放进固件,只会把 C 项目炖成一锅操作系统火锅。

第四,服务可替换。TranscriptionAdapter 可以走本地命令,也可以调用 OpenAI-compatible /audio/transcriptions。固件只上传 PCM,不关心后面坐着 SiliconFlow、Groq、faster-whisper,还是未来的新模型。

二、状态链路:两秒一次的“你忙完了吗”

固件常量 VIBE_STICK_STATE_POLL_MS 为 2000。设备通过 GET /state 拉取状态,Bridge 返回统一的 VibeStickState

@dataclass
class VibeStickState:
    time: str
    wifi: bool
    ble: bool
    battery: int | None
    active_provider: str
    provider: ProviderState
    codex: CodexState
    alert: AlertState

provider 是当前活动 Agent 的统一视图,codex 则保留兼容字段。状态包含 IDLERUNNINGDONEAPPROVALERROROFFLINE 等值。固件用 cJSON 解析后更新屏幕上的状态点、项目名和 5H/7D 配额。

值得注意的是,电池值由设备本地 PMIC 读取,而不是信任 Bridge。Bridge 的 to_jsonable() 甚至明确把 battery 置为 None。谁离电池近,谁说了算,架构上没有安排远程电脑隔空把脉。

告警也不是每两秒响一次。Bridge 为完成、审批和错误生成稳定 event_id,固件记住上一次播放的事件。只有事件发生变化才播放提示音,否则一个完成事件能把工位变成电子门铃体验区。

三、语音链路:按住说话,松开发送

语音流程跨越硬件、网络、ASR 和桌面输入:

  1. 长按正面蓝键,固件启动麦克风并调用 /recording/start
  2. I2S 以 16 kHz、16 bit、单声道采集 PCM,最长 45 秒;
  3. 松开按键,设备显示 UPLOADING 并向 /recording/audio 上传二进制音频数据;
  4. Bridge 将 PCM 封装为 WAV,进行时长、静音和削峰检查;
  5. TranscriptionAdapter 调用本地命令或 OpenAI-compatible ASR;
  6. 成功后,PasteInjector 将文本粘贴进当前焦点应用;
  7. 设备显示 SENT,失败时保留 PCM,并允许短按重试。

这条链路里最有产品味的不是“支持语音”,而是失败以后怎么办。固件保留录音缓冲区,Bridge 返回 retryable 和尝试次数,默认每段录音最多处理 3 次。网络临时抽风时,用户不用把同一句需求重新表演三遍。

四、配网与发现:IP 地址不应成为入门考试

首次启动时,设备创建 VibeStick-XXXX SoftAP,并在 192.168.4.1 提供配置页。Wi-Fi、Bridge 地址和 Token 被写入 NVS。Captive Portal DNS 会把常见探测请求引到配置页,附近 2.4 GHz 网络也可以直接扫描。

如果电脑 DHCP 地址变化,固件会向 UDP 8766 广播发现请求。Bridge 校验 Token 后返回服务端口,设备从 UDP 源地址获取新主机地址并更新 NVS。这比要求用户背诵 ipconfig 的输出友好多了,毕竟产品说明书不该兼任网络管理员招聘试卷。

五、安全边界:够用,但还没到高枕无忧

Bridge 在非 loopback 地址监听时要求提供有效 Token;受保护接口使用 X-Vibe-Stick-Token,服务端通过 hmac.compare_digest 比较。管理页只允许本机 loopback 访问,录音上传大小默认限制为 2 MB。

但当前传输仍是局域网内的 HTTP,不是 HTTPS;SoftAP 的安全配对、Token 轮换、NVS 加密和设备解绑还在未来规划中。因此它适合受信任的家庭或办公局域网,不应被描述成已经完成零信任加固的商业硬件。

六、这套架构最值得借鉴什么

VibeStick 的价值不只是一块会显示百分比的小屏幕。它演示了一种实用的 AI 外设架构:硬件负责确定性的输入输出,Bridge 吸收不稳定的 Agent、模型和操作系统差异,协议只传递稳定的状态与音频。

这种分层让后续演进有路可走:加新 Agent 时扩展 provider;换 ASR 时替换 adapter;支持新设备时围绕协议做硬件抽象。反过来,如果一开始就让固件直接登录各种服务,版本 0.1 可能还没发布,证书、OAuth 和内存占用已经先开完三轮需求评审。

下一篇将钻进 main.cvibe_audio.c,看看 135×240 的屏幕、两个按键、一颗麦克风和一只扬声器,如何在资源有限的设备里协同工作。


本文基于 VibeStick 当前工作区 0.1.4 源码。VibeStick 是社区项目,不是 M5Stack、OpenAI 或 Anthropic 官方产品;Codex/Claude 名称仅用于说明兼容能力。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐