本章仅给速查表。字节级 legacy 规范请看 v4.2 BLE 协议规范——那是 legacy 唯一权威来源;CM4 单独由 CM4 协议 收敛(ASCII over BLE/MQTT)。本页只做 headline 以便快速定位。
| UUID 简写 | 方向 | 用途 |
|---|---|---|
| AE03 | App → 中继盒 | write / writeWithoutResponse(命令) |
| AE05 | 中继盒 → App | legacy: indicate;CM4: notify(数据 + 事件通知) |
服务容器:AE30。但 App 启动扫描时不是按该服务 UUID 匹配,而是按广播名是否以 CM / MW 开头来筛选设备;连接后才发现并校验 AE30 服务。BleService 在连接后根据 AE05 特征的 notify / indicate 属性选择数据通道;不是按 advertised 属性决定。
| 命令字节 | 长度 | 作用 |
|---|---|---|
0x55 AE 00 00 |
4B | 查询设置(中继盒立即回一个 12B) |
0x55 AB 00 [00\|01] |
4B | 切换中继盒物理单位(0=F / 1=C) |
0x55 AD 00 00 |
4B | 静音报警 |
0x55 B1 |
2B | 独占锁刷新 —— 每 45 秒一次 |
0x55 [AF\|B0\|B2] [unit] [temp] [MAC 6B] |
10B | SET_TARGET(设置目标温度,CM1/CM2/CM3 全部接受) |
颜色命令字节映射(SET_TARGET 的第二字节):
| 颜色字节 | 探针类型 | 命令字节 |
|---|---|---|
| 0x0A / 0x0B | 黑针(长 / 短) | 0x55AF |
| 0x0D | 白针 | 0x55B0 |
| 0x0E | 蓝针 | 0x55B2(注意:不是 0x55B1) |
| 0x0F | 黄针(理论上) | legacy 物理上从无 yellow probe,probeCommandByte(probe4) fallback 返 0xB2 但 unreachable |
⚠️ 常见坑:蓝针命令是
0x55B2,不要误写成0x55B1。0x55B1是独占锁刷新命令,撞了会让心跳被中继盒当成蓝针温度设置。详见 v4.2 §二 · 蓝针命令字节。
CM4 用 ASCII 字符串而非裸字节,由 Cm4BoosterTransport + CM4Command(lib/core/protocol/cm4_protocol.dart,208 行)派发:
| 命令 | 作用 |
|---|---|
CNT_0 |
独占锁刷新(每 45 秒,60 s TTL,per Liang 2026-06-06;与 legacy 0x55B1 同机制) |
SET_RD |
查询所有设置;中继盒回 SETT==<32hex> |
SET_F / SET_C |
中继盒物理屏单位切换(全局,不带探针参数) |
RING_OFF |
静音 |
SET_<color>=ABCDEF |
设置目标温度 + 肉种 + 熟度(A=报警开关(0=关闭、1=启用), B=单位, CD=温度两位 hex, E=肉种 1-9, F=熟度 1-5) |
BL_LVL=N / RI_LVL=N |
背光 / 音量(1-3) |
SSID=... / PSWD=... |
WiFi 凭据下发(PSWD 后中继盒重启) |
USUS=N |
server region:0=CN / 1=US / 2=EU |
OTA |
进入 OTA 升级模式 |
SET_H=XY / SET_L=XY / CLK=XY |
per-probe 高/低温报警 + 计时器开关(X 是 slot 0-3,注意与 SET_<color> 的色名 prefix 不对称) |
<color> 当前为 SET_BL / SET_WH / SET_BU / SET_YE(per cloud doc 2026-05-19,6e5dfb7 反向恢复——本周内已 flip-flop 两次,详见 CM4 协议 §SET 前缀历史)。同设备相邻 AE03 写之间至少 ≥200 ms(per Beta 2026-05-11 8b47f3e)。
完整命令格式见 CM4 协议 和 lib/core/protocol/cm4_protocol.dart。
legacy_data_parser.dart 429 行)| 包类型 | 长度 | 频率 | 关键字段 |
|---|---|---|---|
| 设置响应 | 12B | 每 3s | 电量、固件、单位、目标 ADC、报警状态、压缩 MAC 4B |
| 探针温度 | 15B | 每 3–6s(探针活跃时) | 电量、固件、内温 ADC、环境温 ADC、探针电量/固件、完整 MAC 6B、RSSI |
| 入仓通知 | 2 / 3 / 8B | 事件驱动 | 2B = 老款全局;3B = 早期;8B = 新款含探针地址 |
| 心跳 | 2B | 每 3s(无探针活跃时) | 仅电量 + 固件 |
cm4_data_parser.dart 343 行)| 形式 | 触发 | 作用 |
|---|---|---|
D=<30hex>\r\n |
每根活跃探针 ~3s 一帧(不合并) | 探针遥测;解码后 = 同一 15-byte 布局 |
PENON <id> / PENOFF <id> |
探针上线 / 下线 | id 仅接受数字(1..4),传入色名会被 parser 丢弃 |
SETT==<32hex>\r\n |
物理按键改设置 → unsolicited push(BLE + MQTT 双轨);亦是 SET_RD 响应 |
16-byte 解码,per-probe |
WIFI_STS=N |
WiFi 信号强度(0=断开, 1-4=信号格) | parser 解析为 CM4WifiStatus(含 RSSI) |
双通道遥测(per Beta 2026-05-11 / 55d699a):CM4 中继盒在 BLE 与 MQTT 都可达时同时往两个通道推遥测,固件没有切换开关——"fallback" 是 App-side 的 DeviceConnectionManager._mode 过滤,每次只消费一边。详见 MQTT 与云端 §双通道遥测。
Per Liang 2026-04-28:中继盒电量字节有两种编码——legacy 模式下整字节就是 0–10 等级;插上充电器后高 nibble 变 8、低 nibble 是等级(例 0x82 = 充电中 20 %、0x8A = 充电中 100 %)。temperature_lookup.dart 的 decodeBoosterBattery(legacy_data_parser / cm4_data_parser 解析共享)把两种编码归一到同一 0–10 scale,并把 isCharging 标志单独导出供 UI 渲染充电图标。该字节出现在 12B 设置响应、15B 探针温度(首字节)、2B 心跳的电量位上;CM4 的 D=hex byte 1 / SETT== byte 0 同样语义。
| 长度 | 机型 | 格式 |
|---|---|---|
| 2B | MW2 老款 | 55 AA(全局信号) |
| 3B | 早期版本 | 55 AA XX |
| 8B | CM1/CM2/CM3/MW3 新款 | 55 AA + 6B 探针 MAC |
App 用 6 字节 MAC 匹配具体哪根针入仓了。CM4 走 PENOFF <id> ASCII,不走 0x55AA 二进制。
这是 v4.1 发现的 bug 修复点,容易写错:
| 数据位置 | 字节序 |
|---|---|
| 15B 包的 ADC(字节 3-4 / 5-6) | 大端 |
| 12B 包的报警目标 ADC(字节 4-5) | 小端 |
| CM4 SETT== 的报警目标(字节 4-5) | 小端 |
| 15B 包的探针地址(字节 9-14) | 按收到的 6 字节顺序直接使用(代码未反转) |
| 12B 包的压缩地址(字节 9-12) | 还原后是正向 MAC(和 15B 反向相反) |
| SET_TARGET 命令的 MAC | 直接使用最新数据包中的 6 字节地址顺序(NOT reversed) |
交叉验证:App 每次收到 12B 时对比重构 MAC 与 15B 反转 MAC。不一致打 SETTINGS_MAC_MISMATCH 警告。保留此校验,这是当年发现字节序 bug 的关键手段。
详见 v4.2 §三、字节序。
两张 LUT(lib/core/protocol/temperature_lookup.dart,199 行——2026-05 从 cm4_data_parser.dart 抽出,legacy / CM4 共享):
| 表名 | 覆盖范围 | 条目数 | 用途 |
|---|---|---|---|
tempIntArray |
0–120 °C | 121 | 探针内温(烹饪温度) |
tempExtArray |
0–274 °C | 275 | 探针环境温(烤箱/烧烤温度) |
超出范围:
AlarmType.internalUnderTemp 单声+通知AlarmType.internalOverTemp 触发全屏 WarningPage(per Liang 2026-05-07 ≥ 不是 >)internalHiClampC),但 WarningPage 全屏读数显示真实温度internalHardClampC)AlarmType.ambientUnderTempAlarmType.ambientOverTemp 全屏报警 + 钳位显示Per Liang 2026-05-01 v5 review:原 v4 §15 "钳到 101 °C" 的设计被发现把真实 102–120 °C 读数压成一个数字,已改为「101 = UI 显示 HI 的阈值;120 = parser 硬钳;WarningPage 显示原值」三段式。常量见 alarm_service.dart 的 AlarmThresholds.{internalHiClampC, internalHiClampF, internalHardClampC, internalHardClampF, internalMaxDisplayF}。
最关键的一点:0x55B1 不是心跳,是独占锁命令。目的:防多手机同时控制同一中继盒。CM4 没有这套机制——CM4 的连接互斥由固件 60 s 空闲自断兜底,详见 CM4 协议 §心跳。
| 步骤 | 行为 |
|---|---|
手机 A 连上 + 发 0x55B1 |
中继盒锁定到 A,停止广播 |
| A 每 45 秒刷新 | 锁 TTL = 60s,留 15s margin |
| A 闪退 / 被杀 | 60 秒后锁释放,中继盒重新广播 |
手机 B 连上并发 0x55B1 |
B 得锁,A 断 |
| 用户主动在 App 里断开 | 直接断 BLE,不用发释放锁命令 |
Per Liang 2026-04-30 audit reply(多设备 + SET_TARGET 三件套):
(a) 锁刷新相位错峰 —— 多台中继盒短时间内连上时,第一次周期性 0x55B1 / CNT_0 写入按 (deviceId.hashCode % hbInterval×1000) ms 错开(ble_service.dart:1354),使 7 台设备的 45 秒刷新写不会撞在同一墙钟时刻,避免 Xiaomi/Huawei 浅 GATT 写队列(深度 4)丢写而触发 60 秒 TTL 失锁——Liang 原话「不同的中继盒,按顺序发 0x55B1 就好,中继盒已经保留了微小的容错机制」。DeviceConnection.heartbeatStartTimer 一次性触发,再 arm 现有 heartbeatTimer。
(b) 连接单飞门 (_connectGate) —— BleService.connect() / directConnect() 通过 chained-Future 串行化连接窗口(ble_service.dart:351),避免 7 台已知设备启动时并发抢 CCCD 写 / MTU 协商 / indicate 订阅;已连接快速路径仍在门外,事件 CONNECT_GATE_ENTER / CONNECT_GATE_EXIT 用于诊断。
(c) CM1/CM2/CM3 SET_TARGET 短路 —— 2026-04-30 final clarification 已撤回:Liang 当日下午澄清此前「SET_TARGET 是 CM4 的指令,不是 CM1/CM2/CM3 的指令」是指 CM4 ASCII SET_TARGET= 命令名(CM4 独有),而 CM1/CM2/CM3 固件仍然接受这个 10 字节 0x55 wire format(V4 §2.2 的指令、shipping 几个月、TAPD #5 ALARM_SYNC_ADOPT 双向 round-trip 都依赖它)。因此 BleDeviceService._isLegacyNoSetTarget helper + setLegacyTarget 里的 SET_TARGET_SKIP_NOT_SUPPORTED 早返路径已删除。legacy_protocol.dart 的 setTarget doc-comment 也改写到反映 Liang 真实意图,避免下一个 agent 再误读。cookingSession 本地态 + _persistProbeTarget SharedPreferences 缓存路径与之前一致,未受影响。