设备恢复出厂后重新添加到 Apple Home,串口停在“BLE connected”,家庭 App 一直转圈,最后提示无法添加配件。

只有设备日志时,很容易把注意力集中在蓝牙:是不是 MTU 没协商好?GATT 特征没有被发现?手机没有订阅 indication?

但一次手机与设备的日志对照,给出了不同的解释:BLE 连接和服务发现已经成功,手机却仍在向恢复出厂前的 Thread 地址尝试 PASE。蓝牙被找到了,不等于本轮配网已经选择蓝牙。

本文是匿名化故障分析。时间改为相对时间,地址、服务实例和设备身份使用占位符,不提供原始诊断或工厂凭据。结论仅针对这次捕获到的失败链路,不代表所有 Apple Home 配网失败同源,也不表示固件修复已经实机验证。

一、先分清:蓝牙连上了,究竟完成了什么?

Matter over Thread 的首次配网常见于 BLE,但“扫描二维码”“建立 BLE 连接”和“通过 BLE 完成配网”是三个不同的动作。

层次 解决什么问题 不能据此认定什么
BLE GAP 连接 手机与设备建立蓝牙链路 不等于 Matter 传输通道已建立
ATT/GATT 协商 MTU,发现服务与特征 找到特征,不等于已经向特征写入配网数据
CHIPoBLE / BTP 用 GATT 承载 Matter 的传输协议,包括能力协商、分片等 不等于已通过配网码认证
PASE 根据配网凭据建立安全会话 不等于已加入 Thread 或完成 Fabric 配置
后续 commissioning 设备认证、配置 Fabric、必要时配置网络、完成配网 前一个步骤成功不代表最终成功

在 Matter BLE 通道里,手机通过 C1 向设备写入,设备通过 C2 indication 返回;BTP 能力协商与相应订阅完成后,才具备继续承载 Matter 消息的传输条件。可结合公开的 BLEEndPoint 实现 阅读这些边界。

本次手机日志已经确认:连接调用者是 com.apple.homed,远端 MTU 为 247,Matter 服务 0xFFF6 及其特征发现成功。但没有看到这一轮继续建立 BLE BTP/PASE 的证据。

仅凭“没有看到”仍不够归因;真正有价值的是,手机日志同时记录了它正在另一条通道上做什么

二、恢复出厂后的配网,为什么会找旧 Thread 地址?

PASE 不只支持 BLE,也可以运行在 IP 网络上。已联网设备开放配网窗口、加入另一个 Fabric 时,就可以通过已有 IP 网络完成配网。

本次控制器同时启动了两种发现:

1
2
3
开始添加配件
├─ BLE 扫描、连接、服务发现
└─ DNS-SD 查找局域网中的可配网设备

网络发现先返回了一条与 discriminator 匹配的记录,手机随即向记录中的地址启动 UDP PASE。几秒后 BLE 才完成准备。

这不是“手机先连 BLE,让网关测一下旧 Thread 是否在线,再决定怎么配网”。发起 PASE 的是手机上的控制器;发出的也不是普通在线探测,而是实际的 PASE 请求。 边界路由器负责 IP 网络间的可达与转发,并不因此成为这次 PASE 的发起者。

公开的 SetUpCodePairer 实现 提供了与日志一致的机制解释:存在进行中的 PASE 时,ConnectToDiscoveredDevice() 会等待当前尝试结束;后发现的 BLE 可以进入候选队首,但不会立即抢占当前 UDP 尝试。

这是公开代码与日志的对照,不是对 Apple 私有系统版本的逐行源码确认,也不能把“哪个先返回就总选哪个”推广为所有控制器的固定策略。

三、开放配网窗口时,发布的不是 Thread 密码

设设备仍在 Thread 网络中,主机名是 device-host.local,地址记为 OLD_THREAD_IP。开放网络配网窗口,相当于发布:

我现在允许配网。通过这个主机和端口可以联系我,这些是我的设备筛选信息与通信参数。

DNS-SD 用一组记录表达这件事:

记录 作用 脱敏示意
PTR 与子类型索引 查找可配网服务实例 _matterc._udp,按 discriminator 筛选
SRV 服务对应的主机和端口 device-host.local:5540
AAAA 主机对应的 IPv6 OLD_THREAD_IP
TXT 设备提示、配网状态、通信参数 VPDCMSIISAISAT

其中,VP 是厂商和产品 ID,D 是 discriminator,CM 表示配网模式,SII/SAI/SAT 是可靠消息重传所需的相关参数;还可能包含轮换标识 RI 和配对提示 PH

这些不是 Thread Network Key、Operational Dataset 或明文配网码。 开窗也不意味着更换 IPv6:主机与地址可能早已存在,变化的是配网服务是否开放及其描述信息。

Thread 终端通常通过 SRP 注册服务,再由边界路由器的广告代理向 Wi-Fi 或以太网侧发布。这里是本地服务发现,不是把设备信息发给互联网公共 DNS。OpenThread 服务发现说明

四、把成功与失败分开,才能看清时序

同一份 sysdiagnose 里包含多轮操作。前两轮已有 CommissioningComplete errorCode=0 和家庭侧完成事件;真正的失败发生在后来的恢复出厂与立即重新添加。

以下以新一轮手机发现启动为 T=0,时间做了取整:

相对时间 观察到的事件
T−14 秒左右 同一次长按达到 5 秒,设备在已配网状态下开放 DNS-SD-only 配网窗口,SRP 更新成功
T−13 秒左右 设备收到远端删除一个 Fabric 的请求
T−9 秒左右 持续长按达到 10 秒,安排恢复出厂
T−7 秒左右 删除最后一个 Fabric 时,同一个配网服务又被发布,随后 SRP 更新成功
T−4 秒左右 擦除 Thread 信息并重启;启动状态显示无 Fabric、无 Thread 配置、未附着
T+0 秒 手机 DNS-SD 返回同一个配网服务和旧地址,立即发送 UDP PBKDFParamRequest
T+2~4 秒 BLE 建链、MTU 协商、Matter 服务发现成功
T+5、26、64 秒左右 同一 Exchange ID、同一 Message Counter 的 UDP PASE 请求重传
T+102 秒左右 家庭显示无法添加,配网任务以超时/取消结束

最关键的关联不是“错误都发生在同一分钟”,而是:

  1. 手机解析到的服务实例与设备重置前发布的实例一致;
  2. 解析出的 IPv6 是设备重置前使用的地址;
  3. 后续重传属于同一个 PASE 交换,而不是另一条旧订阅或 OTA 会话;
  4. BLE 就绪以后,旧 UDP 交换仍在继续。

错误收尾阶段的 Cancelled 或“用户取消”标签,不足以证明用户主动取消;也不能单凭最终的 Timeout 判定最先到期的是哪个上层计时器。

五、记录可能残留在哪里?

1
2
3
4
5
6
7
8
9
10
设备服务状态
│ SRP 注册、更新、撤销

SRP 服务器与广告代理
│ 在局域网发布或撤销发现记录

手机 mDNSResponder 的解析与缓存
│ 向调用方返回服务与地址

手机配网任务保存的目标、重传参数与 PASE 状态

这几层的生命周期不是同步的。

  • 设备清空本地 Flash,不等于 SRP 服务器已经收到撤销。
  • SRP 服务撤销成功,不等于所有手机缓存都已经完成失效处理。
  • 手机不再发现某个服务,也不保证已经启动的 PASE 会立即取消。

本次手机解析器返回 SRV、TXT、AAAA 时,均标记为 expired: no。这只能证明记录在解析器眼里尚未过期,不能证明它刚确认过设备在线,也不能单凭这一点判断返回数据来自现有缓存还是新的网络应答。

SRP 注册租约、mDNS 记录 TTL、设备本地配网窗口和 PASE 重试计时是不同概念。不能把“配网窗口十分钟”直接当作所有旧记录的保留时间。SRP 租约与 TTL 接口说明

因此,当前证据能确认“旧发现结果进入了本轮配网”,但不足以指定某一台 HomePod 或某一个缓存为唯一责任点。

六、怎样改更合理:松手判定,还是重置前撤销?

这两个方向解决的问题不同,不是二选一。

方向 A:长按动作互斥,释放后才执行较短档位

例如一个产品把 5 秒定义为开窗、10 秒定义为恢复出厂,可以设计为:

  • 按住期间到 5 秒,不立即开放配网窗口;
  • 按住满 5 秒但不到 10 秒后释放,才执行开窗;
  • 达到恢复出厂档位,则只执行恢复出厂,不在经过短档位时先开窗。

这减少了“为了恢复出厂,却先发布了一次配网公告”的机会。但它只约束这个按键入口,不能清理此前已有的记录,也不能防止其他回调重新发布服务。

本次最后一个 Fabric 删除时还有服务更新,因此不能宣称只改松手判定就已根治。

方向 B:重置前把服务撤销做成有结果的流程

这比单纯延迟重启更直接。但首先应检查 SDK 是否已经提供这一步,而不是重复添加同一 API。

OpenThread 的两个接口尤其容易混淆:

  • otSrpClientClearHostAndServices():只清本地状态,不与服务器交互。
  • otSrpClientRemoveHostAndServices():发起撤销过程,结果由异步回调报告。

调用返回成功,只说明请求开始,不是远端已经清除记录的证明;也不能立即释放撤销过程中仍需使用的服务对象。OpenThread SRP API 契约

可审查的目标顺序是:

1
2
3
4
锁定“准备恢复出厂”,阻止普通开窗和重新发布
→ 保留旧 Thread 网络与注册身份,发起服务撤销
→ 等待明确完成,或到达有界截止时间
→ 清理本地 Fabric/网络存储、停止协议栈、重启

这是设计方向,不是可不加检查地照搬的代码顺序。要特别核对 Fabric 删除回调、服务刷新回调、Thread 停栈与多个重置入口,确保不会“刚撤销又重新发布”,也不会阻塞负责处理撤销应答的线程。

边界路由器的广告代理还需把撤销传播到局域网。mDNS 为失效记录定义了 TTL 为零的 goodbye,但接收方仍有缓存处理过程;设备无法凭一个 SRP 应答保证所有第三方控制器已忘记旧地址。RFC 6762 §10.1

断网或网关掉电时也必须允许恢复出厂完成,因此不能无限等待。超时应明确记录为“撤销未确认”,而不是当成清理成功。

我的取舍

先确认现有撤销路径的调用顺序、完成条件和超时结果,再决定怎样加固;按键松手判定作为减少不必要公告的互斥措施。不要在证据不足时直接把 BLE MTU、C3 或 PASE 超时当成修复入口。

七、这次诊断真正改变了什么?

原先只有“设备没收到后续蓝牙配网”的单端现象。手机日志补上了另一半:控制器没有闲着,它一直在旧 IP 路径上进行另一条配网尝试。

排查类似问题时,价值最高的不是收集更多孤立错误码,而是逐层确认:

1
发现了谁 → 选了哪条通道 → 发了哪个交换 → 收到了什么 → 何时回退或终止

本次统一日志使用跨平台工具离线解析,存在部分解码警告,因此没有将缺失日志单独当作协议未发生的证明。配对实例、地址、交换编号、重传及 UART 重置状态的交叉印证,才是结论基础。

关于没有 Mac 时怎样取得可分析的材料,可接着阅读日志获取指南;关于已配网设备为何能走网络配网,可阅读DNS-SD、BCM 与 ECM 的区别;BLE 层次见连接与 GATT 基础