图 4 - 设备状态机转换图
整个设备的"心情"由 11 个状态描述,状态之间只有特定的合法跳转——这张图把所有跳转列全了。
如果上方图无法显示,点这里看 PNG 版本

11 个状态总览
| 状态 | 中文 | 屏幕/灯效 | 含义 |
|---|---|---|---|
| Unknown | 未知 | 黑 | 还没初始化 |
| Starting | 启动中 | 滚动条 | 正在执行各种 Init |
| WifiConfiguring | 配网 | 配网图标 | 没 Wi-Fi 凭证或连不上 |
| Activating | 激活中 | 激活码 | 需要用户在 App 输码激活 |
| Idle | 待机 | 时钟/表情 | 监听唤醒词中 |
| Connecting | 连接中 | 加载圈 | 正在打开音频通道 |
| Listening | 听话中 | 蓝光 | 收音上传到云 |
| Speaking | 说话中 | 绿光 | 播放云端 TTS |
| Upgrading | 升级中 | 进度条 | 跑 OTA |
| FatalError | 致命错误 | 红光+错码 | 不可恢复错误 |
| AudioTesting | 自检 | 测试图 | 工厂烧录时跑音频测试 |
合法跳转的关键规则
代码里 IsValidTransition() 函数定义了所有合法跳转。不允许跳转的会被 ERROR 日志阻止——这是保证状态一致性的重要手段。
几个高频规则:
- Idle 是中枢:基本所有功能性状态都从 Idle 出发,结束后也回到 Idle。
- Listening ↔ Speaking 自由切换:用户打断 / 连续对话场景下来回切。
- Upgrading 是单向:进了就只能
esp_restart出来,不允许回退。 - FatalError 也是单向:进了等用户重启。
- WifiConfiguring 是 Starting 的特殊分支:配完会回 Starting 重新走一遍流程。
监听者模式
任何模块都可以 RegisterListener(callback) 监听状态变更。Application 主要用这个机制:
- 状态变了 → 监听器把"主线程事件" set 起来 → 主循环
HandleStateChangedEvent()根据新状态做对应反应(开关唤醒词、切 LED、切屏幕等)。
注意线程安全:监听器列表用 mutex 保护;通知时先拷贝列表再调回调,防止回调内部修改列表造成死锁。
一句话讲清
"设备就 11 种心情,每种心情该亮什么灯、显示什么、监听什么音频都是写死的。所有交互都体现为状态间的跳转,跳转规则由
IsValidTransition()集中管控,非法跳转会被丢弃并打日志。"
关联章节
/03-state-machine整章