快速讲解卡
把整个项目浓缩成 15 段速记,每段 30 秒能说完一块。可以拿来快速回顾整体脉络,也可以作为对外讲解的素材——配合视觉资源里的图边讲边比划,比单纯念说明书有效得多。
用法:每段开头有
[图 N]提示该翻到哪张图配合看/讲。
段 1:30 秒电梯介绍
这是一个跑在 ESP32 上的离线唤醒 + 联网对话 的 AI 语音设备固件。用户喊"你好小智"它就醒,醒了之后录音上传到云端大模型,大模型回复的语音流回来设备播给用户听。整个项目支持 110 多块开发板,从面包板上的 ESP32-S3 到带触摸彩屏的旗舰板都能跑。
段 2:项目能力速览
它能做四件事:
- 离线唤醒:唤醒词检测在设备本地用 ESP-SR 跑,不联网也能识别"你好小智";
- 流式对话:唤醒后语音流式上传,云端流式 ASR + LLM + TTS,端到端延迟一般 800ms 到 1.5 秒;
- 设备控制:通过 MCP 协议让大模型反向调用设备能力,比如"把音量调到 80"、"拍一张照片"、"打开桌上的灯";
- 远程升级:A/B 双分区 OTA,跑不起来自动回滚,永不变砖。
段 3:整体架构 — [图 1: 架构总览]
看这张图:上半是云端,下半是 ESP32 设备。
设备端最核心的是黄色那个 Application——主调度器。所有子系统都接在它上面。
数据流就两条:
- 上行:麦克风进来的 PCM 先过 AFE 做回声消除和降噪,再压成 Opus 走 WebSocket 或 MQTT+UDP 发到云端;
- 下行:云端的 Opus 解码后塞给喇叭。
还有一个 MCP Server——它是设备端的工具接口注册中心,让大模型可以反向控制设备。
段 4:启动流程 — [图 2: 启动时序]
设备从按下开关到能听用户说话总共 3 到 8 秒,分这几步:
- Bootloader 跳到固件(100ms)
- Board 把硬件初始化好(200-400ms)—— 屏幕、按键、codec 都在这一步亮起来
- Application 初始化软件子系统(500ms)
- 启动 Wi-Fi 或 4G(1-3 秒,看信号)
- 联网后 POST 设备信息到 OTA 服务器,可能拉新资源(500-2000ms)
- 协议建链握手(几百毫秒)
- 进 Idle 状态,唤醒词开始监听
这个流程的关键点是 Board 一定要先于 Application——后者要知道有什么硬件才能驱动。
段 5:音频处理 — [图 3: 音频数据流]
这是这个项目最值得讲的一块。看图:
上面是上行链:麦克风 → AFE 处理 → Opus 编码 → 发网络。中间用 5 个队列把不同速度的环节解耦,每个环节独立的 FreeRTOS 任务,所以彼此不会卡。
AFE 是用 Espressif 自家的 ESP-SR 库,做三件事:AEC 消回声 + VAD 检测人声 + NS 降噪。它会把"用户开口"信号实时报上来,让设备能打断 TTS。
下面是下行链:网络 → Opus 解码 → 重采样 → 喇叭。注意喇叭播放的 PCM 还会反馈给 AFE 当参考——这就是 AEC 能消回声的基础。
段 6:状态机 — [图 4: 状态机]
整个设备就 11 种"心情",每种心情都有自己该亮什么灯、显示什么、播什么提示音。
几个关键状态:
- Idle:待机,唤醒词常驻;
- Listening:在收音上传;
- Speaking:在播放云端 TTS;
- Upgrading:跑 OTA 中;
- WifiConfiguring:等用户配 Wi-Fi。
状态之间的转换是有规则的——比如不能从 Speaking 直接跳到 Upgrading,要经过 Idle。这个规则在
IsValidTransition()函数集中管控,跳错了会被丢弃并打日志。这种设计的好处是调试方便——出问题先看日志里状态转换序列,能很快定位是哪一步出错。
段 7:一次完整对话 — [图 5: 唤醒到回复]
看这个时序图,用户说"你好小智,今天天气怎么样":
- 第一句"你好小智"被本地 WakeNet 识别,触发唤醒(200-400ms);
- 设备状态切到 Listening,开始持续录音上传;
- 同时云端流式 ASR 在转文字、LLM 在推理、TTS 在合成——三个组件流水线并行;
- 云端发"tts.start"信令,设备切到 Speaking,开始播音;
- 一段话播完,回 Idle。
整个过程任何时候用户开口都能打断——AFE 持续监测人声,VAD 一报警就中止当前的 Speaking 直接进 Listening。
段 8:通信协议 — [图 6: 协议对比]
项目实现了两套协议,都是
Protocol抽象基类的不同实现,业务层完全无感。
- WebSocket:一条 WSS 长连接搞定一切,文本帧走 JSON-RPC,二进制帧走 Opus + 自定义包头。简单,Wi-Fi 场景首选。
- MQTT + UDP:MQTT 走控制信令,UDP 走音频流(用 AES-128-CTR 加密)。复杂但移动网络下更稳——丢几个 UDP 包不影响信令。
切协议只改
Application::InitializeProtocol()里的一行代码,其它都不动。
段 9:MCP 工具协议 — [图 7: MCP 时序]
这是这个项目最现代化的部分。MCP(Model Context Protocol)是去年才出来的协议,定义了大模型怎么调用外部工具。
设备端的
McpServer注册了一堆工具:调音量、调亮度、拍照、切主题、控制灯……流程是这样:
- 设备和云端建链后,云端 MCP Client 问"你有什么工具";
- 设备返回工具元数据(名字、参数、范围);
- 大模型看到这些工具描述就知道"我能让设备干什么";
- 用户说"调到 80",大模型决定调
self.set_volume({volume: 80});- 设备收到 JSON-RPC 请求,校验参数范围,然后派到主线程执行硬件操作;
- 结果回 JSON-RPC reply 给大模型。
关键设计:工具回调一定在主循环执行,保证硬件操作(GPIO/I²C)的线程安全。
段 10:OTA 升级 — [图 8: OTA 流程]
它怎么能远程升级又不变砖?看这张图:
- Flash 切两个对称的 OTA 分区,ota_0 和 ota_1;
- 任何时候只跑一个,升级时把新固件下到另一个分区;
- 下载完成 + SHA-256 校验 + 芯片型号检查通过;
esp_ota_set_boot_partition切启动指针 → 重启;- 新固件启动第一件事是开 1 秒看门狗——如果跑得正常,调
MarkCurrentVersionValid()关掉看门狗;- 如果跑挂了(看门狗触发),Bootloader 自动回滚到上一个分区。
这就是防砖的核心。再加上激活流程用 eFuse 内的 32 字节序列号 HMAC 签名,能防止山寨设备绕过认证。
段 11:配网 — [图 9: 三种配网]
三种配网方式可以同时启用:
- 热点配网:设备开 Wi-Fi 热点,手机连过去用浏览器填 SSID/密码——零依赖;
- BluFi 蓝牙配网:手机 App 用蓝牙发 SSID/密码——体验最好;
- 声波配网:手机喇叭播放 AFSK 调制的声音,设备麦克风听到解调——最神奇。
一套设备可以同时支持 3 种,给用户最大选择。
段 12:板级抽象 — [图 10: 板级类图]
110 块板子是怎么不打架的?看这个类图:
顶层是
Board抽象基类,定义了"所有板子都得回答什么问题"——比如GetAudioCodec()/GetDisplay()。第二层按网络栈分:
- WifiBoard — Wi-Fi 板
- Ml307Board — 4G 板
- DualNetworkBoard — 双栈板(运行时切换走重启)
第三层是具体板子,比如
CompactWifiBoard、EspBox3Board。每个板子的.cc文件末尾用DECLARE_BOARD宏注册自己,CMake 在编译期选其中一个,零运行时开销。这种设计的好处:加新板子完全不影响其它 109 个板——只要新增一个目录、改一处 Kconfig、改一处 CMakeLists。
段 13:文件结构 — [图 11: 文件依赖]
这张图看清核心文件之间的依赖关系。
application.cc是中央枢纽,所有子系统都通过抽象接口接进来。几个改动场景:
- 加 MCP 工具:板子目录加一个 controller,独立的;
- 加新协议:
protocols/加一个文件 + 改 Application 一行;- 加新音频 codec:
codecs/加一个文件 + 改 CMakeLists;- 加新板子:boards/ 加一个目录,对其它板零影响。
这种"开口在抽象层、改动在具体实现"的架构是项目能撑住 110 块板的关键。
段 14:用到的技术(关键词列表)
整个项目用到的硬技术,按层从高到低:
- C++ 17 现代特性:
std::function、std::variant、std::optional、RAII、智能指针- FreeRTOS 多任务:Task、Queue、EventGroup、Notify、Mutex
- 状态机模式 + 监听者模式
- 抽象基类 + 多态:协议、Codec、Display、LED、Board 全用了
- JSON-RPC 2.0 + MCP 协议
- WebSocket / MQTT / UDP / HTTP
- AES-128-CTR 流加密
- HMAC-SHA256 签名 + eFuse 硬件密钥
- A/B OTA 双分区 + 防回滚
- ESP-SR 语音前端(AEC/VAD/NS/WakeNet)
- Opus 编解码 + 重采样
- LVGL 图形库 + 自研 EmoteEngine 动画
- WS2812B(RMT 外设)+ PWM LED(LEDC 外设)
- CMake 板子派发 + Kconfig 可视化配置
- ESP-IDF Component Manager 包管理
段 15:可以扩展的方向
这套架构留了哪些扩展点?
- 加自家的 MCP 工具:注册到
McpServer就行——比如让 LLM 控制家里的智能音箱、查股票、记事;- 接私有 LLM:服务器侧改实现,设备端不变;
- 加多模态能力:摄像头已经做了
take_photo工具,可以继续加视频流;- 移植到非 ESP32 平台:只需要替换
Board抽象层的实现;- 加自定义唤醒词:项目已支持 Sherpa-ONNX 跑用户自训练的模型。