<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Oniums</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://oniums.github.io/</id>
  <link href="https://oniums.github.io/" rel="alternate"/>
  <link href="https://oniums.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Oniums</rights>
  <subtitle>Oniums Lab · 嵌入式工程技术实验室</subtitle>
  <title>Oniums</title>
  <updated>2026-08-19T07:00:17.470Z</updated>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Matter" scheme="https://oniums.github.io/tags/matter/"/>
    <category term="Thread" scheme="https://oniums.github.io/tags/thread/"/>
    <category term="Commissioning" scheme="https://oniums.github.io/tags/commissioning/"/>
    <category term="Bluetooth LE" scheme="https://oniums.github.io/tags/bluetooth-le/"/>
    <category term="Multi-Admin" scheme="https://oniums.github.io/tags/multi-admin/"/>
    <category term="DNS-SD" scheme="https://oniums.github.io/tags/dns-sd/"/>
    <content>
      <![CDATA[<p>一台 Matter over Thread 设备已经加入生态 A。现在让它重新进入配网状态，再用生态 B 的 App 扫描机身二维码，设备没有重新发出 Bluetooth LE 配网广播，为什么仍然可能成功加入第二个 Fabric？</p><p>这个问题最容易产生两个误解：</p><ol><li>扫描二维码就等于接下来一定使用 BLE；</li><li>BLE 对应 Basic Commissioning，DNS-SD 对应 Enhanced Commissioning。</li></ol><p>两种理解都不准确。<strong>BLE 与 DNS-SD 解决“怎样发现和连接设备”，BCM 与 ECM 解决“配网窗口使用哪一种 Passcode”。它们是两个不同维度。</strong></p><span id="more"></span><p>本文从一台已接入 Thread 网络的传感器出发，说明首次配网、BCM 二次配网和 ECM 二次配网的差别，以及 Thread Border Router、手机、Hub 和二维码各自在流程中承担什么角色。</p><p>本文只讨论“把已配网设备加入新的 Matter Fabric”。Matter Network Recovery、厂商私有 BLE OTA、产测和维护服务不在本文范围内，它们可能有独立的 BLE 行为。</p><h2 id="一句话结论"><a href="#一句话结论" class="headerlink" title="一句话结论"></a>一句话结论</h2><p>对于已经加入至少一个 Matter Fabric、并已连接 Thread 或其他 IP 网络的设备：</p><ul><li>后续 Fabric 的配网发现应通过现有 IP 网络上的 DNS-SD；</li><li>已配网设备不应重新通过 BLE 发布普通 commissioning announcement；</li><li>BCM 使用设备原始 Onboarding Payload，例如机身二维码或原手动码；</li><li>ECM 使用现有管理员临时生成的新 Passcode；</li><li>BCM 和 ECM 都可以通过 IP 建立 PASE，二维码本身不决定底层通道；</li><li>新增 Matter Fabric 不等于切换 Thread 网络。</li></ul><p>可以把三个常见场景先压缩成下面这张表：</p><table><thead><tr><th>场景</th><th>发现与连接通道</th><th>配对凭据</th><th>结果</th></tr></thead><tbody><tr><td>Factory-new 首次配网</td><td>常见 Thread 设备使用 BLE；也取决于设备支持能力</td><td>机身原二维码或手动码</td><td>加入首个 Fabric，必要时获得 Thread Dataset</td></tr><tr><td>已配网设备打开 Basic 窗口</td><td>现有 IP 网络上的 DNS-SD</td><td>机身原二维码或手动码</td><td>新增一个 Fabric，通常保持原 operational network</td></tr><tr><td>现有管理员打开 Enhanced 窗口</td><td>现有 IP 网络上的 DNS-SD</td><td>新生成的临时码</td><td>新增一个 Fabric，通常保持原 operational network</td></tr></tbody></table><p>这只是三个常见工程场景，不是 Matter 对所有 commissioning flow 的完整分类。Matter Core Specification 还分别定义了 Standard、User-Intent、Custom 等产品交互流程，以及 discovery、commissioning channel、管理员辅助开窗等不同维度。</p><h2 id="先把两个维度彻底分开"><a href="#先把两个维度彻底分开" class="headerlink" title="先把两个维度彻底分开"></a>先把两个维度彻底分开</h2><h3 id="维度一：怎样发现并连接设备"><a href="#维度一：怎样发现并连接设备" class="headerlink" title="维度一：怎样发现并连接设备"></a>维度一：怎样发现并连接设备</h3><p>Matter Commissionable Node Discovery 可以使用多种技术：</p><ul><li>Bluetooth LE；</li><li>已有 IP 网络上的 DNS-SD；</li><li>支持时还可能使用 Wi-Fi Public Action Frame、NFC 等机制。</li></ul><p>这些机制回答的是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">待配网设备在哪里？</span><br><span class="line">怎样从多个候选设备中找到目标？</span><br><span class="line">建立 PASE 前，Commissioner 应连接哪个地址和端口？</span><br></pre></td></tr></table></figure><h3 id="维度二：配网窗口使用哪一种凭据"><a href="#维度二：配网窗口使用哪一种凭据" class="headerlink" title="维度二：配网窗口使用哪一种凭据"></a>维度二：配网窗口使用哪一种凭据</h3><p>BCM 与 ECM 回答的是另一个问题：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">新 Commissioner 应使用原始固定 Passcode，</span><br><span class="line">还是使用现有管理员刚生成的临时 Passcode？</span><br></pre></td></tr></table></figure><table><thead><tr><th>方法</th><th>全称</th><th>Passcode 来源</th><th align="right">DNS-SD 中的 Commissioning Mode</th></tr></thead><tbody><tr><td>BCM</td><td>Basic Commissioning Method</td><td>Commissionee 原始 Onboarding Material</td><td align="right"><code>CM=1</code></td></tr><tr><td>ECM</td><td>Enhanced Commissioning Method</td><td>当前管理员生成的随机临时 Passcode</td><td align="right"><code>CM=2</code></td></tr></tbody></table><p>因此下面两组等式都不成立：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">BLE = BCM</span><br><span class="line">DNS-SD = ECM</span><br></pre></td></tr></table></figure><p>正确关系应该是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">BLE / DNS-SD：发现和建立 commissioning channel 的方式</span><br><span class="line">BCM / ECM：配网窗口和 Passcode 的方法</span><br></pre></td></tr></table></figure><h2 id="场景一：Factory-new-设备为什么常见-BLE"><a href="#场景一：Factory-new-设备为什么常见-BLE" class="headerlink" title="场景一：Factory-new 设备为什么常见 BLE"></a>场景一：Factory-new 设备为什么常见 BLE</h2><p>一台刚恢复出厂的 Matter over Thread 设备还没有 Thread Dataset，也没有可用的 operational IP 路径。对于这类产品，BLE 是很自然的临时 commissioning channel：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">手机扫描二维码</span><br><span class="line">  ↓</span><br><span class="line">解析 Discriminator 和 Setup Passcode</span><br><span class="line">  ↓</span><br><span class="line">发现并连接 BLE Commissionable Advertising</span><br><span class="line">  ↓</span><br><span class="line">CHIPoBLE / BTP</span><br><span class="line">  ↓</span><br><span class="line">PASE</span><br><span class="line">  ↓</span><br><span class="line">设备证明、CSR、AddNOC</span><br><span class="line">  ↓</span><br><span class="line">写入 Thread Dataset 并连接 Thread</span><br><span class="line">  ↓</span><br><span class="line">通过 operational IP 建立 CASE</span><br><span class="line">  ↓</span><br><span class="line">CommissioningComplete</span><br></pre></td></tr></table></figure><p>这里 BLE 的职责是提供临时发现和传输通道。投入运行后，设备的长期身份来自 Fabric、Node ID、NOC 和 CASE，不来自 BLE MAC 地址。</p><p>需要注意，Matter 并没有规定所有首次配网都必须走 BLE。如果设备已经通过其他方式连接到 IP 网络，首次 Matter commissioning 也可以从 DNS-SD&#x2F;IP 开始。</p><h2 id="场景二：已配网设备打开-Basic-窗口"><a href="#场景二：已配网设备打开-Basic-窗口" class="headerlink" title="场景二：已配网设备打开 Basic 窗口"></a>场景二：已配网设备打开 Basic 窗口</h2><p>设备已经拥有 Fabric，并且已经位于 operational Thread&#x2F;IP 网络上。此时如果产品通过按键、菜单或其他本地动作打开 Basic Commissioning Window，典型流程变成：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">用户在设备上触发 Basic 开窗动作</span><br><span class="line">  ↓</span><br><span class="line">设备在现有 IP 网络注册 _matterc._udp</span><br><span class="line">  ↓</span><br><span class="line">DNS-SD TXT 记录表明 CM=1</span><br><span class="line">  ↓</span><br><span class="line">新生态扫描机身原二维码</span><br><span class="line">  ↓</span><br><span class="line">按 Discriminator 匹配 DNS-SD 结果</span><br><span class="line">  ↓</span><br><span class="line">通过 IPv6/UDP 建立 PASE</span><br><span class="line">  ↓</span><br><span class="line">Attestation、CSR、AddNOC</span><br><span class="line">  ↓</span><br><span class="line">新增第二个 Fabric</span><br><span class="line">  ↓</span><br><span class="line">CASE、CommissioningComplete</span><br></pre></td></tr></table></figure><p>这里的“按几秒、按哪个键、灯怎样提示”不是 Matter 统一规定，而是产品交互设计。Matter 规范约束的是：已配网设备如果再次发布 commissioning request，应在 operational network 上使用 DNS-SD，而不是重新发布普通 BLE commissioning announcement。</p><p>从凭据角度看，<code>CM=1</code> 表示新 Commissioner 使用 Commissionee 提供的 Passcode，例如设备标签、包装或屏幕上的原始 Onboarding Material。扫描原二维码并不意味着后续必须走 BLE。</p><h3 id="规范中的-BCM-与物理按键要区分"><a href="#规范中的-BCM-与物理按键要区分" class="headerlink" title="规范中的 BCM 与物理按键要区分"></a>规范中的 BCM 与物理按键要区分</h3><p>Matter Core Specification 的 Administrator Assisted BCM 给出的规范流程，是当前管理员通过 CASE 发送 <code>OpenBasicCommissioningWindow</code>。一些产品和生态还提供物理动作直接打开 Basic 窗口，让用户复用原始二维码。</p><p>BCM 对 Node 和 Administrator&#x2F;Commissioner 是可选方法，因此不能假设每款设备和每个生态都支持原二维码二次配网。</p><p>因此，描述具体产品时应分别给出证据：</p><ul><li>“长按 N 秒”来自产品设计、说明书或 DCL pairing instruction；</li><li>“打开 Basic Commissioning Window”来自设备实现；</li><li>“已配网后使用 DNS-SD&#x2F;IP”来自 Matter 规范；</li><li>“使用原始二维码”来自 BCM&#x2F;<code>CM=1</code> 的凭据语义。</li></ul><p>不能把四项合并后，误写成 Matter 规范强制所有设备“长按固定秒数”。</p><h2 id="场景三：现有管理员打开-Enhanced-窗口"><a href="#场景三：现有管理员打开-Enhanced-窗口" class="headerlink" title="场景三：现有管理员打开 Enhanced 窗口"></a>场景三：现有管理员打开 Enhanced 窗口</h2><p>ECM 是更标准化的 Multi-Admin 分享路径：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">用户在生态 A 中选择“共享 Matter 设备”</span><br><span class="line">  ↓</span><br><span class="line">生态 A 的 Administrator 通过 CASE 访问设备</span><br><span class="line">  ↓</span><br><span class="line">发送 OpenCommissioningWindow</span><br><span class="line">  ↓</span><br><span class="line">生成随机临时 Passcode 和对应 PAKE Verifier</span><br><span class="line">  ↓</span><br><span class="line">设备发布 _matterc._udp，CM=2</span><br><span class="line">  ↓</span><br><span class="line">生态 A 向用户展示临时二维码或 11 位配对码</span><br><span class="line">  ↓</span><br><span class="line">生态 B 使用临时码通过 IP 建立 PASE</span><br><span class="line">  ↓</span><br><span class="line">AddNOC，加入生态 B 的 Fabric</span><br></pre></td></tr></table></figure><p>ECM 的几个关键点是：</p><ul><li>Node 和 Commissioner&#x2F;Administrator 必须实现 ECM；</li><li>使用新的随机 Passcode，不复用设备机身固定 Passcode；</li><li>临时 Passcode 有明确的窗口生命周期；</li><li>当前管理员先通过已有 CASE 会话授权开窗；</li><li>新管理员仍然使用 DNS-SD&#x2F;IP 发现设备并完成后续 commissioning。</li></ul><h2 id="DNS-SD-具体发布什么"><a href="#DNS-SD-具体发布什么" class="headerlink" title="DNS-SD 具体发布什么"></a>DNS-SD 具体发布什么</h2><p>已配网设备打开 commissioning window 后，发布的是 Matter Commissionable Node Discovery 服务：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">_matterc._udp</span><br></pre></td></tr></table></figure><p>DNS-SD 常见记录包括：</p><table><thead><tr><th>DNS 记录</th><th>提供的信息</th><th>用途</th></tr></thead><tbody><tr><td>PTR</td><td><code>_matterc._udp</code> 下有哪些服务实例</td><td>枚举可配网设备</td></tr><tr><td>SRV</td><td>目标主机名和 Matter 端口</td><td>告诉 Commissioner 连接哪里</td></tr><tr><td>AAAA</td><td>设备 IPv6 地址</td><td>建立实际 IP 通信</td></tr><tr><td>TXT</td><td>Discriminator、Commissioning Mode、VID&#x2F;PID 等</td><td>匹配二维码并判断开窗类型</td></tr><tr><td>Subtype PTR</td><td>按 Discriminator、Vendor ID、Device Type 等筛选</td><td>减少无关候选设备</td></tr></tbody></table><p>一组抽象后的记录可能类似：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">PTR  _matterc._udp.local</span><br><span class="line">     → &lt;temporary-instance&gt;._matterc._udp.local</span><br><span class="line"></span><br><span class="line">SRV  &lt;temporary-instance&gt;._matterc._udp.local</span><br><span class="line">     → &lt;device-host&gt;:&lt;matter-port&gt;</span><br><span class="line"></span><br><span class="line">AAAA &lt;device-host&gt;</span><br><span class="line">     → &lt;device-ipv6-address&gt;</span><br><span class="line"></span><br><span class="line">TXT  D=&lt;discriminator&gt;</span><br><span class="line">     CM=1</span><br><span class="line">     VP=&lt;vendor-id&gt;+&lt;product-id&gt;</span><br><span class="line">     DT=&lt;device-type&gt;</span><br></pre></td></tr></table></figure><p>这些都是发现信息。DNS-SD 不应携带：</p><ul><li>Matter Setup Passcode；</li><li>Thread Network Key 或完整 Operational Dataset；</li><li>Fabric Root Key、NOC 私钥或 CASE 会话密钥；</li><li>传感器实时状态或其他业务数据。</li></ul><p>二维码提供 Passcode 和用于匹配的 Discriminator，DNS-SD 提供地址、端口和公开发现元数据。两者组合后，Commissioner 才能找到正确设备并建立 PASE。</p><h2 id="Thread-设备为什么需要-Border-Router-代理"><a href="#Thread-设备为什么需要-Border-Router-代理" class="headerlink" title="Thread 设备为什么需要 Border Router 代理"></a>Thread 设备为什么需要 Border Router 代理</h2><p>Wi-Fi 和 Ethernet 设备可以直接在局域网使用 mDNS。Thread 是低功耗 Mesh，如果把大量局域网 multicast 原样灌入 Thread，会增加空口和电池负担。</p><p>Matter over Thread 因此采用更合适的路径：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Thread 设备</span><br><span class="line">  │ 使用 SRP 注册服务</span><br><span class="line">  ▼</span><br><span class="line">Thread Service Registry</span><br><span class="line">  │ 通常由 Thread Border Router 提供</span><br><span class="line">  ▼</span><br><span class="line">Advertising Proxy</span><br><span class="line">  │ 在相邻 Wi-Fi / Ethernet LAN 发布 DNS-SD</span><br><span class="line">  ▼</span><br><span class="line">手机或新生态的 Commissioner</span><br></pre></td></tr></table></figure><p>这里要分清三个角色：</p><table><thead><tr><th>角色</th><th>职责</th></tr></thead><tbody><tr><td>Matter 设备</td><td>拥有并注册 <code>_matterc._udp</code> 服务</td></tr><tr><td>Thread Border Router</td><td>路由 IPv6，并代理 Thread 侧 DNS-SD 服务</td></tr><tr><td>新 Commissioner</td><td>查询服务、匹配二维码并发起 PASE</td></tr></tbody></table><p>Border Router 的代理机制通常一直存在。设备打开窗口后，新出现的是 <code>_matterc._udp</code> 服务记录；不是原生态收到按键事件后，临时决定“把设备分享出去”。</p><p>Border Router 也不会因此把原 Fabric 的 NOC、密钥或权限交给新生态。它只解决 IP 路由和服务发现，新 Fabric 的信任关系仍由 PASE、设备证明、Operational Credentials 和 <code>AddNOC</code> 建立。</p><h2 id="扫码后的手机会不会先等-BLE-超时"><a href="#扫码后的手机会不会先等-BLE-超时" class="headerlink" title="扫码后的手机会不会先等 BLE 超时"></a>扫码后的手机会不会先等 BLE 超时</h2><p>二维码只是一份 Onboarding Payload。扫码后，Commissioner 获得 Passcode、Discriminator、VID&#x2F;PID 等信息，用于寻找正确设备和建立 PASE。</p><p>Matter 规范要求 Commissioner 支持 DNS-SD commissioning discovery，不应因为二维码中的初始 discovery capability 就忽略 DNS-SD。具体生态可能：</p><ul><li>同时观察 BLE 和 DNS-SD；</li><li>根据上下文选择发现方式；</li><li>把流程交给手机系统服务或家庭 Hub；</li><li>使用不同的扫描超时、缓存和重试策略。</li></ul><p>这些调度细节属于生态实现。对于一个已经关闭 BLE、最终又成功加入第二个 Fabric 的设备，能够确定的是实际 device-facing commissioning channel 使用了 IP；不能仅凭成功结果反推出手机一定经历过“BLE 超时后再回退 DNS-SD”。</p><h2 id="为什么新增-Fabric-通常不需要更换-Thread-网络"><a href="#为什么新增-Fabric-通常不需要更换-Thread-网络" class="headerlink" title="为什么新增 Fabric 通常不需要更换 Thread 网络"></a>为什么新增 Fabric 通常不需要更换 Thread 网络</h2><p>Thread operational network 与 Matter Fabric 是不同层次：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Thread Dataset</span><br><span class="line">→ 解决设备如何接入低功耗 IPv6 Mesh</span><br><span class="line"></span><br><span class="line">Matter Fabric / NOC</span><br><span class="line">→ 解决设备属于哪个管理和信任域</span><br></pre></td></tr></table></figure><p>设备已经连接到可达的 Thread 网络时，第二个 Commissioner 可以经由现有 Border Router 访问设备，并给它安装新的 Fabric credentials。结果可能是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">一张 Thread 网络</span><br><span class="line">  ├── Fabric A</span><br><span class="line">  ├── Fabric B</span><br><span class="line">  └── Fabric C</span><br></pre></td></tr></table></figure><p>这不表示多个生态共享同一套 Matter 密钥。每个 Fabric 都有独立的 NOC、Node ID、ACL 和 CASE 会话；Thread Border Router 只是转发 IPv6 数据，不能因为承担路由角色就解密不同 Fabric 的 Matter 应用消息。</p><h2 id="怎样验证链路到底走了什么"><a href="#怎样验证链路到底走了什么" class="headerlink" title="怎样验证链路到底走了什么"></a>怎样验证链路到底走了什么</h2><h3 id="设备侧"><a href="#设备侧" class="headerlink" title="设备侧"></a>设备侧</h3><p>设备日志可以重点观察：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Commissioning window opened</span><br><span class="line">DNS-SD / _matterc._udp registration</span><br><span class="line">PASE PBKDFParam / Pake1 / Pake2 / Pake3</span><br><span class="line">ArmFailSafe</span><br><span class="line">AttestationRequest</span><br><span class="line">CSRRequest</span><br><span class="line">AddTrustedRootCertificate</span><br><span class="line">AddNOC</span><br><span class="line">CASE Sigma</span><br><span class="line">CommissioningComplete</span><br><span class="line">Fabric committed</span><br></pre></td></tr></table></figure><p>如果已配网设备没有任何 BLE connection 事件，却完成了 PASE 和后续 commissioning，可以证明 device-facing 链路使用了 IP，但不能仅凭设备 UART 还原手机内部是否并行启动过 BLE scan。</p><h3 id="局域网侧"><a href="#局域网侧" class="headerlink" title="局域网侧"></a>局域网侧</h3><p>在支持相应工具的电脑上，可以观察 commissionable service：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dns-sd -B _matterc._udp <span class="built_in">local</span></span><br></pre></td></tr></table></figure><p>Linux 常见工具：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">avahi-browse -rt _matterc._udp</span><br></pre></td></tr></table></figure><p>Wireshark 可以从这些方向筛选：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mdns</span><br><span class="line">dns</span><br><span class="line">udp.port == 5353</span><br></pre></td></tr></table></figure><p>需要注意，Thread 侧本身通常使用 SRP 和 Unicast DNS，由 Border Router 的 Advertising Proxy 在相邻 LAN 上代表设备处理 mDNS。只抓 Thread 空口，不一定能看到与 Wi-Fi LAN 完全相同的 mDNS 报文。</p><h3 id="成功判据"><a href="#成功判据" class="headerlink" title="成功判据"></a>成功判据</h3><p>发现 <code>_matterc._udp</code> 只证明设备可被找到；<code>AddNOC</code> 也只证明新 Fabric credentials 已进入配网事务。完整闭环还应继续确认：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">PASE完成</span><br><span class="line">→ Attestation/CSR完成</span><br><span class="line">→ AddNOC成功</span><br><span class="line">→ operational CASE建立</span><br><span class="line">→ CommissioningComplete成功</span><br><span class="line">→ Fabric提交并在重启后仍存在</span><br><span class="line">→ 新生态建立订阅或正常读写</span><br></pre></td></tr></table></figure><h2 id="Matter-官方文档在哪里描述"><a href="#Matter-官方文档在哪里描述" class="headerlink" title="Matter 官方文档在哪里描述"></a>Matter 官方文档在哪里描述</h2><p>可以从 <a href="https://csa-iot.org/developer-resource/specifications-download-request/">Connectivity Standards Alliance 规范下载页面</a>获取 Matter Core Specification。以 Matter 1.5 为例，相关章节主要是：</p><table><thead><tr><th>章节</th><th>内容</th></tr></thead><tbody><tr><td>§4.3.1</td><td>Commissionable Node Discovery，定义 <code>_matterc._udp</code>、TXT 字段和 <code>CM</code></td></tr><tr><td>§5.4.2.1</td><td>多种 discovery technology 的 announcement</td></tr><tr><td>§5.4.2.2</td><td>已配网节点只通过 operational network 上的 DNS-SD 发布 commissioning request</td></tr><tr><td>§5.4.2.7</td><td>Existing IP-bearing Network，以及 Thread SRP&#x2F;Advertising Proxy</td></tr><tr><td>§5.4.3</td><td>Commissioner 怎样执行 discovery</td></tr><tr><td>§5.5</td><td>PASE、Attestation、AddNOC、CASE、CommissioningComplete 主流程</td></tr><tr><td>§5.6.2</td><td>Basic Commissioning Method，原 Onboarding Payload 与 IP discovery</td></tr><tr><td>§5.6.3</td><td>Enhanced Commissioning Method，临时 Passcode 与 DNS-SD</td></tr></tbody></table><p>生态实现参考还可以阅读：</p><ul><li><a href="https://developers.home.google.com/matter/primer/commissionable-and-operational-discovery">Google：Commissionable and Operational Discovery</a></li><li><a href="https://developer.amazon.com/docs/alexaplus/smarthome/best-practices-commission-matter.html">Amazon：Best Practices to Commission Matter Devices with Alexa</a></li></ul><p>Google 的说明特别解释了 Thread 设备如何通过 SRP 和 Border Router Advertising Proxy 对外提供 DNS-SD；Amazon 的说明则把 BCM 原始二维码、ECM 临时码和 <code>CM=1</code>&#x2F;<code>CM=2</code> 的用户路径进行了对照。</p><h2 id="最容易出现的六个误解"><a href="#最容易出现的六个误解" class="headerlink" title="最容易出现的六个误解"></a>最容易出现的六个误解</h2><h3 id="误解一：扫码就一定走-BLE"><a href="#误解一：扫码就一定走-BLE" class="headerlink" title="误解一：扫码就一定走 BLE"></a>误解一：扫码就一定走 BLE</h3><p>二维码提供凭据和匹配信息，不负责选择无线承载。PASE 可以通过 BLE commissioning channel，也可以通过现有 IP 网络。</p><h3 id="误解二：BLE-就是-BCM，DNS-SD-就是-ECM"><a href="#误解二：BLE-就是-BCM，DNS-SD-就是-ECM" class="headerlink" title="误解二：BLE 就是 BCM，DNS-SD 就是 ECM"></a>误解二：BLE 就是 BCM，DNS-SD 就是 ECM</h3><p>BLE&#x2F;DNS-SD 是 discovery&#x2F;channel；BCM&#x2F;ECM 是 Passcode&#x2F;window，两者是正交维度。</p><h3 id="误解三：Border-Router-是原-Fabric-的“分享服务器”"><a href="#误解三：Border-Router-是原-Fabric-的“分享服务器”" class="headerlink" title="误解三：Border Router 是原 Fabric 的“分享服务器”"></a>误解三：Border Router 是原 Fabric 的“分享服务器”</h3><p>Border Router 代理 DNS-SD 并路由 IPv6，不代表它把原 Fabric credentials 分享给新生态。</p><h3 id="误解四：新增-Fabric-就要加入新的-Thread-网络"><a href="#误解四：新增-Fabric-就要加入新的-Thread-网络" class="headerlink" title="误解四：新增 Fabric 就要加入新的 Thread 网络"></a>误解四：新增 Fabric 就要加入新的 Thread 网络</h3><p>Fabric 与 Thread operational network 不在同一层。Multi-Admin 可以在同一张 Thread 网络上增加多个独立 Fabric。</p><h3 id="误解五：看到-AddNOC-就代表配网成功"><a href="#误解五：看到-AddNOC-就代表配网成功" class="headerlink" title="误解五：看到 AddNOC 就代表配网成功"></a>误解五：看到 AddNOC 就代表配网成功</h3><p>还需要 CASE、<code>CommissioningComplete</code>、Fabric commit，以及需要时的订阅和业务交互证据。</p><h3 id="误解六：Matter-规定所有产品长按固定秒数打开-BCM"><a href="#误解六：Matter-规定所有产品长按固定秒数打开-BCM" class="headerlink" title="误解六：Matter 规定所有产品长按固定秒数打开 BCM"></a>误解六：Matter 规定所有产品长按固定秒数打开 BCM</h3><p>按键和时长是产品交互定义。Matter 规定的是 commissioning window、discovery、安全会话和凭据语义。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>理解 Matter Multi-Admin 的关键，不是记住某一家 App 的页面顺序，而是始终分开三个问题：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">怎样找到设备？</span><br><span class="line">→ BLE、DNS-SD、其他 discovery technology</span><br><span class="line"></span><br><span class="line">使用什么临时信任凭据？</span><br><span class="line">→ BCM 原始 Passcode，或 ECM 临时 Passcode</span><br><span class="line"></span><br><span class="line">最终建立什么长期关系？</span><br><span class="line">→ AddNOC，把设备加入一个新的 Matter Fabric</span><br></pre></td></tr></table></figure><p>对于已连接 Thread 网络的设备，打开二次配网窗口后出现的核心变化，是设备开始注册 <code>_matterc._udp</code> commissionable service。Thread Border Router 将这项服务代理到家庭 LAN，新 Commissioner 用二维码或临时码匹配设备，通过 IP 建立 PASE，最后安装新的 Fabric credentials。</p><p>**二维码不是 BLE 的同义词，DNS-SD 不是 ECM 的同义词，Thread 网络也不是 Matter Fabric。**把这三组概念分开，跨生态二次配网的大部分现象就能解释清楚。</p>]]>
    </content>
    <id>https://oniums.github.io/posts/matter-secondary-commissioning-bcm-ecm-dns-sd/</id>
    <link href="https://oniums.github.io/posts/matter-secondary-commissioning-bcm-ecm-dns-sd/"/>
    <published>2026-08-19T02:00:00.000Z</published>
    <summary>
      <![CDATA[<p>一台 Matter over Thread 设备已经加入生态 A。现在让它重新进入配网状态，再用生态 B 的 App 扫描机身二维码，设备没有重新发出 Bluetooth LE 配网广播，为什么仍然可能成功加入第二个 Fabric？</p>
<p>这个问题最容易产生两个误解：</p>
<ol>
<li>扫描二维码就等于接下来一定使用 BLE；</li>
<li>BLE 对应 Basic Commissioning，DNS-SD 对应 Enhanced Commissioning。</li>
</ol>
<p>两种理解都不准确。<strong>BLE 与 DNS-SD 解决“怎样发现和连接设备”，BCM 与 ECM 解决“配网窗口使用哪一种 Passcode”。它们是两个不同维度。</strong></p>]]>
    </summary>
    <title>Matter 已配网设备为什么不再用 BLE？一次讲清 DNS-SD、BCM 与 ECM</title>
    <updated>2026-08-19T07:00:17.470Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Matter" scheme="https://oniums.github.io/tags/matter/"/>
    <category term="Commissioning" scheme="https://oniums.github.io/tags/commissioning/"/>
    <category term="Bluetooth LE" scheme="https://oniums.github.io/tags/bluetooth-le/"/>
    <category term="Privacy" scheme="https://oniums.github.io/tags/privacy/"/>
    <category term="Random Static Address" scheme="https://oniums.github.io/tags/random-static-address/"/>
    <content>
      <![CDATA[<p>调试 Matter over Thread 设备时，可能会遇到一个很容易被误判的现象：设备恢复出厂后可以被手机扫描到，但重启一次，扫描工具显示的 BLE MAC 地址就变了。</p><p>这是不是地址没有保存？会不会导致手机找不到原来的设备？量产时是不是应该给每台设备烧录一个固定 BLE MAC？</p><p>对于 Matter 配网广播，答案恰好相反：<strong>每次启动都使用新的随机 BLE 地址，通常才是符合规范的行为。</strong></p><span id="more"></span><p>不过，“随机地址”不等于“每次连接都换地址”，也不等于常见的 RPA 定时轮换地址。本文把规范要求、Bluetooth 地址类型、主流参考实现和产品测试方法放到同一条时间线上说明。</p><h2 id="一句话结论"><a href="#一句话结论" class="headerlink" title="一句话结论"></a>一句话结论</h2><p>对于使用 Bluetooth LE 进行发现和配网的 Matter 待配网设备：</p><ul><li>BLE 广播必须使用 <strong>LE Random Device Address</strong>；</li><li>具体使用 Bluetooth 定义的 <strong>Random Static Address</strong>；</li><li>地址必须至少在每次设备启动时更换；</li><li>同一次启动期间，配网重试、断开重连或重新开始广播，不要求再次换地址；</li><li>永久固定的 Public Address，或者跨重启保持不变的“伪随机地址”，都不满足这项 Matter 配网要求。</li></ul><p>因此，更准确的描述不是“每次配网随机一次”，而是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">每次启动生成一个新的 Random Static Address</span><br><span class="line">              ↓</span><br><span class="line">本次启动期间保持稳定</span><br><span class="line">              ↓</span><br><span class="line">下一次启动重新生成</span><br></pre></td></tr></table></figure><h2 id="Matter-规范到底要求了什么？"><a href="#Matter-规范到底要求了什么？" class="headerlink" title="Matter 规范到底要求了什么？"></a>Matter 规范到底要求了什么？</h2><p><a href="https://csa-iot.org/wp-content/uploads/2026/06/23-27349-011_Matter-1.6-Core-Specification.pdf">Matter 1.6 Core Specification</a> 第 5.4.2.5.5 节“Advertising Address”（PDF 第 319 页）给出了两个 <code>SHALL</code> 级要求：</p><ol><li>待配网设备的 BLE 广播使用 LE Random Device Address；</li><li>地址至少在每次启动时更换。</li></ol><p>这里的 <code>SHALL</code> 表示强制要求，不是优化建议。该条款还明确引用 Bluetooth Core Specification 中的 Static Device Address 定义，所以不能只看到“Random Device Address”，就把它理解成任意一种随机地址。</p><p>这项要求属于 Matter 使用 BLE 进行 Commissionable Node Discovery 和配网的场景。Matter over Thread 产品经常采用这条路径，但它并非 Thread 独有：只要 Matter 设备使用 BLE 作为配网通道，就要关注这项要求。</p><h2 id="为什么“Random-Static”既随机又静态？"><a href="#为什么“Random-Static”既随机又静态？" class="headerlink" title="为什么“Random Static”既随机又静态？"></a>为什么“Random Static”既随机又静态？</h2><p>这个名字第一次看很矛盾，其实两个词描述的是不同维度：</p><ul><li><strong>Random</strong>：地址不是永久分配的 Public Address，而是随机生成的 Random Device Address；</li><li><strong>Static</strong>：地址初始化后，在当前启动或电源周期中保持稳定，不进行定时轮换。</li></ul><p>按照 <a href="https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core_v6.3/out/en/low-energy-controller/link-layer-specification.html">Bluetooth Core Specification 的 Device Address 定义</a>，Random Device Address 还可以细分为三类：</p><table><thead><tr><th>地址类型</th><th align="right">地址高两位 <code>[47:46]</code></th><th>生命周期特点</th><th>Matter 配网广播是否指定使用</th></tr></thead><tbody><tr><td>Random Static Address</td><td align="right"><code>11</code></td><td>初始化时随机生成，当前电源周期保持不变</td><td>是</td></tr><tr><td>Resolvable Private Address，RPA</td><td align="right"><code>01</code></td><td>由 IRK 和随机数生成，可周期轮换并被授权设备解析</td><td>否</td></tr><tr><td>Non-resolvable Private Address，NRPA</td><td align="right"><code>00</code></td><td>无法通过 IRK 解析，通常用于更短期的隐私场景</td><td>否</td></tr></tbody></table><p>Public Device Address 则是另一大类，通常来自固定的设备身份分配，不属于 Random Device Address。</p><p>因此，看到扫描工具显示 <code>Random</code> 还不够。要判断 Matter 要求是否真正满足，至少还要确认：</p><ol><li>地址类型确实是 Random Static；</li><li>地址位 <code>[47:46]</code> 为 <code>11</code>；</li><li>当前启动期间地址稳定；</li><li>下一次启动后地址发生变化。</li></ol><h2 id="“每次启动更换”不等于“每次连接更换”"><a href="#“每次启动更换”不等于“每次连接更换”" class="headerlink" title="“每次启动更换”不等于“每次连接更换”"></a>“每次启动更换”不等于“每次连接更换”</h2><p>把地址变更粒度搞错，可能造成两种相反的问题。</p><p>如果地址永远固定，附近观察者就更容易跨时间关联同一台待配网设备，违背 Matter 这里的隐私目标。</p><p>如果地址在同一次启动中频繁变化，手机刚扫描到地址 A，准备连接时设备却已经变成地址 B，也会增加发现、连接和故障恢复的复杂度。Random Static Address 的“Static”正是为了避免这种不稳定。</p><p>常见场景可以这样判断：</p><table><thead><tr><th>场景</th><th>地址是否应变化</th></tr></thead><tbody><tr><td>手机第一次扫描到设备</td><td>使用本次启动生成的地址</td></tr><tr><td>配网连接失败，手机重新扫描和连接</td><td>通常不变</td></tr><tr><td>设备停止后重新开始 Matter 广播，但没有重新启动</td><td>通常不变</td></tr><tr><td>设备重新启动，再次进入待配网状态</td><td>必须换成新的随机地址</td></tr><tr><td>恢复出厂并重新启动</td><td>必须换成新的随机地址</td></tr><tr><td>从 Flash 读取上次保存的 Random Static Address</td><td>跨启动不变，不符合该项要求</td></tr><tr><td>由芯片 UID 确定性计算地址</td><td>即使格式是 Random Static，跨启动不变仍不符合要求</td></tr></tbody></table><h2 id="为什么-Matter-不把-BLE-MAC-当作永久设备身份？"><a href="#为什么-Matter-不把-BLE-MAC-当作永久设备身份？" class="headerlink" title="为什么 Matter 不把 BLE MAC 当作永久设备身份？"></a>为什么 Matter 不把 BLE MAC 当作永久设备身份？</h2><p>BLE 在这里主要解决的是“附近发现并建立临时配网通道”。它不是 Matter 设备投入运行后的长期身份基础。</p><p>典型的 Matter over Thread 首次配网可以简化为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">二维码或手动码</span><br><span class="line">  ↓</span><br><span class="line">通过 Discriminator 缩小待配网设备范围</span><br><span class="line">  ↓</span><br><span class="line">BLE 广播、连接和 BTP 传输</span><br><span class="line">  ↓</span><br><span class="line">PASE 建立临时安全会话</span><br><span class="line">  ↓</span><br><span class="line">设备证明、NOC/Fabric 配置、Thread Dataset</span><br><span class="line">  ↓</span><br><span class="line">设备接入 Thread</span><br><span class="line">  ↓</span><br><span class="line">通过 IPv6 建立 CASE</span><br><span class="line">  ↓</span><br><span class="line">CommissioningComplete</span><br></pre></td></tr></table></figure><p>进入正常运行阶段后，Controller 通过 Matter Fabric、Node ID、证书和 CASE 识别并访问设备，通信承载变成 Thread 上的 IPv6。BLE 配网地址不需要承担永久设备索引的职责。</p><p>这也解释了为什么 Commissioner 或手机 App 不应把 BLE MAC 当作 Matter 设备的长期主键。设备重启后，App 应重新扫描待配网广播，并使用 Onboarding Payload、Discriminator 和 Matter Service Data 完成目标匹配。</p><p>还要分清下面几种地址和身份：</p><table><thead><tr><th>名称</th><th>所属层次</th><th>是否受本文这条要求约束</th></tr></thead><tbody><tr><td>待配网设备的 BLE Advertising Address</td><td>Bluetooth LE</td><td>是</td></tr><tr><td>手机或 Commissioner 自己的 BLE 地址</td><td>Bluetooth LE</td><td>不是这条设备侧要求的对象</td></tr><tr><td>Thread Extended Address &#x2F; EUI-64</td><td>IEEE 802.15.4 &#x2F; Thread</td><td>否</td></tr><tr><td>Matter Node ID、Fabric ID、NOC</td><td>Matter</td><td>否</td></tr><tr><td>厂商私有 Device ID</td><td>产品应用协议</td><td>否，但不能替代 Matter 身份</td></tr></tbody></table><h2 id="主流实现通常怎样做？"><a href="#主流实现通常怎样做？" class="headerlink" title="主流实现通常怎样做？"></a>主流实现通常怎样做？</h2><p>没有公开、可信的全市场统计可以证明“多少品牌随机、多少品牌固定”。比市场印象更可靠的证据，是 Matter 开源参考实现和芯片平台适配层。</p><p>在 Project CHIP 当前源码中，可以看到多种平台都围绕“启动时生成 Random Static Address”实现：</p><ul><li><a href="https://github.com/project-chip/connectedhomeip/blob/0f267927e02ce234ec75a7a4970104a73bcc06dc/src/platform/Zephyr/BLEManagerImpl.cpp">Zephyr 平台 BLEManagerImpl</a> 创建 Random Static Identity；</li><li><a href="https://github.com/project-chip/connectedhomeip/blob/0f267927e02ce234ec75a7a4970104a73bcc06dc/src/platform/ESP32/nimble/BLEManagerImpl.cpp">ESP32 NimBLE BLEManagerImpl</a> 生成并设置新的 Static Random Address；</li><li><a href="https://github.com/project-chip/connectedhomeip/blob/0f267927e02ce234ec75a7a4970104a73bcc06dc/src/platform/silabs/efr32/BLEManagerImpl.cpp">Silicon Labs EFR32 BLEManagerImpl</a> 特别处理了“同一次启动内重新初始化 BLE 不能误换地址”；</li><li><a href="https://github.com/project-chip/connectedhomeip/blob/0f267927e02ce234ec75a7a4970104a73bcc06dc/src/platform/bouffalolab/common/BLEManagerImpl.cpp">Bouffalo Lab BLEManagerImpl</a> 直接按 once per boot 生成 Random Static Address。</li></ul><p>这不能代替对每一款量产固件的检查，但足以说明：<strong>主流 Matter SDK 的设计方向是每次启动随机，而不是永久固定。</strong> 很多厂商直接继承芯片平台或 Matter SDK 的默认行为，因此最终产品通常也会表现为重启后 BLE 地址变化。</p><p>如果某台产品一直显示固定地址，可能存在多种原因：</p><ul><li>使用了旧版 SDK 或厂商自己的 BLE 平台层；</li><li>把 Public Address 直接用于 Matter 广播；</li><li>把 Random Static Address 持久化到了 Flash；</li><li>扫描工具显示的是系统解析后的身份，而不是当前空口地址；</li><li>当前看到的是厂商私有 BLE 服务，不是 Matter Commissionable Advertising。</li></ul><p>不能仅凭“地址固定”立即断言整台产品不合格，但它足以触发一次针对实际 Matter 广播的合规检查。</p><h2 id="厂商私有-BLE-OTA-为什么可能需要稳定地址？"><a href="#厂商私有-BLE-OTA-为什么可能需要稳定地址？" class="headerlink" title="厂商私有 BLE OTA 为什么可能需要稳定地址？"></a>厂商私有 BLE OTA 为什么可能需要稳定地址？</h2><p>有些设备除了 Matter 配网，还提供厂商私有 BLE OTA、产测或维护服务。手机 App 可能希望设备重启后仍能被识别，于是产品会设计一个稳定的 BLE Identity。</p><p>这和 Matter 配网地址的目标不同：</p><ul><li>Matter 配网强调临时发现与隐私，要求每次启动更换地址；</li><li>厂商私有服务可能强调重连和设备索引，希望身份稳定。</li></ul><p>工程上不能因为私有 OTA 需要稳定地址，就直接让 Matter 配网广播永久使用同一个 MAC。也不能为了满足 Matter 地址轮换，就让依赖固定 MAC 的私有 App 在升级后失去设备。</p><p>可选设计要结合控制器能力和隐私模型评估，例如：</p><ul><li>为 Matter 配网和私有服务使用不同的 BLE Identity 或 Advertising Set；</li><li>让私有 App 每次重新扫描、重新发现 GATT，而不是缓存旧地址和 Handle；</li><li>在受保护的应用协议中确认设备身份，不把可被长期跟踪的永久标识直接放进明文广播；</li><li>明确规定两种模式何时启用，避免同一个广播同时承担冲突的身份目标。</li></ul><p>这里没有一种适合所有芯片和手机系统的固定答案，但有一个边界很明确：<strong>厂商私有 BLE 需求不能取消 Matter Commissionable Advertising 的规范义务。</strong></p><h2 id="怎样在设备上验证？"><a href="#怎样在设备上验证？" class="headerlink" title="怎样在设备上验证？"></a>怎样在设备上验证？</h2><p>最小验证不需要先做完整 Matter 配网。使用能够显示 BLE 地址类型和原始广播数据的扫描工具，记录多次启动即可。</p><p>建议至少执行下面这组测试：</p><table><thead><tr><th>测试</th><th>操作</th><th>预期结果</th></tr></thead><tbody><tr><td>地址类型</td><td>检查 Matter <code>0xFFF6</code> Service Data 对应广播的地址类型</td><td>Random Static</td></tr><tr><td>位格式</td><td>检查地址位 <code>[47:46]</code></td><td><code>11</code></td></tr><tr><td>启动内稳定性</td><td>同一次启动中停止&#x2F;恢复广播、断开并重新扫描</td><td>地址不变</td></tr><tr><td>跨启动随机性</td><td>连续重启设备至少 5 次</td><td>每次地址都不同</td></tr><tr><td>配网匹配</td><td>每次重启后重新扫码和扫描</td><td>Commissioner 仍能选中正确设备</td></tr><tr><td>完整闭环</td><td>完成 PASE、Thread Attach、CASE 和 CommissioningComplete</td><td>地址变化不影响完整配网</td></tr></tbody></table><p>测试记录应同时保存：</p><ul><li>启动序号；</li><li>广播地址和地址类型；</li><li>Matter Service Data；</li><li>Discriminator；</li><li>是否成功建立 BLE 连接和 BTP；</li><li>最终是否到达 CommissioningComplete。</li></ul><p>只看到 MAC 变化，不能证明配网成功；只看到 BLE Connected，也不能证明 PASE、Thread 或 CASE 成功。地址合规和端到端配网是两条相关但独立的证据链。</p><h2 id="实现检查清单"><a href="#实现检查清单" class="headerlink" title="实现检查清单"></a>实现检查清单</h2><p>如果正在开发 Matter 设备，可以用下面的清单快速审查平台层：</p><ul><li><input disabled="" type="checkbox"> 使用密码学安全随机源生成 48-bit 地址的随机部分；</li><li><input disabled="" type="checkbox"> 设置 Random Static Address 规定的高两位；</li><li><input disabled="" type="checkbox"> 排除规范禁止的全 0 或全 1 随机部分；</li><li><input disabled="" type="checkbox"> 在 BLE 广播启动前完成地址初始化；</li><li><input disabled="" type="checkbox"> 同一次启动中不因配网重试或 BLE 子系统重复初始化而换地址；</li><li><input disabled="" type="checkbox"> 不从 Flash 恢复上一次启动的 Matter 配网地址；</li><li><input disabled="" type="checkbox"> 不使用芯片 UID、序列号或量产固定地址确定性生成跨启动地址；</li><li><input disabled="" type="checkbox"> App 不把 BLE MAC 或旧 GATT Handle 当作永久设备身份；</li><li><input disabled="" type="checkbox"> 把 Matter 配网 BLE、厂商私有 BLE 和 Thread 地址分别测试；</li><li><input disabled="" type="checkbox"> 用真实设备记录跨启动地址，而不是只检查配置项或源码。</li></ul><h2 id="最后再记住三个边界"><a href="#最后再记住三个边界" class="headerlink" title="最后再记住三个边界"></a>最后再记住三个边界</h2><ol><li><strong>随机地址是强制要求，不只是隐私建议。</strong></li><li><strong>随机粒度是每次启动，不是每次连接。</strong></li><li><strong>BLE 配网地址不是 Thread 地址，也不是 Matter 的长期设备身份。</strong></li></ol><p>如果调试时发现“设备重启后 BLE MAC 变了”，先不要把它当成数据丢失或 Flash 故障。对于 Matter 配网设备，这往往正是规范要求平台层主动实现的行为。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>Connectivity Standards Alliance, <a href="https://csa-iot.org/wp-content/uploads/2026/06/23-27349-011_Matter-1.6-Core-Specification.pdf">Matter 1.6 Core Specification</a>, §5.4.2.5.5, Advertising Address.</li><li>Bluetooth SIG, <a href="https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core_v6.3/out/en/low-energy-controller/link-layer-specification.html">Bluetooth Core Specification 6.3, Vol 6, Part B, §1.3.2</a>, Random Device Address.</li><li>Project CHIP, <a href="https://github.com/project-chip/connectedhomeip/tree/0f267927e02ce234ec75a7a4970104a73bcc06dc">connectedhomeip</a>, BLE platform implementations.</li></ul>]]>
    </content>
    <id>https://oniums.github.io/posts/matter-ble-random-static-address-per-boot/</id>
    <link href="https://oniums.github.io/posts/matter-ble-random-static-address-per-boot/"/>
    <published>2026-08-17T06:35:00.000Z</published>
    <summary>
      <![CDATA[<p>调试 Matter over Thread 设备时，可能会遇到一个很容易被误判的现象：设备恢复出厂后可以被手机扫描到，但重启一次，扫描工具显示的 BLE MAC 地址就变了。</p>
<p>这是不是地址没有保存？会不会导致手机找不到原来的设备？量产时是不是应该给每台设备烧录一个固定 BLE MAC？</p>
<p>对于 Matter 配网广播，答案恰好相反：<strong>每次启动都使用新的随机 BLE 地址，通常才是符合规范的行为。</strong></p>]]>
    </summary>
    <title>Matter BLE 配网的 MAC 地址为什么每次重启都会变？</title>
    <updated>2026-08-17T06:35:50.963Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Matter" scheme="https://oniums.github.io/tags/matter/"/>
    <category term="Thread" scheme="https://oniums.github.io/tags/thread/"/>
    <category term="Commissioning" scheme="https://oniums.github.io/tags/commissioning/"/>
    <category term="Echo Dot Max" scheme="https://oniums.github.io/tags/echo-dot-max/"/>
    <category term="HomePod mini" scheme="https://oniums.github.io/tags/homepod-mini/"/>
    <category term="Google Nest Hub Max 2" scheme="https://oniums.github.io/tags/google-nest-hub-max-2/"/>
    <category term="SmartThings Hub V3" scheme="https://oniums.github.io/tags/smartthings-hub-v3/"/>
    <content>
      <![CDATA[<p>同一台 Matter over Thread 设备，分别通过 Echo Dot Max、HomePod mini、Google Nest Hub Max 2 和 SmartThings Hub V3 配网时，设备日志为什么不完全一样？</p><p>实测中，HomePod mini、Google Nest Hub Max 2 和 SmartThings Hub V3 在写入 Matter 运行凭据后，直接把 Thread Operational Dataset 下发给设备；Echo Dot Max 则先让设备查询附近的 Thread 网络，再决定下一步。</p><p>这是否意味着 Echo Dot Max 固定会先扫描，而另外三台中枢永远不扫描？扫描发生在中枢还是终端？结果又是怎样返回的？</p><span id="more"></span><p>先给出本文从多份脱敏现场日志中得到的结论：</p><blockquote><p>四台中枢遵循的是同一条 Matter commissioning 主干。给定设备和软件组合的日志中，HomePod mini、Google Nest Hub Max 2 与 SmartThings Hub V3 直接下发已知 Thread Dataset；Echo Dot Max 先请求终端扫描 Thread 网络，并通过当前 BLE&#x2F;PASE 会话取得扫描结果。</p></blockquote><p>这是一组具体设备的现场实现证据，不是厂商平台永远不变的公开承诺。同一型号在不同 App、固件、账户状态、Border Router 组合下也可能选择不同分支。</p><p>现场也观察到 Echo Dot Max 能够成功加入的情况，但目前没有对应的完整成功日志。因此，本文中的 Echo Dot Max 序列只代表当前失败样本，不能写成该型号只有扫描路径或必然失败。</p><p>本文只公开四台消费级中枢的名称，不公开待配终端的具体产品型号、原始日志、设备版本、网络名称、地址、Fabric&#x2F;Node 标识、证书、密钥、Thread Dataset 或内部构建信息。</p><h2 id="先别急着叫它“网关”"><a href="#先别急着叫它“网关”" class="headerlink" title="先别急着叫它“网关”"></a>先别急着叫它“网关”</h2><p>一个家庭中枢可能把多个角色装在同一个盒子里，但分析协议时必须把它们拆开。</p><table><thead><tr><th>角色</th><th>主要职责</th><th>不一定是谁</th></tr></thead><tbody><tr><td>Matter Commissioner</td><td>为新设备执行配网，发送 PASE、凭据和 Network Commissioning 命令</td><td>不一定是 Border Router 本体</td></tr><tr><td>Matter Controller</td><td>配网后读取、控制和订阅设备</td><td>不一定是最初的 Commissioner</td></tr><tr><td>Thread Border Router</td><td>在 Thread Mesh 与家庭其他 IP 网络之间路由数据</td><td>不负责定义 Matter Fabric 身份</td></tr><tr><td>Commissionee</td><td>正在等待配网的新设备</td><td>执行实际 Thread Attach 的一方</td></tr></tbody></table><p>手机、音箱、家庭中枢和云端服务可能共同完成 Commissioner 工作；家庭中枢也可能同时是 Controller 和 Thread Border Router。</p><p>所以，“网关让设备扫网”这句话在口语上可以理解，但更准确的说法是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Commissioner 通过 Matter 命令请求扫描</span><br><span class="line">    -&gt; 待配设备使用本地 802.15.4 Radio 执行或读取扫描结果</span><br><span class="line">    -&gt; 待配设备通过当前 Matter 会话返回结果</span><br><span class="line">    -&gt; 该会话在常见首次配网中承载于 BLE</span><br></pre></td></tr></table></figure><p>真正扫描无线信道的是待配设备，不是 BLE 本身。BLE 只是 Commissioner 与设备交换 Matter commissioning 消息的临时承载。</p><h2 id="四台中枢共有的配网骨架"><a href="#四台中枢共有的配网骨架" class="headerlink" title="四台中枢共有的配网骨架"></a>四台中枢共有的配网骨架</h2><p>去掉实现细节后，一次典型的 Matter over Thread 首次配网可以分成下面几段。</p><table><thead><tr><th>阶段</th><th>主要动作</th><th>成功到这里还不能证明什么</th></tr></thead><tbody><tr><td>BLE&#x2F;BTP</td><td>手机或中枢发现并连接设备</td><td>还没有建立 Matter 安全会话</td></tr><tr><td>PASE</td><td>使用 Setup Passcode 建立临时安全通道</td><td>还没有证明设备身份，也没有加入 Fabric</td></tr><tr><td>Device Attestation</td><td>检查 DAC、PAI、CD 等证明材料</td><td>还没有写入长期运行身份</td></tr><tr><td>Operational Credentials</td><td>CSR、Trusted Root、<code>AddNOC</code></td><td><code>AddNOC</code> 只创建 pending Fabric，不代表配网完成</td></tr><tr><td>Network Commissioning</td><td>扫描、下发 Dataset、连接 Thread 网络</td><td>保存 Dataset 不等于已经 Attach</td></tr><tr><td>Thread Attach</td><td>设备成为 Child&#x2F;Router，进入 IPv6 Mesh</td><td>还不等于 Matter CASE 已成功</td></tr><tr><td>CASE</td><td>使用 NOC 建立长期 Matter 安全会话</td><td>仍需完成 commissioning 收尾</td></tr><tr><td><code>CommissioningComplete</code></td><td>提交 Fabric、解除 Fail-safe</td><td>还不自动证明长期订阅和中枢 App 在线</td></tr></tbody></table><p>四台中枢的实测路径差异不是“有没有 Matter 安全流程”，而是 Commissioner 在 Network Commissioning 阶段怎样取得和选择 Thread 网络。</p><h2 id="Network-Commissioning-有两条常见路径"><a href="#Network-Commissioning-有两条常见路径" class="headerlink" title="Network Commissioning 有两条常见路径"></a>Network Commissioning 有两条常见路径</h2><h3 id="路径-A：先让设备扫描-Thread-网络"><a href="#路径-A：先让设备扫描-Thread-网络" class="headerlink" title="路径 A：先让设备扫描 Thread 网络"></a>路径 A：先让设备扫描 Thread 网络</h3><p>Commissioner 可以向设备发送 <code>ScanNetworks</code>。设备扫描附近的 Thread 网络后，返回网络描述信息，例如信道、PAN 相关标识、RSSI 和网络名称等。</p><p>简化时序如下：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Commissioner</span><br><span class="line">    -&gt; ScanNetworks</span><br><span class="line"></span><br><span class="line">待配设备</span><br><span class="line">    -&gt; 本地 802.15.4 扫描</span><br><span class="line">    -&gt; ScanNetworksResponse</span><br><span class="line"></span><br><span class="line">Commissioner</span><br><span class="line">    -&gt; 根据扫描结果选择网络</span><br><span class="line">    -&gt; AddOrUpdateThreadNetwork（下发 Dataset）</span><br><span class="line">    -&gt; ConnectNetwork</span><br></pre></td></tr></table></figure><p>扫描结果不是 Thread Network Key。它帮助 Commissioner 了解设备所在位置能看到哪些 Thread 网络；真正加入网络仍需要 Commissioner 提供相应的 Active Operational Dataset。</p><p>这条路径适合 Commissioner 需要确认现场可见网络、处理多张 Thread Mesh，或者其配网策略本来就要求先查询设备视角的情况。</p><h3 id="路径-B：直接下发已知-Dataset"><a href="#路径-B：直接下发已知-Dataset" class="headerlink" title="路径 B：直接下发已知 Dataset"></a>路径 B：直接下发已知 Dataset</h3><p>如果中枢已经通过自己的 Border Router、凭据存储或家庭状态知道目标 Thread 网络，就不一定需要先让设备扫描。</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">中枢已经持有目标 Thread Operational Dataset</span><br><span class="line">    -&gt; AddOrUpdateThreadNetwork</span><br><span class="line">    -&gt; ConnectNetwork</span><br><span class="line"></span><br><span class="line">待配设备</span><br><span class="line">    -&gt; 保存 Dataset</span><br><span class="line">    -&gt; 从 BLE 切换到 Thread</span><br><span class="line">    -&gt; Attach 到目标 Mesh</span><br></pre></td></tr></table></figure><p>这不是“设备没有扫描能力”，也不是协议少做了一步，而是 Commissioner 已经掌握选择网络所需的信息。</p><h2 id="四台具体中枢的日志展示了什么？"><a href="#四台具体中枢的日志展示了什么？" class="headerlink" title="四台具体中枢的日志展示了什么？"></a>四台具体中枢的日志展示了什么？</h2><p>将终端和网络身份全部脱敏后，四台中枢对应日志的 Network Commissioning 段可以压缩为下表。</p><table><thead><tr><th>观察项</th><th>Echo Dot Max 当前失败样本</th><th>HomePod mini 实测</th><th>Google Nest Hub Max 2 实测</th><th>SmartThings Hub V3 实测</th></tr></thead><tbody><tr><td>PASE 与设备证明</td><td>完成</td><td>完成</td><td>完成</td><td>完成</td></tr><tr><td><code>AddNOC</code></td><td>成功</td><td>成功</td><td>成功尝试中完成</td><td>成功</td></tr><tr><td><code>AddNOC</code> 后的网络动作</td><td>连续请求扫描 Thread 网络</td><td>直接下发 Thread Dataset</td><td>直接下发 Thread Dataset</td><td>多次 Read、延长 Fail-safe 后直接下发 Thread Dataset</td></tr><tr><td><code>ConnectNetwork</code></td><td>因设备实现故障未到达</td><td>观察到</td><td>观察到</td><td>观察到</td></tr><tr><td>Thread Attach</td><td>未到达</td><td>成为 Child</td><td>成为 Child</td><td>成为 Child</td></tr><tr><td>CASE</td><td>未到达</td><td>完成</td><td>完成</td><td>完成</td></tr><tr><td>订阅闭环</td><td>未到达</td><td>本文未单独核定</td><td>短观察窗内未看到新订阅</td><td><code>SubscribeRequest -&gt; ReportData -&gt; SubscribeResponse</code> 完成</td></tr><tr><td><code>CommissioningComplete</code></td><td>当前失败样本未到达；另有成功现象待日志确认</td><td>完成</td><td>完成</td><td>完成</td></tr></tbody></table><p>HomePod mini、Google Nest Hub Max 2 和 SmartThings Hub V3 的 Dataset&#x2F;Connect 命令名称，是根据消息长度及其后紧邻的 Thread 网络状态生效、BLE 断开和 Thread Attach 得出的高可信映射。UART 没有直接打印 Cluster&#x2F;Command Path；如需逐命令字节级确认，仍需 BLE Matter TLV 或 pcap 解码。</p><h3 id="Echo-Dot-Max-当前失败样本：先查询终端能看到什么"><a href="#Echo-Dot-Max-当前失败样本：先查询终端能看到什么" class="headerlink" title="Echo Dot Max 当前失败样本：先查询终端能看到什么"></a>Echo Dot Max 当前失败样本：先查询终端能看到什么</h3><p>样本中，Echo Dot Max 对应的 Commissioner 在 <code>AddNOC</code> 后连续发出两次大小和时序都一致的 Network Commissioning 请求。结合终端响应路径，可以确认它正在查询 Thread 扫描结果。</p><p>第一次查询返回了空结果，随后 Commissioner 立即再次查询。第二次请求暴露了设备平台的扫描缓存生命周期问题，设备在编码空扫描结果时发生重启，因此后续 Dataset、<code>ConnectNetwork</code>、Thread Attach、CASE 和 <code>CommissioningComplete</code> 都没有发生。</p><p>这里最重要的判断不是“Echo Dot Max 多发了一条命令”，而是：</p><blockquote><p>重复 <code>ScanNetworks</code> 和零扫描结果都是终端实现必须安全处理的输入。中枢的重试策略可以暴露固件缺陷，但不应导致终端崩溃。</p></blockquote><p>由于缺少 Echo Dot Max 的 Commissioner 侧日志，我们只能观察到它在第一次空响应后重试，不能证明这台中枢为什么选择立即重试。</p><h4 id="为什么-Echo-Dot-Max-也可能成功？"><a href="#为什么-Echo-Dot-Max-也可能成功？" class="headerlink" title="为什么 Echo Dot Max 也可能成功？"></a>为什么 Echo Dot Max 也可能成功？</h4><p>现场另有成功加入的现象，说明不能把上述失败序列固化为 Echo Dot Max 的唯一流程。当前至少存在几种可能：</p><ol><li>中枢已经取得目标 Thread Dataset，成功时直接下发 Dataset，没有请求扫描；</li><li>中枢仍先扫描，但只请求一次，或者第一次为空后改用已知 Dataset，没有触发第二次空缓存；</li><li>请求发生时终端扫描缓存和射频状态不同，得到的是新的、未被消费的结果；</li><li>成功场景是已有 Thread 网络上的 Multi-Admin，而不是恢复出厂后的首次 BLE-to-Thread 配网。</li></ol><p>这些都是待验证分支。要确认 Echo Dot Max 是否会根据现场状态选择不同加网方式，至少需要一份成功日志，并检查：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">AddNOC</span><br><span class="line">  -&gt; 是否出现 ScanNetworks</span><br><span class="line">  -&gt; 是否直接添加 Thread Dataset</span><br><span class="line">  -&gt; ConnectNetwork</span><br><span class="line">  -&gt; Thread CHILD / SRP</span><br><span class="line">  -&gt; CASE</span><br><span class="line">  -&gt; CommissioningComplete</span><br></pre></td></tr></table></figure><p>还应记录成功时设备是否恢复出厂、是否使用 Multi-Admin 临时码、中枢是否已经保存目标 Thread 凭据，以及 App 是否出现网络选择或 Network Key 页面。没有这些信息，只能确认“有成功和失败两种结果”，不能确认其内部路径为何不同。</p><h3 id="HomePod-mini：直接提供目标-Thread-网络"><a href="#HomePod-mini：直接提供目标-Thread-网络" class="headerlink" title="HomePod mini：直接提供目标 Thread 网络"></a>HomePod mini：直接提供目标 Thread 网络</h3><p>HomePod mini 样本在 <code>AddNOC</code> 后直接进入 Dataset 添加&#x2F;更新与 <code>ConnectNetwork</code>。终端随后断开 BLE、切换到 Thread、成为 Child、完成 SRP 和 CASE，最后收到 <code>CommissioningComplete</code>。</p><p>这说明在该次配网中，HomePod mini 对应的 Commissioner 已经知道要让终端加入哪张 Thread 网络，不需要先从终端取得扫描列表。</p><p>同一份日志后面还出现了第二个 Matter Fabric，但没有再次下发 Thread Dataset。这也说明：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">增加 Matter Fabric</span><br><span class="line">    !=</span><br><span class="line">重新加入一张 Thread 网络</span><br></pre></td></tr></table></figure><p>一个已经在线的设备可以在同一张 Thread Mesh 上承载多个 Matter Fabric 的 CASE 会话。</p><h3 id="Google-Nest-Hub-Max-2：失败重试与扫描问题是两回事"><a href="#Google-Nest-Hub-Max-2：失败重试与扫描问题是两回事" class="headerlink" title="Google Nest Hub Max 2：失败重试与扫描问题是两回事"></a>Google Nest Hub Max 2：失败重试与扫描问题是两回事</h3><p>Google Nest Hub Max 2 样本包含两次尝试。</p><p>第一次已经建立 PASE，并完成了部分凭据流程；随后 Commissioner 主动把 Fail-safe 调整为 1 秒，设备在一秒后按协议清理本次未完成状态。原来的长 Fail-safe 尚未自然耗尽，因此这不能归因于设备本地超时，也与 Thread 扫描崩溃不同。</p><p>第二次尝试继续完成 <code>AddNOC</code>，随后直接下发 Thread Dataset 并连接。设备成为 Child、完成 SRP、CASE、Read&#x2F;ReportData 和 <code>CommissioningComplete</code>。</p><p>日志在成功后只覆盖了较短时间，没有看到新的 <code>SubscribeRequest</code>。这只能说明“观察窗口内没有看到订阅”，不能写成“Google Nest Hub Max 2 永远不会订阅”或“终端一定离线”。</p><h3 id="SmartThings-Hub-V3：直接下发-Dataset，并在收尾前建立订阅"><a href="#SmartThings-Hub-V3：直接下发-Dataset，并在收尾前建立订阅" class="headerlink" title="SmartThings Hub V3：直接下发 Dataset，并在收尾前建立订阅"></a>SmartThings Hub V3：直接下发 Dataset，并在收尾前建立订阅</h3><p>SmartThings Hub V3 样本也没有请求终端扫描 Thread 网络。它在 <code>AddNOC</code> 成功后先执行多次属性读取，并把 Fail-safe 从最初的 240 秒延长到 270 秒，随后直接下发 Thread Dataset，再执行 <code>ConnectNetwork</code>。</p><p>终端之后按下面的顺序完成切换：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">BLE 断开</span><br><span class="line">  -&gt; 切换到 Thread Radio</span><br><span class="line">  -&gt; Thread Role: CHILD</span><br><span class="line">  -&gt; SRP update succeeded</span><br><span class="line">  -&gt; CASE Session established</span><br><span class="line">  -&gt; SubscribeRequest</span><br><span class="line">  -&gt; 初始 ReportData</span><br><span class="line">  -&gt; SubscribeResponse</span><br><span class="line">  -&gt; CommissioningComplete</span><br><span class="line">  -&gt; Fabric commit / Fail-safe disarm</span><br></pre></td></tr></table></figure><p>这份日志的一个特点是，SmartThings Hub V3 在发送 <code>CommissioningComplete</code> 前已经建立首个订阅。配网提交后，中枢还继续执行多次 Read、Write 和新的 Subscribe，说明终端不仅加入 Thread Mesh，也已经进入 Matter 业务交互阶段。</p><p>它进一步缩小了 Echo Dot Max 故障的范围：同一终端能够正常完成 PASE、证书、Dataset 保存、Thread Attach、CASE、订阅与 Fabric commit；崩溃集中在 Echo Dot Max 实测触发的扫描响应路径。</p><p>这些顺序只代表本次 SmartThings Hub V3 与当时软件组合，不能外推为该型号所有版本都必然在 <code>CommissioningComplete</code> 前订阅。</p><h2 id="为什么同一标准允许这些差异？"><a href="#为什么同一标准允许这些差异？" class="headerlink" title="为什么同一标准允许这些差异？"></a>为什么同一标准允许这些差异？</h2><p>Matter 规定互操作命令、数据模型和安全结果，但 Commissioner 仍然需要做策略选择。</p><h3 id="1-中枢是否已经持有-Thread-凭据"><a href="#1-中枢是否已经持有-Thread-凭据" class="headerlink" title="1. 中枢是否已经持有 Thread 凭据"></a>1. 中枢是否已经持有 Thread 凭据</h3><p>如果中枢及其配套服务管理着自己的 Border Router 和 Thread credential store，就可能已经知道目标 Dataset，可以直接下发。</p><p>如果中枢面对多张 Thread 网络、跨厂商 Border Router，或者凭据选择尚未完成，就可能先查询终端能看到哪些网络。</p><h3 id="2-Commissioner-与-Border-Router-是否在同一体系"><a href="#2-Commissioner-与-Border-Router-是否在同一体系" class="headerlink" title="2. Commissioner 与 Border Router 是否在同一体系"></a>2. Commissioner 与 Border Router 是否在同一体系</h3><p>Commissioner 和 Border Router 即使装在同一台中枢里，也属于不同角色。它们之间怎样同步网络状态和凭据，是具体中枢及配套软件实现的一部分。</p><p>跨平台或多 Border Router 家庭里，网络名称相同也不代表 Dataset 相同。Commissioner 需要避免把旧凭据或另一张同名网络的凭据发给设备。</p><h3 id="3-中枢怎样处理空结果和重试"><a href="#3-中枢怎样处理空结果和重试" class="headerlink" title="3. 中枢怎样处理空结果和重试"></a>3. 中枢怎样处理空结果和重试</h3><p>一次扫描可能因为射频时序、环境、缓存状态或信道覆盖返回空结果。Commissioner 可以重试、延迟、换用已知 Dataset，或者让用户重新选择。</p><p>策略可以不同，但设备对重复请求的协议行为必须有定义：返回新的扫描结果、返回一致的缓存，或者返回明确状态，而不是崩溃。</p><h3 id="4-设备平台是否能同时运行-BLE-与-Thread-Radio"><a href="#4-设备平台是否能同时运行-BLE-与-Thread-Radio" class="headerlink" title="4. 设备平台是否能同时运行 BLE 与 Thread Radio"></a>4. 设备平台是否能同时运行 BLE 与 Thread Radio</h3><p>部分芯片使用同一套射频资源承载 BLE 和 IEEE 802.15.4，无法在 BLE 配网会话期间直接执行完整 Thread 扫描。</p><p>一种常见实现是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">启动 BLE 前先扫描 Thread</span><br><span class="line">    -&gt; 缓存扫描结果</span><br><span class="line">    -&gt; 开启 BLE 配网</span><br><span class="line">    -&gt; 收到 ScanNetworks 时返回缓存</span><br></pre></td></tr></table></figure><p>这可以满足单射频约束，但缓存必须支持正确的所有权、重复读取、失效和空结果语义。</p><h2 id="一个容易被具体调用顺序触发的固件陷阱"><a href="#一个容易被具体调用顺序触发的固件陷阱" class="headerlink" title="一个容易被具体调用顺序触发的固件陷阱"></a>一个容易被具体调用顺序触发的固件陷阱</h2><p>在某个单射频嵌入式平台上，启动扫描结果被保存在一个全局、带游标的 iterator 中。产品自己的工厂检查先通过 <code>Next()</code> 读完这个 iterator，Matter Network Commissioning 随后又复用同一个对象。</p><p>状态变化可以抽象为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">启动扫描完成：总数=N，游标=0</span><br><span class="line">    -&gt; 产品逻辑读完：总数=N，游标=N</span><br><span class="line">    -&gt; 第一次 ScanNetworks：按总数分配，但 Next 已无数据</span><br><span class="line">    -&gt; 响应结束释放缓存：总数=0</span><br><span class="line">    -&gt; 第二次 ScanNetworks：进入零结果路径</span><br></pre></td></tr></table></figure><p>如果空结果编码又把“0 个元素”直接传给 heap allocation，而底层把零长度返回的空指针当成致命 OOM，就可能形成：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">重复 ScanNetworks</span><br><span class="line">  -&gt; 空扫描结果</span><br><span class="line">  -&gt; 零长度内存申请</span><br><span class="line">  -&gt; 被错误识别为内存耗尽</span><br><span class="line">  -&gt; 主动 abort / 系统重启</span><br></pre></td></tr></table></figure><p>这不是普通的 heap 不足，增加内存也不能修复。真正需要处理的是三层边界：</p><ol><li>扫描缓存应是不可变快照，或为每个消费者创建独立 iterator；</li><li>一个消费者的 <code>Next()</code> 和 <code>Release()</code> 不能影响另一个请求；</li><li>零结果必须编码成合法空数组，零长度 allocation 不能被当成真实 OOM。</li></ol><p>这个案例也解释了为什么 HomePod mini、Google Nest Hub Max 2、SmartThings Hub V3 当前样本成功，而 Echo Dot Max 当前失败样本崩溃：前三台中枢没有让终端进入扫描缓存响应路径，Echo Dot Max 失败样本中的连续扫描恰好把潜伏问题暴露出来。Echo Dot Max 的成功现象仍需单独日志确认其实际路径。</p><p>不能因此得出“只需要兼容 Echo Dot Max”。任何未来使用重复扫描的 Commissioner 都可能触发相同问题。</p><h2 id="怎样阅读四台中枢对应的终端日志？"><a href="#怎样阅读四台中枢对应的终端日志？" class="headerlink" title="怎样阅读四台中枢对应的终端日志？"></a>怎样阅读四台中枢对应的终端日志？</h2><p>不要从最终的“添加成功&#x2F;失败”倒推根因，应该按阶段找锚点。</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">BLE connected</span><br><span class="line">  -&gt; PBKDFParam / PASE_Pake</span><br><span class="line">  -&gt; ArmFailSafe</span><br><span class="line">  -&gt; Attestation / CSR</span><br><span class="line">  -&gt; AddTrustedRootCertificate / AddNOC</span><br><span class="line">  -&gt; ScanNetworks 或 Dataset 添加</span><br><span class="line">  -&gt; ConnectNetwork</span><br><span class="line">  -&gt; BLE-to-Thread handoff</span><br><span class="line">  -&gt; Thread role CHILD / ROUTER</span><br><span class="line">  -&gt; SRP</span><br><span class="line">  -&gt; CASE</span><br><span class="line">  -&gt; CommissioningComplete</span><br><span class="line">  -&gt; Subscribe / ReportData</span><br></pre></td></tr></table></figure><p>可以用下面的判断表快速定位。</p><table><thead><tr><th>最后证据</th><th>当前能证明什么</th><th>下一步重点</th></tr></thead><tbody><tr><td>BLE 已连接，没有 PBKDF&#x2F;PASE</td><td>只到 BLE&#x2F;BTP 边界</td><td>手机&#x2F;中枢 MatterSupport、BTP 和 PASE 日志</td></tr><tr><td>PASE 完成，没有 <code>AddNOC</code></td><td>临时安全会话正常</td><td>Attestation、CSR、证书和 Commissioner 策略</td></tr><tr><td><code>AddNOC</code> 成功，没有网络动作</td><td>pending Fabric 已创建</td><td>Network Commissioning 命令和 Commissioner 日志</td></tr><tr><td>收到 Dataset，没有 Child</td><td>设备拿到网络参数但 Attach 失败</td><td>信道、网络身份、Parent 可达性、射频</td></tr><tr><td>Child&#x2F;SRP 成功，没有 CASE</td><td>Thread 已在线，Matter 长期会话未建立</td><td>Controller、Border Router、DNS-SD 和 CASE</td></tr><tr><td>CASE 成功，没有 <code>CommissioningComplete</code></td><td>Fabric 仍可能被 Fail-safe 回滚</td><td>Commissioner 收尾和超时原因</td></tr><tr><td><code>CommissioningComplete</code> 成功，没有订阅</td><td>配网已闭环，业务在线证据不足</td><td>Controller Subscribe、ReportData 和中枢 App</td></tr></tbody></table><h2 id="给设备开发者的兼容性测试清单"><a href="#给设备开发者的兼容性测试清单" class="headerlink" title="给设备开发者的兼容性测试清单"></a>给设备开发者的兼容性测试清单</h2><p>只用一台中枢、一次成功路径做验证，很容易遗漏 Commissioner 调用顺序差异。至少应覆盖：</p><h3 id="扫描响应"><a href="#扫描响应" class="headerlink" title="扫描响应"></a>扫描响应</h3><ul><li>零个、一个、多个可见 Thread 网络；</li><li>单次扫描、连续两次、连续三次扫描；</li><li>扫描完成前请求、扫描刚完成、缓存过期后请求；</li><li>两个消费者读取同一份扫描快照；</li><li>第一个响应释放后，第二个请求仍能正常读取；</li><li>空结果返回合法响应，不触发零长度 allocation fatal。</li></ul><h3 id="Dataset-与连接"><a href="#Dataset-与连接" class="headerlink" title="Dataset 与连接"></a>Dataset 与连接</h3><ul><li>Commissioner 不扫描，直接下发 Dataset；</li><li>先扫描，再下发 Dataset；</li><li>下发同一 Dataset、更新 Dataset、错误 Dataset；</li><li>BLE 与 Thread 共享射频时的安全切换；</li><li><code>ConnectNetwork</code> 失败后重试和 Fail-safe 回滚。</li></ul><h3 id="完整闭环"><a href="#完整闭环" class="headerlink" title="完整闭环"></a>完整闭环</h3><ul><li><code>AddNOC</code> 后继续完成 Thread Attach；</li><li>Thread Child 后完成 SRP 和 CASE；</li><li><code>CommissioningComplete</code> 后 Fabric 真正持久化；</li><li>重启后能够恢复 Fabric 和 Thread 网络；</li><li>Controller 建立 Subscribe，设备按业务变化发送 ReportData；</li><li>Echo Dot Max、HomePod mini、Google Nest Hub Max 2、SmartThings Hub V3 各自至少完成一次首次配网和一次失败恢复。</li></ul><h2 id="哪些结论不能只靠设备-UART？"><a href="#哪些结论不能只靠设备-UART？" class="headerlink" title="哪些结论不能只靠设备 UART？"></a>哪些结论不能只靠设备 UART？</h2><p>设备日志能证明自己收到了什么、执行到哪一步、是否 Attach、是否建立 CASE。它通常不能单独证明：</p><ul><li>Commissioner 为什么选择扫描或立即重试；</li><li>App 为什么显示某个页面；</li><li>Border Router 是否拥有正确或最新的 Thread 凭据；</li><li>消息只打印长度时，具体 Cluster&#x2F;Command Path 一定是什么；</li><li>没有看到 <code>SubscribeRequest</code> 是对端从未发送，还是抓取窗口太短；</li><li>Thread Child 是否已经等价于中枢 App 在线。</li></ul><p>要确认这些问题，还需要 Controller&#x2F;App 日志、Border Router 日志或 BLE&#x2F;Thread&#x2F;IP 抓包。设备侧“没有观察到”不能直接改写成“对端没有发送”。</p><h2 id="日志脱敏不要只删设备地址"><a href="#日志脱敏不要只删设备地址" class="headerlink" title="日志脱敏不要只删设备地址"></a>日志脱敏不要只删设备地址</h2><p>Matter 和 Thread debug 日志可能包含比地址更敏感的内容。公开文章、工单和聊天记录至少应检查：</p><ul><li>Setup Passcode、二维码和手动配对码；</li><li>DAC 私钥、证书原文和认证数据；</li><li>Thread Network Key、PSKc 和完整 Operational Dataset；</li><li>Fabric ID、Node ID、Compressed Fabric ID；</li><li>网络名称、Extended PAN ID、MAC&#x2F;IP 地址；</li><li>内部产品型号、版本、提交号、构建地址和源码路径。</li></ul><p>真正需要公开的是协议阶段、状态迁移和错误语义，不是现场凭据与设备身份。</p><h2 id="最终结论"><a href="#最终结论" class="headerlink" title="最终结论"></a>最终结论</h2><p>Echo Dot Max、HomePod mini、Google Nest Hub Max 2 和 SmartThings Hub V3 并没有使用四套不同的 Matter 协议。四台中枢对应的配网过程都以 PASE、设备证明、Operational Credentials、Thread Attach、CASE 和 <code>CommissioningComplete</code> 为共同主干；Echo Dot Max 当前失败样本因终端崩溃没有走完后半段，另有成功现象但命令链尚未取得。</p><p>本次实测差异发生在 Commissioner 的 Network Commissioning 调用顺序：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Echo Dot Max 当前失败样本</span><br><span class="line">    -&gt; 先请求终端扫描 Thread 网络</span><br><span class="line">    -&gt; 终端通过 BLE/PASE 返回扫描结果</span><br><span class="line"></span><br><span class="line">HomePod mini / Google Nest Hub Max 2 / SmartThings Hub V3 当前样本</span><br><span class="line">    -&gt; 中枢侧已经知道目标 Thread Dataset</span><br><span class="line">    -&gt; 直接下发 Dataset 并 ConnectNetwork</span><br></pre></td></tr></table></figure><p>这两条路径都合理。真正的设备兼容性要求，是同时安全支持“扫描后选择”和“直接提供网络”，并正确处理零结果、重复请求、射频切换和 Fail-safe 回滚。</p><p>分析具体中枢的配网差异时，最重要的不是把一次实测固化成型号的永久行为，而是始终沿着证据链追问：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">是谁发起命令</span><br><span class="line">  -&gt; 设备在哪个承载上收到</span><br><span class="line">  -&gt; Thread 网络信息从哪里来</span><br><span class="line">  -&gt; 设备是否真正 Attach</span><br><span class="line">  -&gt; CASE 与 CommissioningComplete 是否闭环</span><br><span class="line">  -&gt; 配网后 Controller 是否建立订阅</span><br></pre></td></tr></table></figure><p>只有把这些层次分开，才能判断问题属于中枢凭据选择、Commissioner 调用顺序、Thread 射频接入，还是终端本地状态和内存生命周期。</p><h2 id="延伸阅读"><a href="#延伸阅读" class="headerlink" title="延伸阅读"></a>延伸阅读</h2><ul><li><a href="/posts/matter-over-thread-zigbee-commissioning-comparison/">Matter over Thread 入网为什么这么复杂？与 Zigbee 一步一步对照看懂</a></li><li><a href="/posts/alexa-thread-network-key-prompt-analysis/">Alexa 为什么会要求输入 Thread Network Key？一次 Matter over Thread 配网故障的分层分析</a></li><li><a href="/posts/apple-home-dual-fabric-commissioning-log-analysis/">一次 Apple Home 配网为什么会出现两个 Matter Fabric？从完整日志还原真相</a></li><li><a href="/posts/matter-thread-zigbee-layers/">Matter、Thread 与 Zigbee：先分清它们在哪一层</a></li></ul>]]>
    </content>
    <id>https://oniums.github.io/posts/matter-thread-commissioning-controller-device-comparison/</id>
    <link href="https://oniums.github.io/posts/matter-thread-commissioning-controller-device-comparison/"/>
    <published>2026-08-06T02:00:00.000Z</published>
    <summary>
      <![CDATA[<p>同一台 Matter over Thread 设备，分别通过 Echo Dot Max、HomePod mini、Google Nest Hub Max 2 和 SmartThings Hub V3 配网时，设备日志为什么不完全一样？</p>
<p>实测中，HomePod mini、Google Nest Hub Max 2 和 SmartThings Hub V3 在写入 Matter 运行凭据后，直接把 Thread Operational Dataset 下发给设备；Echo Dot Max 则先让设备查询附近的 Thread 网络，再决定下一步。</p>
<p>这是否意味着 Echo Dot Max 固定会先扫描，而另外三台中枢永远不扫描？扫描发生在中枢还是终端？结果又是怎样返回的？</p>]]>
    </summary>
    <title>同一台 Matter over Thread 设备，在 Echo Dot Max、HomePod mini、Google Nest Hub Max 2 和 SmartThings Hub V3 上怎样配网？</title>
    <updated>2026-08-06T02:37:07.977Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Matter" scheme="https://oniums.github.io/tags/matter/"/>
    <category term="Thread" scheme="https://oniums.github.io/tags/thread/"/>
    <category term="Alexa" scheme="https://oniums.github.io/tags/alexa/"/>
    <category term="Amazon Echo" scheme="https://oniums.github.io/tags/amazon-echo/"/>
    <category term="Commissioning" scheme="https://oniums.github.io/tags/commissioning/"/>
    <content>
      <![CDATA[<p>使用 Alexa 给一台 Matter over Thread 设备配网时，我遇到了一个很奇怪的页面：Matter 二维码已经扫描完成，Alexa 也显示出了 Thread 网络名称，下一步却要求手工输入一个 32 位的 <code>Thread Network Key</code>。</p><p>问题是，Echo 机身、Alexa App 和 Matter 设备标签上都找不到这个 Key。Amazon Forum 上也有人问过完全相同的问题：到底应该去哪里找？</p><span id="more"></span><p>先给出结论：</p><blockquote><p>如果目标 Thread 网络由 Echo 创建，普通用户通常不应该手工寻找或输入 Network Key。Echo 会自动生成并保存 Thread 凭据，正常配网流程应由 Alexa 自动取得这些凭据，再通过 Matter Network Commissioning 交给新设备。</p></blockquote><p>因此，这个输入框更像一个被暴露到用户界面的凭据取得失败：Alexa 已经走到“选择 Thread 网络”的阶段，却没有成功取得该网络的安全资料。</p><p>本文根据亲历现象、Amazon 当前公开文档和 Matter over Thread 的协议分层，还原这个页面到底在问什么、故障发生在哪一段，以及普通用户和设备开发者应该分别怎样处理。</p><p>真实网络密钥、设备地址、内部日志和产品身份均不会出现在本文中。</p><h2 id="先区分四种完全不同的凭据"><a href="#先区分四种完全不同的凭据" class="headerlink" title="先区分四种完全不同的凭据"></a>先区分四种完全不同的凭据</h2><p>这个问题难理解，很大程度上是因为配网过程中同时出现了二维码、密码、Network Key 和证书。它们服务于不同协议层。</p><table><thead><tr><th>名称</th><th>常见形式</th><th>所属层次</th><th>主要用途</th></tr></thead><tbody><tr><td>Matter Setup Passcode</td><td>二维码或 11 位数字码</td><td>Matter Commissioning</td><td>通过 PASE 建立临时安全配网会话</td></tr><tr><td>Thread Network Key</td><td>16 字节，通常写成 32 个十六进制字符</td><td>Thread 网络安全</td><td>让节点参与目标 Thread 网络的受保护通信</td></tr><tr><td>Thread Active Operational Dataset</td><td>一组 TLV 网络参数</td><td>Thread 网络接入</td><td>描述 Network Key、网络名、信道、PAN 信息和其他运行参数</td></tr><tr><td>Matter NOC</td><td>运行证书</td><td>Matter Fabric</td><td>赋予设备长期 Matter 身份，用于后续 CASE 会话</td></tr></tbody></table><p>扫描 Matter 二维码，只解决第一行的问题。二维码中的 Setup Passcode 不是 Thread Network Key，也不能由它计算出用户家中的 Thread Key。</p><p>同样，Thread Network Key 也不是 Matter Fabric 身份。多个 Matter Fabric 可以共享同一张 Thread 网络；知道 Thread Key 并不等于拥有某个 Fabric 的 NOC 和控制权限。</p><p>可以先用下面这张简图记住它们的关系：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Matter 二维码 / Setup Passcode</span><br><span class="line">    -&gt; 建立 PASE 临时会话</span><br><span class="line"></span><br><span class="line">Thread Operational Dataset</span><br><span class="line">    -&gt; 让设备加入低功耗 IPv6 Mesh</span><br><span class="line"></span><br><span class="line">Matter NOC</span><br><span class="line">    -&gt; 让设备加入 Fabric 并建立长期 CASE 会话</span><br></pre></td></tr></table></figure><h2 id="正常的-Matter-over-Thread-配网怎样走？"><a href="#正常的-Matter-over-Thread-配网怎样走？" class="headerlink" title="正常的 Matter over Thread 配网怎样走？"></a>正常的 Matter over Thread 配网怎样走？</h2><p>以一台恢复出厂的新设备和一个已经工作的 Echo Thread Border Router 为例，典型流程可以压缩为六个阶段。</p><table><thead><tr><th>阶段</th><th>主要动作</th><th>成功到这里能证明什么</th></tr></thead><tbody><tr><td>1</td><td>Alexa 扫描 Matter 二维码，通过 BLE 找到设备</td><td>找到了目标 Commissionee</td></tr><tr><td>2</td><td>使用 Setup Passcode 建立 PASE</td><td>Commissioner 与设备有了临时安全通道</td></tr><tr><td>3</td><td>完成设备证明、CSR 和 <code>AddNOC</code> 等步骤</td><td>设备获得目标 Matter Fabric 的运行凭据</td></tr><tr><td>4</td><td>Alexa&#x2F;Echo 选择 Thread 网络，取得并下发其 Operational Dataset</td><td>设备获得目标 Thread 网络配置</td></tr><tr><td>5</td><td>设备完成 Thread Attach，成为 Child 或其他角色</td><td>设备已经进入 Thread IPv6 Mesh</td></tr><tr><td>6</td><td>通过 IP 建立 CASE，执行 <code>CommissioningComplete</code></td><td>新 Fabric 被确认提交，设备可进入日常控制阶段</td></tr></tbody></table><p>这条时间线有两个很重要的边界：</p><ul><li>扫码成功不等于 Thread 已经入网；</li><li>Thread Attach 成功也不等于 Matter 配网已经完成。</li></ul><p>Alexa 弹出 Network Key 输入框时，设备通常还没有走到 Thread Attach。问题集中在第四阶段：<strong>Commissioner 正在选择或取得 Thread 网络凭据。</strong></p><h2 id="为什么说这不是设备标签缺了一个密码？"><a href="#为什么说这不是设备标签缺了一个密码？" class="headerlink" title="为什么说这不是设备标签缺了一个密码？"></a>为什么说这不是设备标签缺了一个密码？</h2><p>恢复出厂的 Matter over Thread 设备不可能预先知道用户家中未来会使用哪一张 Thread 网络。它出厂时保存的是自己的 Matter Setup Passcode、设备证明材料等数据，而不是 Echo 将来随机生成的家庭 Thread Network Key。</p><p>Thread 网络凭据应由拥有或管理该网络的一方提供：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Echo 或其他 Border Router 创建 Thread 网络</span><br><span class="line">    -&gt; 平台安全保存 Operational Dataset</span><br><span class="line">    -&gt; 配网时由 Commissioner 取得 Dataset</span><br><span class="line">    -&gt; 通过已建立的 PASE 会话发送给新设备</span><br></pre></td></tr></table></figure><p>所以，当 Alexa 一边显示网络名、一边要求用户输入 Key，不能据此断定终端固件没有生成 Key。恰恰相反，终端此时是等待接收目标网络的凭据，而不是负责创造 Echo 网络的凭据。</p><h2 id="Amazon-当前是怎样保存-Thread-凭据的？"><a href="#Amazon-当前是怎样保存-Thread-凭据的？" class="headerlink" title="Amazon 当前是怎样保存 Thread 凭据的？"></a>Amazon 当前是怎样保存 Thread 凭据的？</h2><p>Amazon 的用户帮助文档说明，Echo 等设备创建 Thread 网络时可以自动生成 Thread credentials，并把它们安全保存到 Amazon，以便后续设备减少手工配置步骤。文档提供了删除已保存 Thread passwords 的入口，却没有给普通用户提供显示原始 Network Key 的步骤。</p><p>如果 Thread 网络由 eero 创建，eero 和 Amazon 账户链接后可以同步 Thread network identifiers、passwords 和 keys。这个同步用于快速设备入网，也用于让受支持的 eero 与 Echo 尽量进入同一张 Thread Mesh。</p><p>Amazon 还提供 Credential Locker API，可以返回网络的 <code>networkKey</code>、PSKc、PAN ID、信道等资料。不过，这个接口属于受限的 Alexa Smart Home 厂商能力，需要业务授权、Skill 权限和事件网关访问令牌。它不是一个给家庭用户复制 Echo Network Key 的公开查询接口。</p><p>因此，目前更准确的说法是：</p><blockquote><p>Amazon 保存并使用 Thread Network Key，但没有公开一个面向普通 Alexa 用户的“显示原始 Key”操作路径。</p></blockquote><h2 id="为什么退出重试后有时又能成功？"><a href="#为什么退出重试后有时又能成功？" class="headerlink" title="为什么退出重试后有时又能成功？"></a>为什么退出重试后有时又能成功？</h2><p>公开讨论中，有用户在第一次看到 Key 输入框后退出配网，再次扫描时 Alexa 直接越过了该页面并成功连接。我也遇到了同样的 Key 输入页面；但仅凭这个页面，只能确认自动凭据链没有正常闭合，不能确认现场与公开讨论具有同一个后台根因。</p><p>仅凭 App 页面无法证明 Amazon 后台的确切根因，但可以列出几个符合现象的可能性：</p><ol><li>Echo 刚启用 Thread Border Router，网络已经被发现，但凭据尚未完成同步；</li><li>Alexa App、Amazon 账户凭据存储与 Echo 当前网络状态暂时不一致；</li><li>家庭中存在多张 Thread 网络，Alexa 发现了网络名，却没有对应凭据的读取权限；</li><li>Border Router 曾恢复出厂或重建网络，手机或云端仍保留旧的 Thread 记录；</li><li>App 配网状态机进入了本应由平台自动处理的手工兜底分支。</li></ol><p>这些是故障模型，不是对某一次现场问题的已证实根因。要确认是哪一种，至少需要同时观察 Alexa&#x2F;手机侧状态、Border Router 的 MeshCoP 广播信息和终端的 Matter commissioning 日志。</p><h2 id="普通用户应该怎样处理？"><a href="#普通用户应该怎样处理？" class="headerlink" title="普通用户应该怎样处理？"></a>普通用户应该怎样处理？</h2><h3 id="场景一：新设备要加入-Echo-自己创建的网络"><a href="#场景一：新设备要加入-Echo-自己创建的网络" class="headerlink" title="场景一：新设备要加入 Echo 自己创建的网络"></a>场景一：新设备要加入 Echo 自己创建的网络</h3><p>不要把 Matter 二维码中的数字当成 Thread Key，也不要在网上寻找所谓通用 Key。</p><p>可以按下面的低风险顺序处理：</p><ol><li>确认目标 Echo 型号确实具备 Thread Border Router，而不只是 Matter Controller 能力；</li><li>更新 Alexa App 和 Echo 固件；</li><li>保持 Echo 在线，等待其 Thread 网络完成初始化；</li><li>退出当前配网页面，让 Matter 设备重新进入可配网状态后再试；</li><li>记录 Alexa 显示的 Thread Network Name，确认重试时是否选择了同一张网络。</li></ol><p>不要一开始就删除 Amazon 保存的 Thread passwords 或恢复 Echo 出厂设置。这些操作可能让 Echo 创建一张新网络，使原有 Thread 设备失去连接。</p><h3 id="场景二：Thread-网络由-eero-创建"><a href="#场景二：Thread-网络由-eero-创建" class="headerlink" title="场景二：Thread 网络由 eero 创建"></a>场景二：Thread 网络由 eero 创建</h3><p>确认 eero 中已启用 Thread，并在 <code>Amazon Connected Home</code> 中正确链接 eero 与 Amazon 账户，同时允许用于简化设备配置的凭据同步。</p><p>这里要解决的仍然是平台之间传递凭据，而不是从 Matter 设备标签上找密码。</p><h3 id="场景三：设备已经加入-Apple、Google-或其他-Matter-生态"><a href="#场景三：设备已经加入-Apple、Google-或其他-Matter-生态" class="headerlink" title="场景三：设备已经加入 Apple、Google 或其他 Matter 生态"></a>场景三：设备已经加入 Apple、Google 或其他 Matter 生态</h3><p>如果目的是让同一台设备再被 Alexa 控制，优先使用第一个生态生成的 Matter Multi-Admin 配对码，而不是再次使用设备标签上的首次配对码，也不是重新给设备输入 Thread Network Key。</p><p>第二个 Matter Controller 可以通过现有 IP 网络建立自己的 Fabric。Multi-Admin 增加的是 Matter 管理关系，通常不需要让已经在线的终端重新加入另一张 Thread 网络。</p><h3 id="场景四：Border-Router-重置过"><a href="#场景四：Border-Router-重置过" class="headerlink" title="场景四：Border Router 重置过"></a>场景四：Border Router 重置过</h3><p>先确认旧网络是否仍由家中其他 Thread Border Router 维持。不要只比较 Network Name；同名网络也可能具有不同的 Extended PAN ID 和安全资料。</p><p>如果旧网络已经消失，而手机或平台仍保存旧凭据，继续把旧 Dataset 下发给新设备只会让设备反复搜索一张不存在的网络。这时需要按目标平台的正式流程重建或迁移网络，而不是随机输入一个 Key。</p><h2 id="设备开发者怎样判断故障阶段？"><a href="#设备开发者怎样判断故障阶段？" class="headerlink" title="设备开发者怎样判断故障阶段？"></a>设备开发者怎样判断故障阶段？</h2><p>如果能够取得终端 UART 日志，不要只看最终一条“入网失败”。应按协议阶段寻找下面这些锚点：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">BLE connected</span><br><span class="line">  -&gt; PBKDFParam / PASE_Pake</span><br><span class="line">  -&gt; Network Commissioning 命令</span><br><span class="line">  -&gt; Thread Dataset 被保存</span><br><span class="line">  -&gt; OpenThread role: CHILD / ROUTER</span><br><span class="line">  -&gt; SRP 服务发布</span><br><span class="line">  -&gt; CASE established</span><br><span class="line">  -&gt; CommissioningComplete</span><br></pre></td></tr></table></figure><p>判断方法如下：</p><ul><li>连 <code>PBKDFParam</code>、<code>PASE_Pake</code> 都没有：还不能归因于 Thread；</li><li>PASE 已完成，但没有收到网络配置：重点检查 Commissioner 的凭据选择与下发；</li><li>Dataset 已收到，但一直无法成为 <code>CHILD</code>：再检查信道、网络身份、安全资料、射频和 Parent 可达性；</li><li>已经 <code>CHILD</code>，却没有 CASE 或 CommissioningComplete：Thread 接入成功，但 Matter 配网链路仍未闭合。</li></ul><p>Alexa App 的 Network Key 输入框只能帮助定位到平台正在处理 Thread credentials，不能单独证明 Key 错了，更不能证明终端已经尝试过 Thread Attach。</p><h2 id="开发调试时能否从已入网设备读取-Key？"><a href="#开发调试时能否从已入网设备读取-Key？" class="headerlink" title="开发调试时能否从已入网设备读取 Key？"></a>开发调试时能否从已入网设备读取 Key？</h2><p>可以，但适用边界很窄。</p><p>如果这是自己拥有、自己开发并且已经加入目标 Thread 网络的终端，它本地保存的 Active Operational Dataset 必然包含当前 Network Key。开发者可以在受控 Debug 固件中读取 Dataset 的 Network Key，用于授权范围内的 Wireshark&#x2F;802.15.4 抓包分析。</p><p>这种方法解决的是“调试自己的网络”，不是普通用户使用 Alexa 的正常步骤。安全设计至少应满足：</p><ul><li>只存在于明确的 Debug 构建；</li><li>Release 在编译阶段完全排除读取和打印路径；</li><li>Key 只进入受控的本地日志或 Wireshark配置；</li><li>临时缓冲区使用后立即清零；</li><li>不把 Key 写进源码、Git、文章、工单或聊天记录。</li></ul><p>另外，Thread Network Key 只解决 Thread&#x2F;IEEE 802.15.4 网络层的解密。Matter CASE 仍提供端到端会话保护，拿到 Thread Key 不代表 Wireshark 可以直接看到 Matter 业务明文。</p><p>如果新设备从未加入过目标网络，它本地当然也没有这张网络的 Key。此时不能靠终端 Debug 凭空恢复 Echo 尚未下发的凭据。</p><h2 id="一套更可靠的现场取证清单"><a href="#一套更可靠的现场取证清单" class="headerlink" title="一套更可靠的现场取证清单"></a>一套更可靠的现场取证清单</h2><p>再次遇到这个页面时，可以先保存以下信息，不要保存真实 Key：</p><ol><li>手机系统、Alexa App 版本和账户区域；</li><li>Echo&#x2F;eero 的具体型号与固件版本；</li><li>Alexa 显示的 Thread Network Name；</li><li>同一局域网中 <code>_meshcop._udp</code> 服务公布的 Network Name、Extended PAN ID 和 Border Agent ID；</li><li>设备是全新首次配网，还是已经加入过其他生态；</li><li>弹窗出现在扫码后哪个阶段；</li><li>设备 UART 是否出现 PASE、Network Commissioning、Thread Attach、CASE 和 CommissioningComplete；</li><li>退出重试后，是仍要求 Key，还是直接跳过并成功；</li><li>Border Router 是否刚启用 Thread、刚升级或刚恢复出厂；</li><li>家中是否同时存在 Apple、Google、Amazon、eero 或 Home Assistant 创建的多张 Thread 网络。</li></ol><p>这组证据可以把问题分成三类：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">平台没有取得凭据</span><br><span class="line">    vs</span><br><span class="line">平台取得了错误/过期凭据</span><br><span class="line">    vs</span><br><span class="line">设备收到正确 Dataset 后仍然 Attach 失败</span><br></pre></td></tr></table></figure><p>三者的修复方向完全不同。</p><h2 id="安全提醒：不要公开-Thread-Network-Key"><a href="#安全提醒：不要公开-Thread-Network-Key" class="headerlink" title="安全提醒：不要公开 Thread Network Key"></a>安全提醒：不要公开 Thread Network Key</h2><p>Thread Network Key 是整张网络共享的敏感材料，不是一个可以贴进论坛求助的普通错误码。截图、UART 日志、pcap 辅助文件和录屏都可能无意中包含它。</p><p>如果 Key 已经公开泄露，应把它视为网络凭据泄露，并根据 Border Router&#x2F;平台支持能力迁移或重建安全资料。不要假设删除一条 App 记录就已经让旧 Key 失效。</p><h2 id="最终结论"><a href="#最终结论" class="headerlink" title="最终结论"></a>最终结论</h2><p>Alexa 要求输入 32 位 Thread Network Key，不等于 Matter 设备缺少一个出厂密码，也不等于终端固件应该自己生成家庭网络凭据。</p><p>对 Echo 创建的网络，正常设计是：Echo 自动生成凭据，Amazon 安全保存，Alexa 配网时自动取得，再通过 Matter commissioning 下发给新设备。手工输入框的出现，说明这条自动凭据链没有按预期闭合。</p><p>最有效的排查方式不是继续寻找一个隐藏在二维码里的密码，而是沿着下面这条证据链定位：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Matter 扫码与 PASE</span><br><span class="line">  -&gt; 设备证明与 Matter 运行凭据配置</span><br><span class="line">  -&gt; 平台选择 Thread 网络</span><br><span class="line">  -&gt; 平台取得 Operational Dataset</span><br><span class="line">  -&gt; 设备接收 Dataset</span><br><span class="line">  -&gt; Thread Attach</span><br><span class="line">  -&gt; CASE 与 CommissioningComplete</span><br></pre></td></tr></table></figure><p>只有看到 Dataset 已经到达设备，才应该把调查重点从 Alexa&#x2F;凭据同步转向 Thread Attach 和终端实现。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://www.amazonforum.com/s/question/0D56Q0000CkZIKWSQ4/how-do-i-find-the-thread-network-key">Amazon Forum：How do I find the Thread network key?</a></li><li><a href="https://digprjsurvey.amazon.co.uk/csad/help/node/201730860">Amazon：Saving Your Wi-Fi Settings to Amazon FAQs</a></li><li><a href="https://developer.amazon.com/en-US/docs/alexa/device-apis/credential-locker-api.html">Amazon Developer：Credential Locker REST API Reference</a></li><li><a href="https://developer.amazon.com/docs/frustration-free-setup/matter-simple-setup-for-thread-overview.html">Amazon Developer：Matter Simple Setup for Thread Overview</a></li><li><a href="https://eero.com/support/articles/360045529291-What-is-shared-when-I-link-my-Amazon-account">eero：What is shared when I link my Amazon account?</a></li><li><a href="https://developers.home.google.com/thread">Google Home Developers：Thread Network SDK for Android</a></li><li><a href="https://openthread.io/reference/cli/commands#networkkey">OpenThread CLI：Network Key command reference</a></li><li><a href="/posts/matter-over-thread-zigbee-commissioning-comparison/">Matter over Thread 入网为什么这么复杂？与 Zigbee 一步一步对照看懂</a></li><li><a href="/posts/apple-home-dual-fabric-commissioning-log-analysis/">一次 Apple Home 配网为什么会出现两个 Matter Fabric？从完整日志还原真相</a></li></ul>]]>
    </content>
    <id>https://oniums.github.io/posts/alexa-thread-network-key-prompt-analysis/</id>
    <link href="https://oniums.github.io/posts/alexa-thread-network-key-prompt-analysis/"/>
    <published>2026-08-05T03:30:00.000Z</published>
    <summary>
      <![CDATA[<p>使用 Alexa 给一台 Matter over Thread 设备配网时，我遇到了一个很奇怪的页面：Matter 二维码已经扫描完成，Alexa 也显示出了 Thread 网络名称，下一步却要求手工输入一个 32 位的 <code>Thread Network Key</code>。</p>
<p>问题是，Echo 机身、Alexa App 和 Matter 设备标签上都找不到这个 Key。Amazon Forum 上也有人问过完全相同的问题：到底应该去哪里找？</p>]]>
    </summary>
    <title>Alexa 为什么会要求输入 Thread Network Key？一次 Matter over Thread 配网故障的分层分析</title>
    <updated>2026-08-05T03:51:33.870Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Matter" scheme="https://oniums.github.io/tags/matter/"/>
    <category term="Thread" scheme="https://oniums.github.io/tags/thread/"/>
    <category term="Commissioning" scheme="https://oniums.github.io/tags/commissioning/"/>
    <category term="Apple Home" scheme="https://oniums.github.io/tags/apple-home/"/>
    <category term="Fabric" scheme="https://oniums.github.io/tags/fabric/"/>
    <content>
      <![CDATA[<p>把一台 Matter over Thread 设备添加到 Apple Home 后，设备重启时打印出了两个 Fabric。与此同时，产品的“配网成功”灯效也连续出现了两次。</p><p>这是否意味着一台 HomePod 固定占用两个 Fabric？还是日志重复打印、配网重试，或者设备误入了另一个生态？</p><span id="more"></span><p>一份覆盖首次配网、两次凭据写入和重启恢复的完整设备日志给出了更精确的答案：</p><blockquote><p>在这次实测中，Apple 配网流程确实创建了两个独立的 Matter Fabric：一个属于 <code>Apple Inc.</code>，另一个属于 <code>Apple Keychain</code>。但这不能简化成“每台 HomePod 固定占两个 Fabric”，也不能直接推广成 Apple 对所有系统版本的永久承诺。</p></blockquote><p>本文不公开原始设备地址、Node ID、Fabric ID、Thread Dataset 或任何网络密钥，只保留理解协议流程所需的日志锚点。</p><h2 id="先把容易混淆的三个概念拆开"><a href="#先把容易混淆的三个概念拆开" class="headerlink" title="先把容易混淆的三个概念拆开"></a>先把容易混淆的三个概念拆开</h2><h3 id="Fabric-是-Matter-管理域"><a href="#Fabric-是-Matter-管理域" class="headerlink" title="Fabric 是 Matter 管理域"></a>Fabric 是 Matter 管理域</h3><p>Matter Fabric 是一组共享信任根和运行身份体系的 Matter 节点。设备加入新的 Fabric 时，会获得属于该 Fabric 的 NOC、Node ID、访问控制项等数据。</p><p>一个设备可以同时属于多个 Fabric。例如，它可以分别接受 Apple Home、Google Home 或 Home Assistant 的直接 Matter 控制。这正是 Matter Multi-Admin 的基础。</p><h3 id="Thread-网络是-IPv6-Mesh"><a href="#Thread-网络是-IPv6-Mesh" class="headerlink" title="Thread 网络是 IPv6 Mesh"></a>Thread 网络是 IPv6 Mesh</h3><p>Thread Operational Dataset 决定设备加入哪张 Thread Mesh，包括网络名称、信道、PAN 信息和安全材料。它解决的是低功耗 IPv6 网络接入，不定义设备属于哪个 Matter 管理域。</p><p>因此：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">一张 Thread 网络</span><br><span class="line">    可以承载</span><br><span class="line">多个 Matter Fabric 的加密会话</span><br></pre></td></tr></table></figure><p>第二个 Fabric 不等于第二张 Thread 网络。</p><h3 id="HomePod-可以同时承担多个角色"><a href="#HomePod-可以同时承担多个角色" class="headerlink" title="HomePod 可以同时承担多个角色"></a>HomePod 可以同时承担多个角色</h3><p>HomePod 或 Apple TV 可能同时承担家庭中枢、Matter Controller 和 Thread Border Router 等角色。这些角色装在同一台硬件里，并不意味着它们是同一个协议概念。</p><p>Thread Border Router 负责在 Thread 与家庭其他 IP 网络之间路由数据；Fabric 则描述 Matter 身份和信任关系。不能按 HomePod 数量计算 Fabric 数量。</p><h2 id="完整日志展示了怎样的时间线？"><a href="#完整日志展示了怎样的时间线？" class="headerlink" title="完整日志展示了怎样的时间线？"></a>完整日志展示了怎样的时间线？</h2><p>将唯一标识全部脱敏后，整个流程可以压缩成六个阶段。</p><table><thead><tr><th>阶段</th><th>设备侧关键证据</th><th>能证明什么</th></tr></thead><tbody><tr><td>1</td><td>BLE 建连，完成 PBKDF 与 PASE</td><td>手机与新设备建立临时安全配网会话</td></tr><tr><td>2</td><td>第一次 <code>AddTrustedRootCertificate</code>、<code>AddNOC</code></td><td>创建 Fabric 1</td></tr><tr><td>3</td><td>Fabric 1 建立 CASE，收到第一次 <code>CommissioningComplete</code> 并提交存储</td><td>第一个 Fabric 完整配网成功</td></tr><tr><td>4</td><td>Fabric 1 的管理员通过 CASE 再次发送 CSR、Trusted Root 和 <code>AddNOC</code></td><td>由已有管理员创建 Fabric 2</td></tr><tr><td>5</td><td>Fabric 2 建立自己的 CASE，收到第二次 <code>CommissioningComplete</code> 并提交存储</td><td>第二个 Fabric 完整配网成功</td></tr><tr><td>6</td><td>设备重启后分别恢复 Fabric 1 和 Fabric 2</td><td>两者都是真实持久化数据，不是 pending 状态或重复打印</td></tr></tbody></table><p>接下来逐段看最关键的区别。</p><h2 id="第一个-Fabric：典型的首次-BLE-PASE-配网"><a href="#第一个-Fabric：典型的首次-BLE-PASE-配网" class="headerlink" title="第一个 Fabric：典型的首次 BLE&#x2F;PASE 配网"></a>第一个 Fabric：典型的首次 BLE&#x2F;PASE 配网</h2><p>恢复出厂的设备先通过 Bluetooth LE 与 Commissioner 建立连接，然后完成 PASE。随后日志出现第一组运行凭据写入：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Received an AddTrustedRootCertificate command</span><br><span class="line">Received an AddNOC command</span><br><span class="line">Added new fabric at index: 0x1</span><br><span class="line">successfully created fabric index 0x1 via AddNOC</span><br></pre></td></tr></table></figure><p>创建 Fabric 还不是最终成功。设备继续加入 Thread，发布运行服务，并使用新 NOC 建立 CASE。直到下面这组日志闭环，Fabric 1 才真正提交：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">CASE Session established ... fabricIndex 1</span><br><span class="line">Received CommissioningComplete</span><br><span class="line">Fabric index 0x1 was committed to storage</span><br><span class="line">Fail-safe cleanly disarmed</span><br></pre></td></tr></table></figure><p>提交日志记录的 Vendor ID 是 <code>0x1349</code>。Matter 开源 SDK 的厂商映射表将它标记为 <code>Apple Inc.</code>。</p><h2 id="第二个-Fabric：不是第二次-BLE-配网"><a href="#第二个-Fabric：不是第二次-BLE-配网" class="headerlink" title="第二个 Fabric：不是第二次 BLE 配网"></a>第二个 Fabric：不是第二次 BLE 配网</h2><p>第一次 <code>CommissioningComplete</code> 之后，日志没有再次出现 BLE 建连、PBKDF、PASE，也没有再次下发 Thread Dataset。</p><p>相反，Fabric 1 中已经通过 CASE 认证的管理员继续发起了第二组操作：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Msg RX from 1:&lt;redacted-controller-node&gt; ... InvokeCommandRequest</span><br><span class="line">GeneralCommissioning: Received ArmFailSafe</span><br><span class="line">OpCreds: Received a CSRRequest command</span><br><span class="line">OpCreds: Received an AddTrustedRootCertificate command</span><br><span class="line">OpCreds: Received an AddNOC command</span><br><span class="line">Added new fabric at index: 0x2</span><br></pre></td></tr></table></figure><p>消息来源中的 <code>1:</code> 表示请求来自 Fabric Index 1。也就是说，第二个 Fabric 不是陌生生态偶然扫到设备后重新配网，而是第一个 Apple Fabric 的管理员通过现有 CASE 安全会话主动创建。</p><p>随后，新 Fabric 的 Controller 使用自己的 NOC 建立 CASE，并完成第二轮提交：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">CASE matched destination ID: fabricIndex 2</span><br><span class="line">CASE Session established ... fabricIndex 2</span><br><span class="line">Received CommissioningComplete</span><br><span class="line">Fabric index 0x2 was committed to storage</span><br><span class="line">Fail-safe cleanly disarmed</span><br></pre></td></tr></table></figure><p>第二次提交记录的 Vendor ID 是 <code>0x1384</code>。Matter SDK 厂商表对它的名称不是 Home Assistant，也不是第二台 HomePod，而是 <code>Apple Keychain</code>。</p><h2 id="重启恢复排除了“重复日志”的可能"><a href="#重启恢复排除了“重复日志”的可能" class="headerlink" title="重启恢复排除了“重复日志”的可能"></a>重启恢复排除了“重复日志”的可能</h2><p>如果只在配网过程中看到两个 <code>Added new fabric</code>，还要考虑 Fail-safe 回滚、pending Fabric 或失败重试。</p><p>这份日志继续记录了设备重启。Matter Server 初始化时，FabricTable 从持久化存储中分别恢复：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Fabric index 0x1 was retrieved from storage ... VendorId 0x1349</span><br><span class="line">Fabric index 0x2 was retrieved from storage ... VendorId 0x1384</span><br></pre></td></tr></table></figure><p>因此可以排除三种误判：</p><ul><li>不是同一 Fabric 的重复打印；</li><li>不是只创建但没有提交的 pending Fabric；</li><li>不是 FabricIndex 显示异常。</li></ul><p>设备上确实保存了两套不同的 Matter Fabric 身份。</p><h2 id="为什么会出现两次“配网成功”灯效？"><a href="#为什么会出现两次“配网成功”灯效？" class="headerlink" title="为什么会出现两次“配网成功”灯效？"></a>为什么会出现两次“配网成功”灯效？</h2><p>如果产品固件把每个 <code>CommissioningComplete</code> 都转换成一次三秒成功提示，那么上述流程自然会产生两次灯效：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Apple Inc. Fabric 完成</span><br><span class="line">    -&gt; CommissioningComplete</span><br><span class="line">    -&gt; 成功灯效</span><br><span class="line"></span><br><span class="line">Apple Keychain Fabric 完成</span><br><span class="line">    -&gt; CommissioningComplete</span><br><span class="line">    -&gt; 再次成功灯效</span><br></pre></td></tr></table></figure><p>这不是设备加入了两次 Thread 网络，而是产品状态机把两个独立 Fabric 的完成事件都当成了用户可见的“整机配网成功”。</p><p>对于产品设计，这里有一个值得单独讨论的问题：第二个 Fabric 的建立属于同一次用户操作，LED 是否应该逐 Fabric 提示，还是只在整个生态流程结束后提示一次？协议事件和用户语义并不天然相同。</p><h2 id="为什么不能写成“HomePod-固定占两个-Fabric”？"><a href="#为什么不能写成“HomePod-固定占两个-Fabric”？" class="headerlink" title="为什么不能写成“HomePod 固定占两个 Fabric”？"></a>为什么不能写成“HomePod 固定占两个 Fabric”？</h2><p>设备日志能证明两个 Apple Fabric 的存在和创建顺序，但不能仅凭设备侧 UART 确认每个 Controller Node 对应哪一台物理 Apple 设备。它更不能证明 Fabric 数量与 HomePod 数量之间存在一一对应关系。</p><p>更准确的说法是：</p><blockquote><p>在本次 Apple Home 配网实测中，Apple 的配网体系为设备创建了 Apple Inc. 和 Apple Keychain 两个 Fabric。</p></blockquote><p>下面几种说法则超出了证据：</p><ul><li>“每台 HomePod 都固定占两个 Fabric”；</li><li>“再增加一台 HomePod 就会再增加两个 Fabric”；</li><li>“所有 iOS、HomePod 软件版本永远采用相同流程”；</li><li>“Thread Border Router 数量就是 Fabric 数量”。</li></ul><p>Apple 的公开支持文档明确说明，Matter 配对信息会通过 iCloud Keychain 同步，并将 Keychain 与 Connected Services 分开管理。不过，公开文档没有直接写明“添加到 Apple Home 必然创建两个 Fabric”。</p><p>目前我们掌握的是两层证据：</p><ol><li>Matter 开源 SDK 的厂商映射表明确存在 <code>Apple Inc.</code> 和 <code>Apple Keychain</code> 两个 Vendor ID；</li><li>完整设备日志证明当前 Apple 软件组合实际使用了这两个 ID，并分别完成了 NOC、CASE、CommissioningComplete、存储提交和重启恢复。</li></ol><p>这是很强的实现证据，但仍不应包装成 Apple 面向未来版本作出的公开协议承诺。</p><h2 id="对-Matter-设备开发有什么影响？"><a href="#对-Matter-设备开发有什么影响？" class="headerlink" title="对 Matter 设备开发有什么影响？"></a>对 Matter 设备开发有什么影响？</h2><h3 id="1-不要假设一次用户添加只产生一个-Fabric"><a href="#1-不要假设一次用户添加只产生一个-Fabric" class="headerlink" title="1. 不要假设一次用户添加只产生一个 Fabric"></a>1. 不要假设一次用户添加只产生一个 Fabric</h3><p>产品状态机、统计和 UI 提示应把“单个 Fabric 完成”与“用户认为的整次添加完成”分开。仅靠 <code>CommissioningComplete</code> 次数驱动整机灯效，可能在不同生态下产生不同体验。</p><h3 id="2-Fabric-容量要按真实条目计算"><a href="#2-Fabric-容量要按真实条目计算" class="headerlink" title="2. Fabric 容量要按真实条目计算"></a>2. Fabric 容量要按真实条目计算</h3><p>不要按手机、HomePod 或 Border Router 数量估算。应读取 Operational Credentials Cluster 的 Fabric&#x2F;NOC 列表、<code>CommissionedFabrics</code> 和设备支持上限，并验证重启后的 FabricTable。</p><h3 id="3-Thread-网络名不能识别-Matter-生态"><a href="#3-Thread-网络名不能识别-Matter-生态" class="headerlink" title="3. Thread 网络名不能识别 Matter 生态"></a>3. Thread 网络名不能识别 Matter 生态</h3><p>即使 Thread 网络名称带有某个平台特征，也只说明设备使用了那份 Thread Dataset。判断 Matter Fabric 归属，应看 NOC、Fabric Vendor ID、CASE peer 和访问控制信息。</p><h3 id="4-删除、订阅和-OTA-都可能具有-Fabric-维度"><a href="#4-删除、订阅和-OTA-都可能具有-Fabric-维度" class="headerlink" title="4. 删除、订阅和 OTA 都可能具有 Fabric 维度"></a>4. 删除、订阅和 OTA 都可能具有 Fabric 维度</h3><p>两个 Apple 相关 Fabric 是两套身份域。某个 Fabric 的 CASE 或 Subscription 失效，不等于另一个 Fabric 被删除；OTA Provider、订阅和控制会话也必须结合 FabricIndex 分析。</p><h2 id="一套可复用的双-Fabric-验证清单"><a href="#一套可复用的双-Fabric-验证清单" class="headerlink" title="一套可复用的双 Fabric 验证清单"></a>一套可复用的双 Fabric 验证清单</h2><p>以后再遇到“一次配网为什么出现两个 Fabric”，可以依次检查：</p><ol><li>配网开始前 <code>CommissionedFabrics</code> 是否为零；</li><li>出现了几次 <code>AddNOC</code>；</li><li>每次 <code>AddNOC</code> 创建了哪个 FabricIndex；</li><li>请求来自 BLE&#x2F;PASE、IP&#x2F;PASE，还是已有 Fabric 的 CASE；</li><li>每个 Fabric 是否分别建立 CASE；</li><li>是否分别收到 <code>CommissioningComplete</code>；</li><li>是否分别出现 <code>committed to storage</code> 和 Fail-safe disarm；</li><li>重启后是否分别 <code>retrieved from storage</code>；</li><li>Vendor ID 在 Matter 厂商表中对应谁；</li><li>整个过程中是否真的更换或重复下发 Thread Dataset。</li></ol><p>只有把这些证据串起来，才能区分真正的双 Fabric、配网重试、Fail-safe 回滚和日志误读。</p><h2 id="最终结论"><a href="#最终结论" class="headerlink" title="最终结论"></a>最终结论</h2><p>这份完整日志证明：一次 Apple Home 用户配网操作可以在设备上留下两个持久化 Matter Fabric，即 <code>Apple Inc.</code> 与 <code>Apple Keychain</code>。</p><p>第二个 Fabric 由第一个 Apple Fabric 的管理员通过已有 CASE 会话创建；没有第二次 BLE&#x2F;PASE，也没有加入第二张 Thread 网络。重启后两个 Fabric 分别恢复，排除了 pending 数据和重复打印。</p><p>因此，最准确的总结不是“HomePod 固定占两个 Fabric”，而是：</p><blockquote><p>当前实测到的 Apple Home 配网实现会同时使用 Apple Home 控制域与 Apple Keychain 控制域；物理家庭中枢、Thread Border Router 和 Matter Fabric 必须分层理解。</p></blockquote><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://support.apple.com/en-ie/102135">Apple：Pair and manage your Matter accessories</a></li><li><a href="https://developer.apple.com/apple-home/matter/">Apple Developer：Matter support in iOS</a></li><li><a href="https://csa-iot.org/developer-resource/specifications-download-request/">CSA：Matter Specifications 下载页</a></li><li><a href="https://github.com/project-chip/connectedhomeip/blob/master/src/app/zap-templates/zcl/data-model/manufacturers.xml">Matter connectedhomeip：Manufacturer Code 映射</a></li><li><a href="/posts/matter-over-thread-zigbee-commissioning-comparison/">Matter over Thread 入网为什么这么复杂？与 Zigbee 一步一步对照看懂</a></li></ul>]]>
    </content>
    <id>https://oniums.github.io/posts/apple-home-dual-fabric-commissioning-log-analysis/</id>
    <link href="https://oniums.github.io/posts/apple-home-dual-fabric-commissioning-log-analysis/"/>
    <published>2026-08-04T07:10:00.000Z</published>
    <summary>
      <![CDATA[<p>把一台 Matter over Thread 设备添加到 Apple Home 后，设备重启时打印出了两个 Fabric。与此同时，产品的“配网成功”灯效也连续出现了两次。</p>
<p>这是否意味着一台 HomePod 固定占用两个 Fabric？还是日志重复打印、配网重试，或者设备误入了另一个生态？</p>]]>
    </summary>
    <title>一次 Apple Home 配网为什么会出现两个 Matter Fabric？从完整日志还原真相</title>
    <updated>2026-08-04T07:19:30.757Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="搭建教程" scheme="https://oniums.github.io/categories/%E6%90%AD%E5%BB%BA%E6%95%99%E7%A8%8B/"/>
    <category term="Telink" scheme="https://oniums.github.io/tags/telink/"/>
    <category term="Zephyr" scheme="https://oniums.github.io/tags/zephyr/"/>
    <category term="Matter" scheme="https://oniums.github.io/tags/matter/"/>
    <category term="固件构建" scheme="https://oniums.github.io/tags/%E5%9B%BA%E4%BB%B6%E6%9E%84%E5%BB%BA/"/>
    <category term="CMake" scheme="https://oniums.github.io/tags/cmake/"/>
    <category term="west" scheme="https://oniums.github.io/tags/west/"/>
    <content>
      <![CDATA[<p>第一次接触 Telink Matter SDK 时，很容易产生一个疑问：Zephyr 在一个仓库，Matter 在另一个仓库，底下还有 HAL、OpenThread 和一些静态库，执行一次 <code>west build</code> 后，它们怎么就变成了一个固件？</p><p>先记住最重要的结论：</p><blockquote><p><strong>通常不是先生成一个 Zephyr 固件和一个 Matter 固件，再把两个 BIN 拼起来。Matter、Zephyr、应用和芯片底层会先分别编译成许多目标文件或静态库，最后由链接器装进同一个 <code>zephyr.elf</code>，再转换成 <code>zephyr.bin</code>。</strong></p></blockquote><span id="more"></span><p>如果启用了 MCUboot、签名或出厂数据，后面还会有一次镜像签名或按 Flash 地址合并。那属于“固件打包”，不是把 Zephyr 和 Matter 两套程序硬拼在一起。</p><p>本文以公开的 Telink、Zephyr 和 Matter 仓库为线索，不要求读者先懂 CMake、Kconfig 或链接器。读完后，你应该能回答：</p><ol><li>每个仓库分别负责什么？</li><li><code>west build</code> 到底调用了谁？</li><li>Zephyr 在哪里进入 Telink 的底层 API？</li><li><code>zephyr.elf</code>、<code>zephyr.bin</code> 和 <code>merged.bin</code> 有什么区别？</li></ol><h2 id="先认识几位参与者"><a href="#先认识几位参与者" class="headerlink" title="先认识几位参与者"></a>先认识几位参与者</h2><p>可以把一次固件构建想成盖房子：</p><table><thead><tr><th>组成部分</th><th>主要职责</th><th>类比</th></tr></thead><tbody><tr><td>产品应用</td><td>按键、传感器、状态机和产品逻辑</td><td>房屋的使用需求</td></tr><tr><td>Matter</td><td>配网、设备模型、Cluster 和安全通信</td><td>智能家居的通用规则</td></tr><tr><td>Zephyr</td><td>线程、定时器、内存、日志、驱动框架和构建系统</td><td>施工组织与基础设施</td></tr><tr><td>OpenThread</td><td>Thread 网络协议</td><td>通往外界的道路系统</td></tr><tr><td>Telink 驱动与 HAL</td><td>把通用接口落到具体芯片外设和射频</td><td>真正操作机器的工人</td></tr><tr><td>工具链</td><td>编译、链接并转换文件格式</td><td>加工与装配设备</td></tr></tbody></table><p>这些部分不是彼此独立运行的几个固件。它们更像不同来源的零件，最后被装进同一个可执行程序。</p><p>下面的仓库与文件关系核对时间为 2026 年 8 月 2 日。公开开发环境里，常见结构可以简化为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Zephyr workspace</span><br><span class="line">├── zephyr/                       # RTOS、驱动框架、west manifest</span><br><span class="line">├── modules/hal/telink/           # Telink HAL、适配层和底层依赖</span><br><span class="line">├── modules/lib/openthread/       # OpenThread</span><br><span class="line">├── modules/lib/openthread_telink_lib/  # 某些配置使用的 Telink OpenThread 库</span><br><span class="line">└── matter/                       # connectedhomeip，Matter SDK 与示例应用</span><br></pre></td></tr></table></figure><p>实际目录名会随 SDK 版本变化，但角色基本不变。<code>west.yml</code> 像一张依赖清单：它记录要取哪些仓库、放在哪个目录、固定到哪个 revision。Telink 官方环境准备流程中的 <code>west update</code> 和 <code>west blobs fetch hal_telink</code>，就是根据工作区描述取回源码模块和 HAL 所需内容。</p><h2 id="从一条命令开始"><a href="#从一条命令开始" class="headerlink" title="从一条命令开始"></a>从一条命令开始</h2><p>Telink 的公开 Matter 示例通常从类似命令开始：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> scripts/activate.sh</span><br><span class="line">west build -b &lt;board&gt; -- -DFLASH_SIZE=&lt;size&gt;</span><br></pre></td></tr></table></figure><p>表面上只运行了 <code>west</code>，背后大致会经过这条链：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">west build</span><br><span class="line">  ↓</span><br><span class="line">CMake 配置应用和 Zephyr</span><br><span class="line">  ├── 读取 CMakeLists.txt</span><br><span class="line">  ├── 合并 prj.conf / Kconfig</span><br><span class="line">  ├── 解析 board 与 Devicetree overlay</span><br><span class="line">  └── 生成 Ninja 构建规则</span><br><span class="line">  ↓</span><br><span class="line">GN + Ninja 编译 Matter 库</span><br><span class="line">  ↓</span><br><span class="line">Ninja 编译应用、Zephyr、OpenThread、驱动和 HAL</span><br><span class="line">  ↓</span><br><span class="line">链接为 build/zephyr/zephyr.elf</span><br><span class="line">  ↓</span><br><span class="line">objcopy 转换为 build/zephyr/zephyr.bin</span><br><span class="line">  ↓</span><br><span class="line">按配置执行签名、OTA 或多镜像合并</span><br></pre></td></tr></table></figure><p>下面逐步拆开看。</p><h2 id="第一步：应用把构建入口交给-Zephyr"><a href="#第一步：应用把构建入口交给-Zephyr" class="headerlink" title="第一步：应用把构建入口交给 Zephyr"></a>第一步：应用把构建入口交给 Zephyr</h2><p>以公开的 Matter Telink 示例为例，应用目录里通常能看到：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">telink/</span><br><span class="line">├── CMakeLists.txt</span><br><span class="line">├── prj.conf</span><br><span class="line">├── boards/</span><br><span class="line">├── src/</span><br><span class="line">└── *.zap 或其他数据模型文件</span><br></pre></td></tr></table></figure><p>这些文件分别回答不同问题：</p><ul><li><code>CMakeLists.txt</code>：哪些源码要参与编译，还要加载哪些构建模块；</li><li><code>prj.conf</code>：要打开哪些 Zephyr、Matter、网络和驱动功能；</li><li>board 与 overlay：芯片、内存、GPIO、UART、Flash 分区等硬件描述；</li><li>ZAP 或数据模型文件：Matter Endpoint、Cluster、Attribute 和 Command；</li><li><code>src/</code>：应用自己的 C&#x2F;C++ 代码。</li></ul><p>Matter 示例的 Telink <code>common.cmake</code> 会整理配置和 overlay，然后把 <code>config/telink/chip-module</code> 加入 <code>ZEPHYR_EXTRA_MODULES</code>，最后调用 <code>find_package(Zephyr ...)</code>。这一步可以简单理解为：</p><blockquote><p>“这是一个 Matter 应用，但请使用 Zephyr 的规则来组织整次构建。”</p></blockquote><h2 id="第二步：Kconfig-和-Devicetree-决定“编什么”和“硬件在哪”"><a href="#第二步：Kconfig-和-Devicetree-决定“编什么”和“硬件在哪”" class="headerlink" title="第二步：Kconfig 和 Devicetree 决定“编什么”和“硬件在哪”"></a>第二步：Kconfig 和 Devicetree 决定“编什么”和“硬件在哪”</h2><p>初学者经常把 <code>prj.conf</code> 和 Devicetree 混在一起，可以先这样区分：</p><ul><li><strong>Kconfig &#x2F; <code>prj.conf</code> 决定要不要某个功能。</strong> 例如是否启用 Bluetooth、OpenThread、日志或 MCUboot。</li><li><strong>Devicetree 决定硬件是什么、地址在哪、引脚怎样接。</strong> 例如某个 UART、GPIO 或 Flash 分区的位置。</li></ul><p>CMake 配置阶段会把这些输入转换成后续编译能使用的结果，例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">build/zephyr/.config          # 最终生效的 Kconfig</span><br><span class="line">build/zephyr/zephyr.dts       # 最终合并后的硬件描述</span><br><span class="line">build/zephyr/include/generated/...  # 生成的配置头文件</span><br></pre></td></tr></table></figure><p>所以，改了 <code>prj.conf</code> 或 overlay 后，变化并不是运行时才被“读取”，而是在编译前就决定哪些代码存在、使用哪些地址和参数。</p><h2 id="第三步：Matter-为什么使用-GN，又怎样回到-Zephyr？"><a href="#第三步：Matter-为什么使用-GN，又怎样回到-Zephyr？" class="headerlink" title="第三步：Matter 为什么使用 GN，又怎样回到 Zephyr？"></a>第三步：Matter 为什么使用 GN，又怎样回到 Zephyr？</h2><p>Matter SDK 本身大量使用 GN + Ninja，Zephyr 则主要使用 CMake。Telink 没有要求初学者手工运行两套互不相干的构建，而是在 <code>config/telink/chip-module</code> 中搭了一座桥。</p><p>这座桥主要做三件事：</p><ol><li>把 Zephyr 的编译器、编译选项和 Kconfig 结果转换为 Matter GN 参数；</li><li>通过 CMake <code>ExternalProject</code> 调用 GN 和 Ninja 编译 Matter；</li><li>把生成的 Matter 静态库加入 Zephyr 最终链接。</li></ol><p>因此，中间过程虽然能看到 CMake、GN 和两次 Ninja，但最终目标仍然是同一个应用镜像：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Matter 源码</span><br><span class="line">  -&gt; GN 生成规则</span><br><span class="line">  -&gt; Matter 静态库 ───────────┐</span><br><span class="line">                              │</span><br><span class="line">应用源码 -&gt; 目标文件 ─────────┤</span><br><span class="line">Zephyr   -&gt; 内核与子系统库 ───┼─&gt; zephyr.elf</span><br><span class="line">OpenThread -&gt; 网络协议库 ──────┤</span><br><span class="line">Telink HAL -&gt; 驱动/底层库 ─────┘</span><br></pre></td></tr></table></figure><p>这里的“静态库”可以先理解成装有很多已编译零件的盒子。链接器会从盒子中取出当前程序真正引用的部分，并解析函数之间的调用关系。</p><h2 id="第四步：Zephyr-在哪里调用-Telink-底层？"><a href="#第四步：Zephyr-在哪里调用-Telink-底层？" class="headerlink" title="第四步：Zephyr 在哪里调用 Telink 底层？"></a>第四步：Zephyr 在哪里调用 Telink 底层？</h2><p>上层应用通常调用的是通用 API，例如 Flash、Bluetooth 或 IEEE 802.15.4 API。Zephyr 的 Telink 驱动负责把这些通用动作翻译成具体芯片函数。</p><p>最值得看的不是所有源码，而是下面三条代表性调用链。</p><h3 id="例一：读写-Flash"><a href="#例一：读写-Flash" class="headerlink" title="例一：读写 Flash"></a>例一：读写 Flash</h3><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">应用 / Settings / 存储模块</span><br><span class="line">  ↓ Zephyr Flash API</span><br><span class="line">zephyr/drivers/flash/soc_flash_tlx.c</span><br><span class="line">  ↓</span><br><span class="line">flash_read_page()</span><br><span class="line">flash_write_page()</span><br><span class="line">flash_erase_sector()</span><br><span class="line">  ↓</span><br><span class="line">Telink HAL / 芯片 Flash 驱动</span><br></pre></td></tr></table></figure><p><code>soc_flash_tlx.c</code> 最后注册的是 Zephyr <code>flash_driver_api</code>，但函数内部已经在调用 Telink 的 <code>flash_*</code> API。也就是说，上层看到统一的 Zephyr 接口，底层执行的是 Telink 芯片操作。</p><h3 id="例二：BLE-Host-怎样进入-Telink-Controller"><a href="#例二：BLE-Host-怎样进入-Telink-Controller" class="headerlink" title="例二：BLE Host 怎样进入 Telink Controller"></a>例二：BLE Host 怎样进入 Telink Controller</h3><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Matter commissioning / Zephyr Bluetooth Host</span><br><span class="line">  ↓ HCI 命令和数据</span><br><span class="line">zephyr/drivers/bluetooth/hci/hci_tlx.c</span><br><span class="line">  ↓</span><br><span class="line">tlx_bt_controller_init()</span><br><span class="line">tlx_bt_host_send_packet()</span><br><span class="line">  ↓</span><br><span class="line">Telink BLE Controller 与射频底层</span><br></pre></td></tr></table></figure><p>反方向收到 HCI Event 或 ACL 数据时，Telink 适配层会把数据送回 Zephyr 的 <code>bt_recv()</code>。所以这条链是双向的：Zephyr Host 向下发命令，Controller 向上交事件。</p><p>这也解释了一个常见现象：应用和 GATT 代码可能都在 Matter&#x2F;Zephyr 上层，但 BLE Controller 的核心实现未必位于同一个源码目录。</p><h3 id="例三：OpenThread-怎样使用-Telink-2-4-GHz-射频"><a href="#例三：OpenThread-怎样使用-Telink-2-4-GHz-射频" class="headerlink" title="例三：OpenThread 怎样使用 Telink 2.4 GHz 射频"></a>例三：OpenThread 怎样使用 Telink 2.4 GHz 射频</h3><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Matter</span><br><span class="line">  ↓</span><br><span class="line">OpenThread</span><br><span class="line">  ↓ Zephyr IEEE 802.15.4 Radio API</span><br><span class="line">zephyr/drivers/ieee802154/ieee802154_tlx.c</span><br><span class="line">  ↓</span><br><span class="line">rf_set_chn()</span><br><span class="line">rf_set_rxmode()</span><br><span class="line">rf_set_txmode()</span><br><span class="line">rf_tx_pkt()</span><br><span class="line">  ↓</span><br><span class="line">Telink RF / DMA / IRQ 底层</span><br></pre></td></tr></table></figure><p>OpenThread 负责 Thread 协议，但它不会自己操作 Telink 的射频寄存器。Zephyr 的 IEEE 802.15.4 驱动实现标准 Radio API，再调用 Telink 的 RF、DMA、定时器和中断接口。</p><p>把三条链放在一起，就能看到稳定的分层：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">产品 / Matter / OpenThread</span><br><span class="line">        ↓ 通用接口</span><br><span class="line">Zephyr 子系统和 driver API</span><br><span class="line">        ↓ 芯片适配</span><br><span class="line">Telink Zephyr driver</span><br><span class="line">        ↓ 厂商 API</span><br><span class="line">Telink HAL、驱动源码或静态库</span><br><span class="line">        ↓</span><br><span class="line">芯片外设与射频</span><br></pre></td></tr></table></figure><h2 id="“底下还有个库”到底是哪一个？"><a href="#“底下还有个库”到底是哪一个？" class="headerlink" title="“底下还有个库”到底是哪一个？"></a>“底下还有个库”到底是哪一个？</h2><p>这个记忆很可能是对的，但要区分两类库。</p><h3 id="Telink-HAL-与-BLE-芯片底层库"><a href="#Telink-HAL-与-BLE-芯片底层库" class="headerlink" title="Telink HAL 与 BLE&#x2F;芯片底层库"></a>Telink HAL 与 BLE&#x2F;芯片底层库</h3><p>Zephyr 的 manifest 会把 <code>hal_telink</code> 放到 <code>modules/hal/telink</code>。较新的公开 HAL 结构中，<code>hal_v2</code> 还会获取 Telink 的公开 <code>tl_ble_sdk</code> 仓库，并针对目标 SoC 链接对应的静态库。</p><p>以 TL323X 为例，公开文件中可以看到两种容易遇到的命名：</p><ul><li>原始 SDK 的 <code>proj_lib/liblt_TL323X.a</code>；</li><li>Zephyr 集成流程中的 <code>lib_zephyr_tl323x.a</code>。</li></ul><p>具体名字和生成方式会随分支、SDK 发布版发生变化，不要只靠文件名判断版本。更可靠的方法是查看当前 <code>hal_telink</code> 的 <code>CMakeLists.txt</code>、实际链接命令和最终 map 文件。</p><p>同时也不要把 <code>.a</code> 理解成“整个底层只有一个黑盒”。公开 <code>tl_ble_sdk</code> 中还能看到 GPIO、Flash、UART、I2C 等驱动源码和头文件；某些协议栈、控制器或优化实现则可能以静态库参与链接。<strong>仓库可以公开下载，不等于其中每个库都有完整可读源码，也不自动代表所有内容使用同一种开源许可。</strong></p><h3 id="可选的-OpenThread-Telink-库"><a href="#可选的-OpenThread-Telink-库" class="headerlink" title="可选的 OpenThread Telink 库"></a>可选的 OpenThread Telink 库</h3><p><code>openthread_telink_lib</code> 是另一件东西。它公开提供 OpenThread 头文件以及类似下面的库：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">libopenthread-ftd-extended.a</span><br><span class="line">libopenthread-ftd-reduced.a</span><br></pre></td></tr></table></figure><p>只有相应 Kconfig 选项开启时，模块的 CMake 才会选择并链接它。它解决的是 OpenThread 库实现选择，不是 BLE Controller 库，也不等于 Telink 全部 HAL。</p><p>因此，当日志或 map 文件里看到一个 <code>.a</code>，先问三个问题：</p><ol><li>它是芯片驱动&#x2F;BLE Controller，还是 OpenThread？</li><li>是当前配置主动选择的，还是只存在于工作区但没有参与链接？</li><li>它有哪些公开头文件和源码，哪些实现只有二进制？</li></ol><h2 id="第五步：链接器怎样变出一个完整程序？"><a href="#第五步：链接器怎样变出一个完整程序？" class="headerlink" title="第五步：链接器怎样变出一个完整程序？"></a>第五步：链接器怎样变出一个完整程序？</h2><p>编译器通常一次只编译一个源码文件，产生许多 <code>.o</code> 目标文件；Matter、Zephyr、OpenThread 和 HAL 也可能先整理成多个 <code>.a</code> 静态库。</p><p>链接阶段会：</p><ul><li>找到 <code>main</code> 和系统启动入口；</li><li>解析一个函数对另一个函数的引用；</li><li>按 linker script 安排代码、只读数据、RAM 数据和保留区；</li><li>丢弃没有被使用的部分；</li><li>生成带符号和段信息的 <code>zephyr.elf</code>。</li></ul><p>所以“完整固件”的第一次成形发生在链接阶段，而不是最后复制 BIN 时。</p><p>想确认某个底层函数有没有真的进入固件，可以查看：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">build/zephyr/zephyr.map</span><br><span class="line">build/zephyr/zephyr.elf</span><br></pre></td></tr></table></figure><p>map 文件适合追踪“某个符号来自哪个 <code>.o</code> 或 <code>.a</code>”；ELF 则可以配合 <code>nm</code>、<code>readelf</code>、<code>objdump</code> 和调试器继续分析。</p><h2 id="第六步：ELF、BIN-和-merged-bin-有什么区别？"><a href="#第六步：ELF、BIN-和-merged-bin-有什么区别？" class="headerlink" title="第六步：ELF、BIN 和 merged.bin 有什么区别？"></a>第六步：ELF、BIN 和 merged.bin 有什么区别？</h2><p>常见产物可以这样理解：</p><table><thead><tr><th>文件</th><th>主要用途</th></tr></thead><tbody><tr><td><code>zephyr.elf</code></td><td>包含代码、数据、地址和调试符号，适合调试与分析</td></tr><tr><td><code>zephyr.bin</code></td><td>从 ELF 中提取出的原始二进制，常用于烧录</td></tr><tr><td><code>zephyr.map</code></td><td>记录内存布局、符号和来源，适合查大小与调用来源</td></tr><tr><td><code>merged.bin</code></td><td>Telink 后处理得到的最终入口文件；内容取决于配置</td></tr></tbody></table><p>公开的 Telink <code>process_binaries.py</code> 中，如果当前配置没有额外镜像需要合并，<code>merged.bin</code> 可以只是指向 <code>zephyr.bin</code> 的链接。启用不同功能后，它也可能负责：</p><ul><li>把 MCUboot 与签名后的应用放到各自 Flash offset；</li><li>合入出厂数据分区；</li><li>生成 OTA 或 DFU 需要的文件。</li></ul><p>因此，看到 <code>merged.bin</code> 不能立刻推断它一定包含多个镜像；看到只有一个 <code>zephyr.bin</code>，也不能推断它内部只有 Zephyr。Matter、OpenThread 和应用通常早已在 ELF 链接阶段进入其中。</p><h2 id="一个最实用的排查顺序"><a href="#一个最实用的排查顺序" class="headerlink" title="一个最实用的排查顺序"></a>一个最实用的排查顺序</h2><p>如果想亲手确认构建过程，不必一开始读遍所有仓库。按下面顺序就够了：</p><ol><li>看应用 <code>CMakeLists.txt</code>、<code>prj.conf</code> 和 board overlay，确认构建入口；</li><li>看 <code>build/zephyr/.config</code> 与 <code>zephyr.dts</code>，确认最终配置；</li><li>在构建日志或 <code>build.ninja</code> 中找 <code>chip-gn</code>，确认 Matter 子构建；</li><li>在 <code>zephyr.map</code> 中搜索目标函数或 <code>.a</code> 文件名，确认它是否进入最终 ELF；</li><li>看 <code>process_binaries.py</code> 的日志和输出，确认是否又做了签名或镜像合并。</li></ol><p>定位某个外设时，再沿着一条窄链向下追：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">应用调用</span><br><span class="line">  -&gt; Zephyr API</span><br><span class="line">  -&gt; Telink Zephyr driver</span><br><span class="line">  -&gt; Telink 头文件中的函数声明</span><br><span class="line">  -&gt; 对应 .c 或 .a</span><br><span class="line">  -&gt; 最终 map 文件中的来源</span><br></pre></td></tr></table></figure><p>这样比在所有仓库里搜索“Telink”更容易建立完整证据。</p><h2 id="初学者最容易混淆的六件事"><a href="#初学者最容易混淆的六件事" class="headerlink" title="初学者最容易混淆的六件事"></a>初学者最容易混淆的六件事</h2><h3 id="1-west-不是编译器"><a href="#1-west-不是编译器" class="headerlink" title="1. west 不是编译器"></a>1. <code>west</code> 不是编译器</h3><p>它主要管理工作区并调用构建系统。真正完成配置、编译和链接的是 CMake、GN、Ninja 与工具链。</p><h3 id="2-Matter-不是另一个独立-RTOS"><a href="#2-Matter-不是另一个独立-RTOS" class="headerlink" title="2. Matter 不是另一个独立 RTOS"></a>2. Matter 不是另一个独立 RTOS</h3><p>在这个平台组合里，Matter 使用 Zephyr 提供的线程、网络、存储、Bluetooth 和驱动能力。</p><h3 id="3-Matter-库不是第二个可单独运行的-BIN"><a href="#3-Matter-库不是第二个可单独运行的-BIN" class="headerlink" title="3. Matter 库不是第二个可单独运行的 BIN"></a>3. Matter 库不是第二个可单独运行的 BIN</h3><p>它通常作为库参与最终链接。只有多核或特殊架构才可能出现额外可执行镜像，不能把特殊情况当成普通流程。</p><h3 id="4-west-update-不等于所有东西都来自-Zephyr-官方仓库"><a href="#4-west-update-不等于所有东西都来自-Zephyr-官方仓库" class="headerlink" title="4. west update 不等于所有东西都来自 Zephyr 官方仓库"></a>4. <code>west update</code> 不等于所有东西都来自 Zephyr 官方仓库</h3><p>它会按照 manifest 拉取多个项目，其中可以包含 Telink fork、HAL 和其他模块。</p><h3 id="5-工作区里有某个库，不代表它进入了当前固件"><a href="#5-工作区里有某个库，不代表它进入了当前固件" class="headerlink" title="5. 工作区里有某个库，不代表它进入了当前固件"></a>5. 工作区里有某个库，不代表它进入了当前固件</h3><p>Kconfig、CMake 条件和链接器引用共同决定它是否参与构建。最终以配置、链接命令和 map 文件为准。</p><h3 id="6-编译成功不等于设备行为已经验证"><a href="#6-编译成功不等于设备行为已经验证" class="headerlink" title="6. 编译成功不等于设备行为已经验证"></a>6. 编译成功不等于设备行为已经验证</h3><p>编译和链接只能证明静态组合成立。启动、配网、BLE、Thread、Flash、低功耗和 OTA 仍需要真机日志、抓包或功耗测量。</p><h2 id="最后用一句话串起来"><a href="#最后用一句话串起来" class="headerlink" title="最后用一句话串起来"></a>最后用一句话串起来</h2><p>一次普通的 Telink Matter 构建，可以浓缩成：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">west 找齐工作区</span><br><span class="line">  -&gt; CMake 组织 Zephyr 应用</span><br><span class="line">  -&gt; Kconfig 和 Devicetree 确定配置</span><br><span class="line">  -&gt; GN/Ninja 编译 Matter</span><br><span class="line">  -&gt; Ninja 编译其余模块</span><br><span class="line">  -&gt; 链接器把应用、Matter、Zephyr、OpenThread 和 Telink 底层装进 zephyr.elf</span><br><span class="line">  -&gt; 转成 zephyr.bin</span><br><span class="line">  -&gt; 需要时再签名或合并为最终镜像</span><br></pre></td></tr></table></figure><p>以后再面对庞大的 SDK，不需要先记住每个目录。先抓住“谁提供通用接口、谁做芯片适配、谁在最终链接中出现”这三件事，整个构建链就不会再像一个黑盒。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://doc.telink-semi.cn/doc/en/software/res/sdk/matter/telink_matter_developer_guide_en/">Telink Matter Developer Guide</a></li><li><a href="https://docs.zephyrproject.org/latest/develop/application/index.html">Zephyr：Application Development</a></li><li><a href="https://docs.zephyrproject.org/latest/build/cmake/index.html">Zephyr：Build System</a></li><li><a href="https://docs.zephyrproject.org/latest/develop/modules.html">Zephyr：Modules</a></li><li><a href="https://github.com/project-chip/connectedhomeip">Matter SDK：connectedhomeip</a></li><li><a href="https://github.com/project-chip/connectedhomeip/blob/master/config/telink/chip-module/CMakeLists.txt">Matter Telink 构建桥接模块：CMakeLists.txt</a></li><li><a href="https://github.com/project-chip/connectedhomeip/tree/master/src/platform/telink">Matter Telink 平台实现</a></li><li><a href="https://github.com/project-chip/connectedhomeip/blob/master/scripts/tools/telink/process_binaries.py">Matter Telink 镜像后处理：process_binaries.py</a></li><li><a href="https://github.com/telink-semi/zephyr">Telink Zephyr Fork</a></li><li><a href="https://github.com/telink-semi/zephyr/blob/develop_v3.3/drivers/flash/soc_flash_tlx.c">Telink Zephyr Flash Driver</a></li><li><a href="https://github.com/telink-semi/zephyr/blob/develop_v3.3/drivers/bluetooth/hci/hci_tlx.c">Telink Zephyr Bluetooth HCI Driver</a></li><li><a href="https://github.com/telink-semi/zephyr/blob/develop_v3.3/drivers/ieee802154/ieee802154_tlx.c">Telink Zephyr IEEE 802.15.4 Driver</a></li><li><a href="https://github.com/telink-semi/hal_telink">Telink HAL</a></li><li><a href="https://github.com/telink-semi/tl_ble_sdk">Telink BLE SDK</a></li><li><a href="https://github.com/telink-semi/openthread_telink_lib">Telink OpenThread Library</a></li></ul>]]>
    </content>
    <id>https://oniums.github.io/posts/telink-zephyr-matter-build-pipeline/</id>
    <link href="https://oniums.github.io/posts/telink-zephyr-matter-build-pipeline/"/>
    <published>2026-08-02T15:55:00.000Z</published>
    <summary>
      <![CDATA[<p>第一次接触 Telink Matter SDK 时，很容易产生一个疑问：Zephyr 在一个仓库，Matter 在另一个仓库，底下还有 HAL、OpenThread 和一些静态库，执行一次 <code>west build</code> 后，它们怎么就变成了一个固件？</p>
<p>先记住最重要的结论：</p>
<blockquote>
<p><strong>通常不是先生成一个 Zephyr 固件和一个 Matter 固件，再把两个 BIN 拼起来。Matter、Zephyr、应用和芯片底层会先分别编译成许多目标文件或静态库，最后由链接器装进同一个 <code>zephyr.elf</code>，再转换成 <code>zephyr.bin</code>。</strong></p>
</blockquote>]]>
    </summary>
    <title>Telink Matter SDK 入门：Zephyr、Matter 与底层库怎样组成一个固件？</title>
    <updated>2026-08-02T16:07:55.768Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Bluetooth LE" scheme="https://oniums.github.io/tags/bluetooth-le/"/>
    <category term="BLE" scheme="https://oniums.github.io/tags/ble/"/>
    <category term="GATT" scheme="https://oniums.github.io/tags/gatt/"/>
    <category term="ATT" scheme="https://oniums.github.io/tags/att/"/>
    <category term="入门" scheme="https://oniums.github.io/tags/%E5%85%A5%E9%97%A8/"/>
    <content>
      <![CDATA[<p>打开一个蓝牙调试工具，点下 Connect，页面很快显示“Connected”。这是不是说明手机已经能读到设备数据，也能正常控制设备了？</p><p>不一定。</p><p><strong>BLE 连接成功，只表示两台设备已经建立了一条无线链路。</strong> 后面通常还要发现 Service、找到 Characteristic、确认读写方式、订阅通知，应用数据才真正开始流动。</p><span id="more"></span><p>本文不从复杂的协议分层开始，而是跟着一台简单的 BLE 温度计，看清手机从“发现设备”到“收到温度变化”之间发生了什么。</p><p>读完后，你应该能够回答三个问题：</p><ol><li>Service、Characteristic 和 Descriptor 分别是什么？</li><li>BLE 显示 Connected 后，手机为什么还要继续发现和订阅？</li><li>怎样判断问题卡在连接、GATT，还是设备自己的业务协议？</li></ol><h2 id="先看结论：一次常见的-BLE-通信过程"><a href="#先看结论：一次常见的-BLE-通信过程" class="headerlink" title="先看结论：一次常见的 BLE 通信过程"></a>先看结论：一次常见的 BLE 通信过程</h2><p>先不管每个名词的精确定义，只看一条最常见的时间线：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">温度计发送广播</span><br><span class="line">  ↓</span><br><span class="line">手机扫描并发现温度计</span><br><span class="line">  ↓</span><br><span class="line">手机发起连接</span><br><span class="line">  ↓</span><br><span class="line">BLE 链路建立：Connected</span><br><span class="line">  ↓</span><br><span class="line">双方可能进行配对、加密和 MTU 交换</span><br><span class="line">  ↓</span><br><span class="line">手机发现设备提供的 Service</span><br><span class="line">  ↓</span><br><span class="line">手机发现 Service 里的 Characteristic 和 Descriptor</span><br><span class="line">  ↓</span><br><span class="line">手机读取数据、写入命令，或者订阅通知</span><br><span class="line">  ↓</span><br><span class="line">温度变化时，设备主动向手机发送新值</span><br></pre></td></tr></table></figure><p>这里最容易混淆的是中间那条线：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">建立 BLE 连接 ≠ 已经找到 GATT 服务 ≠ 已经订阅数据 ≠ 业务功能正常</span><br></pre></td></tr></table></figure><p>每一步成功，都只能证明这一小步已经完成。</p><h2 id="连接前：广播和扫描是在做什么？"><a href="#连接前：广播和扫描是在做什么？" class="headerlink" title="连接前：广播和扫描是在做什么？"></a>连接前：广播和扫描是在做什么？</h2><p>BLE 设备在没有连接时，可以周期性发送很短的广播数据。广播通常可能包含：</p><ul><li>设备名称；</li><li>设备地址或身份相关信息；</li><li>某些重要 Service 的 UUID；</li><li>厂商自定义数据；</li><li>设备是否允许连接等信息。</li></ul><p>温度计不断发送广播，就像它在说：</p><blockquote><p>“我在这里，我叫客厅温度计，现在可以连接。”</p></blockquote><p>手机进行扫描，是在附近收听这些广播。收到广播，只证明手机“看见”了设备，并不代表双方已经连接。</p><p>当手机决定连接时，它会向目标设备发起连接请求。连接建立后，双方按照约定的时间间隔交换无线数据。此时调试工具通常会显示 Connected。</p><h3 id="三组角色不要混在一起"><a href="#三组角色不要混在一起" class="headerlink" title="三组角色不要混在一起"></a>三组角色不要混在一起</h3><p>BLE 中会遇到几组看起来相似的角色：</p><table><thead><tr><th>所处阶段</th><th>角色</th><th>简单理解</th></tr></thead><tbody><tr><td>广播阶段</td><td>Advertiser &#x2F; Scanner</td><td>一个发送广播，一个扫描广播</td></tr><tr><td>连接阶段</td><td>Peripheral &#x2F; Central</td><td>一个接受连接，一个发起连接</td></tr><tr><td>GATT 阶段</td><td>Server &#x2F; Client</td><td>一个提供数据，一个访问数据</td></tr></tbody></table><p>在常见的手机连接传感器场景中：</p><ul><li>手机通常是 Central，也是 GATT Client；</li><li>传感器通常是 Peripheral，也是 GATT Server。</li></ul><p>但这只是常见组合，不是强制绑定。Central 不一定永远是 GATT Client，Peripheral 也不一定永远是 GATT Server。</p><h2 id="连接后：GATT-像一组整理好的资料柜"><a href="#连接后：GATT-像一组整理好的资料柜" class="headerlink" title="连接后：GATT 像一组整理好的资料柜"></a>连接后：GATT 像一组整理好的资料柜</h2><p>连接只解决“怎样把数据送到对方”。设备究竟提供哪些数据、哪些可以读取、哪些可以写入，则主要由 GATT 描述。</p><p>可以把一台 GATT Server 想成一个资料柜：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">GATT Server</span><br><span class="line">├── Service：电池</span><br><span class="line">│   └── Characteristic：当前电量</span><br><span class="line">├── Service：设备信息</span><br><span class="line">│   ├── Characteristic：厂商名称</span><br><span class="line">│   └── Characteristic：固件版本</span><br><span class="line">└── Service：温度计功能</span><br><span class="line">    ├── Characteristic：当前温度</span><br><span class="line">    └── Characteristic：测量间隔</span><br></pre></td></tr></table></figure><p>Service、Characteristic 和 Descriptor，就是整理这个资料柜的三种基本结构。</p><h3 id="Service：一组相关功能"><a href="#Service：一组相关功能" class="headerlink" title="Service：一组相关功能"></a>Service：一组相关功能</h3><p>Service 用来把同一类功能放在一起，可以先把它理解成一个文件夹。</p><p>常见的标准 Service 有：</p><table><thead><tr><th>Service</th><th>用途</th></tr></thead><tbody><tr><td>Generic Access</td><td>设备名称、外观等基础访问数据</td></tr><tr><td>Generic Attribute</td><td>GATT 数据库自身发生变化时的相关能力</td></tr><tr><td>Device Information</td><td>厂商、型号、序列号、固件版本等设备信息</td></tr><tr><td>Battery Service</td><td>电池电量和电池状态</td></tr></tbody></table><p>设备也可以定义自己的 Service。例如，一台温度计可以使用自定义 Service 表达测量结果和设置项。</p><h3 id="Characteristic：真正要读写的数据项"><a href="#Characteristic：真正要读写的数据项" class="headerlink" title="Characteristic：真正要读写的数据项"></a>Characteristic：真正要读写的数据项</h3><p>Characteristic 是 Service 中具体的数据项。它通常包含：</p><ul><li>UUID：这是什么类型的数据；</li><li>Value：当前数据值；</li><li>Properties：允许怎样操作，例如 Read、Write、Notify；</li><li>Descriptor：对这个数据项的补充说明或配置。</li></ul><p>Properties 回答“支持什么操作”，Permissions 则回答“满足什么安全条件才允许操作”。所以，一个 Characteristic 即使带有 Read 属性，也可能要求链路先加密才能读取。</p><p>例如，标准 Battery Service 的 UUID 是 <code>0x180F</code>，其中 Battery Level Characteristic 的 UUID 是 <code>0x2A19</code>。如果它当前的 Value 是十进制 <code>83</code>，就表示剩余电量为 83%。</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Battery Service（0x180F）</span><br><span class="line">└── Battery Level Characteristic（0x2A19）</span><br><span class="line">    ├── Value：83</span><br><span class="line">    ├── Properties：Read、Notify</span><br><span class="line">    └── Descriptor：通知开关</span><br></pre></td></tr></table></figure><p>标准功能通常使用 Bluetooth SIG 分配的 UUID。产品自己的功能则常使用 128-bit 自定义 UUID。</p><h3 id="Descriptor：Characteristic-的补充信息和开关"><a href="#Descriptor：Characteristic-的补充信息和开关" class="headerlink" title="Descriptor：Characteristic 的补充信息和开关"></a>Descriptor：Characteristic 的补充信息和开关</h3><p>Descriptor 属于某个 Characteristic，可以描述数据格式，也可以控制它的行为。</p><p>初学者最常见的是 Client Characteristic Configuration Descriptor，通常简称 <strong>CCCD</strong>，UUID 为 <code>0x2902</code>。</p><p>如果一个 Characteristic 支持 Notify 或 Indicate，Client 通常要先写入 CCCD，告诉 Server：</p><blockquote><p>“这个数据变化时，请主动发给我。”</p></blockquote><p>因此，看见 Characteristic 支持 Notify，不等于手机已经能收到通知。<strong>支持通知是一种能力，写入 CCCD 才是实际订阅。</strong></p><h2 id="UUID-和-Handle-有什么区别？"><a href="#UUID-和-Handle-有什么区别？" class="headerlink" title="UUID 和 Handle 有什么区别？"></a>UUID 和 Handle 有什么区别？</h2><p>调试工具里经常同时显示 UUID 和 Handle，它们不是一回事。</p><table><thead><tr><th>名称</th><th>用途</th><th>类比</th></tr></thead><tbody><tr><td>UUID</td><td>说明这个 Attribute 是什么类型</td><td>资料名称</td></tr><tr><td>Handle</td><td>指向当前 GATT 数据库中的具体条目</td><td>资料柜里的编号</td></tr></tbody></table><p>同一种标准 Characteristic 在不同设备上可以使用相同 UUID，但它在各自数据库里的 Handle 不一定相同。</p><p>应用通常先通过 UUID 发现目标，再使用发现到的 Handle 访问它。不要因为某次连接中 Handle 是 <code>0x0012</code>，就假设所有设备和所有固件版本都永远相同。</p><h2 id="Read、Write、Notify-和-Indicate"><a href="#Read、Write、Notify-和-Indicate" class="headerlink" title="Read、Write、Notify 和 Indicate"></a>Read、Write、Notify 和 Indicate</h2><p>找到 Characteristic 后，双方常用下面几种方式交换数据：</p><table><thead><tr><th>操作</th><th>谁先发起</th><th>简单理解</th></tr></thead><tbody><tr><td>Read</td><td>Client</td><td>“把现在的值告诉我”</td></tr><tr><td>Write</td><td>Client</td><td>“请把这个值改成我发送的内容”，并等待 GATT 层响应</td></tr><tr><td>Write Without Response</td><td>Client</td><td>“快速写入这个内容”，不等待对应的 GATT 写响应</td></tr><tr><td>Notify</td><td>Server</td><td>“数据变了，我主动告诉你”，不要求 ATT 层确认</td></tr><tr><td>Indicate</td><td>Server</td><td>“数据变了，我主动告诉你”，要求 Client 在 ATT 层确认</td></tr></tbody></table><p>假设温度计每次变化都要上报：</p><ol><li>手机发现温度 Characteristic；</li><li>手机写入它的 CCCD，开启 Notify；</li><li>温度从 24.1°C 变为 24.3°C；</li><li>设备发送 Notification；</li><li>手机收到新值并更新页面。</li></ol><p>如果第 2 步没有完成，设备即使一直在测温，手机也可能什么都收不到。</p><p>Notify 没有 ATT 层的逐条确认，Indicate 则有。这里说的是 GATT&#x2F;ATT 这一层，不表示底层无线链路完全没有校验或重传。</p><h2 id="ATT-又是什么？它和-GATT-有什么关系？"><a href="#ATT-又是什么？它和-GATT-有什么关系？" class="headerlink" title="ATT 又是什么？它和 GATT 有什么关系？"></a>ATT 又是什么？它和 GATT 有什么关系？</h2><p>可以先记住一句话：</p><blockquote><p>GATT 负责把数据组织成 Service、Characteristic 和 Descriptor；ATT 负责发现、读取、写入和传送这些 Attribute。</p></blockquote><p>如果 GATT 像餐厅菜单，ATT 就像点单和上菜时使用的规则：</p><ul><li>菜单怎样分组、一道菜叫什么，由 GATT 描述；</li><li>“读取这个值”“写入那个值”“这个值变化了”等消息怎样发送，由 ATT 处理。</li></ul><p>开发时经常直接使用平台提供的 GATT API，不需要手工拼每一个 ATT 数据包。但抓包、看日志或定位超时时，ATT Read、Write、Notification、MTU Exchange 等名字就会出现。</p><h2 id="MTU：一次-ATT-消息能装多少"><a href="#MTU：一次-ATT-消息能装多少" class="headerlink" title="MTU：一次 ATT 消息能装多少"></a>MTU：一次 ATT 消息能装多少</h2><p>MTU 可以先理解成“一次允许携带多大的包裹”。BLE 的默认 ATT MTU 是 23 字节，但这 23 字节还包含 ATT 自己的字段，并不全是应用数据。</p><p>连接后，Client 和 Server 可以交换各自支持的 MTU，最终使用双方都能接受的大小。更大的 MTU 通常能让较长数据减少拆分，但需要注意：</p><ul><li>MTU 交换成功，不代表 Service 已经发现；</li><li>MTU 更大，不代表通知已经订阅；</li><li>MTU 更大，也不保证业务一定更快；</li><li>真正能放入的 Characteristic Value 通常小于 ATT MTU。</li></ul><p>初学阶段不用急着计算每一层开销。先把 MTU 当成连接建立后可能进行的一项“传输能力协商”即可。</p><h2 id="配对、绑定和加密是不是每次都有？"><a href="#配对、绑定和加密是不是每次都有？" class="headerlink" title="配对、绑定和加密是不是每次都有？"></a>配对、绑定和加密是不是每次都有？</h2><p>不一定。</p><p>有些 Characteristic 允许连接后直接读取；有些数据要求链路加密，甚至要求经过身份验证。此时双方可能进行配对，生成安全密钥并启用加密。</p><ul><li>Pairing：协商安全能力并生成密钥；</li><li>Bonding：把密钥保存下来，方便以后重连；</li><li>Encryption：使用密钥保护当前链路中的数据。</li></ul><p>它们有关联，但不是同义词。<strong>BLE Connected 也不自动等于已经配对、已经绑定或已经加密。</strong></p><p>如果读取某个 Characteristic 时提示权限或认证错误，应检查它的访问权限和当前安全级别，而不是只看连接状态。</p><h2 id="从连接到收到数据，逐步证明了什么？"><a href="#从连接到收到数据，逐步证明了什么？" class="headerlink" title="从连接到收到数据，逐步证明了什么？"></a>从连接到收到数据，逐步证明了什么？</h2><p>下面这张表很适合在调试时使用：</p><table><thead><tr><th>看到的现象</th><th>能证明什么</th><th>还不能证明什么</th></tr></thead><tbody><tr><td>扫描到设备</td><td>广播可被接收，手机能发现它</td><td>可以建立连接</td></tr><tr><td>Connected</td><td>BLE 链路已经建立</td><td>GATT Service 正常、业务正常</td></tr><tr><td>MTU Exchange 完成</td><td>双方协商了 ATT 消息大小</td><td>Service 已发现、吞吐一定更高</td></tr><tr><td>找到目标 Service</td><td>GATT Client 发现了这组功能</td><td>目标 Characteristic 可正常使用</td></tr><tr><td>找到 Characteristic</td><td>UUID 和 Handle 已发现</td><td>权限满足、数据格式正确</td></tr><tr><td>CCCD 已写为开启值</td><td>Client 已请求 Notify 或 Indicate</td><td>Server 一定会产生业务数据</td></tr><tr><td>收到第一条 Notification</td><td>Server 到 Client 的这条数据路径已跑通</td><td>所有命令、异常和重连都正常</td></tr><tr><td>一次完整且被正确解析的读写成功</td><td>本次操作的业务格式和方向基本正确</td><td>长时间稳定性和所有边界情况正常</td></tr></tbody></table><p>所以，“连接上了但不能用”并不矛盾。它只是说明故障范围已经从广播和连接阶段，缩小到了后续阶段。</p><h2 id="初学者最容易遇到的五个误区"><a href="#初学者最容易遇到的五个误区" class="headerlink" title="初学者最容易遇到的五个误区"></a>初学者最容易遇到的五个误区</h2><h3 id="误区一：广播里有-Service-UUID，连接后就一定能用"><a href="#误区一：广播里有-Service-UUID，连接后就一定能用" class="headerlink" title="误区一：广播里有 Service UUID，连接后就一定能用"></a>误区一：广播里有 Service UUID，连接后就一定能用</h3><p>广播中的 Service UUID 主要帮助扫描端识别设备。真正的 Service、Characteristic、权限和数据仍要在连接后通过 GATT 访问。</p><h3 id="误区二：能-Read，就一定能-Notify"><a href="#误区二：能-Read，就一定能-Notify" class="headerlink" title="误区二：能 Read，就一定能 Notify"></a>误区二：能 Read，就一定能 Notify</h3><p>Read 和 Notify 是不同能力。Notify 还需要 Client 完成订阅，Server 也要在合适时机主动发送数据。</p><h3 id="误区三：Notify-和-Indicate-完全一样"><a href="#误区三：Notify-和-Indicate-完全一样" class="headerlink" title="误区三：Notify 和 Indicate 完全一样"></a>误区三：Notify 和 Indicate 完全一样</h3><p>两者都由 Server 主动发送，但 Indicate 要求 ATT 层确认，Notify 不要求。具体选择要看实时性、流量和可靠性需求。</p><h3 id="误区四：UUID-就是数据内容"><a href="#误区四：UUID-就是数据内容" class="headerlink" title="误区四：UUID 就是数据内容"></a>误区四：UUID 就是数据内容</h3><p>UUID 只说明数据类型，真正内容在 Value 中。Value 往往是一串字节，还要按照该 Characteristic 的格式解释。</p><h3 id="误区五：MTU-越大，速度一定越快"><a href="#误区五：MTU-越大，速度一定越快" class="headerlink" title="误区五：MTU 越大，速度一定越快"></a>误区五：MTU 越大，速度一定越快</h3><p>MTU 只是影响一次 ATT 消息可以携带的大小。实际速度还受到连接间隔、PHY、数据长度、设备处理速度和应用交互方式等因素影响。</p><h2 id="最简单的动手顺序"><a href="#最简单的动手顺序" class="headerlink" title="最简单的动手顺序"></a>最简单的动手顺序</h2><p>准备一台支持 BLE 的设备和一个能够查看 GATT 的调试工具，然后只做下面几步：</p><ol><li>扫描设备，观察名称、信号强度和广播内容；</li><li>建立连接，确认 Connected；</li><li>展开 Service 列表，先找 Device Information 和 Battery Service；</li><li>查看 Characteristic 的 UUID、Properties 和 Value；</li><li>对允许 Read 的 Characteristic 执行一次读取；</li><li>对允许 Notify 的 Characteristic 开启订阅，观察 CCCD 和后续数据；</li><li>断开再连接一次，观察哪些步骤需要重新执行。</li></ol><p>不要随意向含义未知的 Characteristic 写入数据。它可能代表重启、清空数据、进入升级模式或其他控制命令。</p><p>第一次动手的目标不是记住所有 UUID，而是建立一条判断链：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">能扫描到吗？</span><br><span class="line">  -&gt; 能连接吗？</span><br><span class="line">  -&gt; 能发现目标 Service 吗？</span><br><span class="line">  -&gt; 能找到目标 Characteristic 吗？</span><br><span class="line">  -&gt; 权限和安全条件满足吗？</span><br><span class="line">  -&gt; CCCD 订阅成功吗？</span><br><span class="line">  -&gt; 第一条读、写或通知出现了吗？</span><br></pre></td></tr></table></figure><h2 id="一页小抄"><a href="#一页小抄" class="headerlink" title="一页小抄"></a>一页小抄</h2><table><thead><tr><th>名词</th><th>一句话理解</th></tr></thead><tbody><tr><td>Advertising</td><td>设备在连接前发送“我在这里”等信息</td></tr><tr><td>Scanning</td><td>手机收听附近广播</td></tr><tr><td>Connection</td><td>双方建立可持续交换数据的 BLE 链路</td></tr><tr><td>GATT Server</td><td>保存并提供 GATT 数据库的一方</td></tr><tr><td>GATT Client</td><td>发现、读取、写入和订阅数据的一方</td></tr><tr><td>Service</td><td>一组相关功能</td></tr><tr><td>Characteristic</td><td>一个具体数据项及其操作方式</td></tr><tr><td>Descriptor</td><td>Characteristic 的补充说明或配置</td></tr><tr><td>UUID</td><td>Attribute 的类型标识</td></tr><tr><td>Handle</td><td>GATT 数据库中具体条目的编号</td></tr><tr><td>CCCD</td><td>Client 用来开启或关闭 Notify、Indicate 的配置项</td></tr><tr><td>ATT</td><td>操作和传送 Attribute 的底层协议</td></tr><tr><td>MTU</td><td>单个 ATT PDU 允许的最大尺寸</td></tr></tbody></table><h2 id="最后再看一次完整过程"><a href="#最后再看一次完整过程" class="headerlink" title="最后再看一次完整过程"></a>最后再看一次完整过程</h2><p>一台 BLE 设备“真正可用”，通常不是一个瞬间，而是一串连续的小成功：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">广播可见</span><br><span class="line">  -&gt; 连接建立</span><br><span class="line">  -&gt; 安全条件满足</span><br><span class="line">  -&gt; 如有需要，完成 MTU 等传输参数协商</span><br><span class="line">  -&gt; Service / Characteristic / Descriptor 被发现</span><br><span class="line">  -&gt; Read、Write 或 CCCD 订阅完成</span><br><span class="line">  -&gt; 第一条业务数据成功传输</span><br></pre></td></tr></table></figure><p>以后再看到“BLE 已连接但没有数据”，不要把所有问题都叫作“连接失败”。沿着这条时间线向下检查，很快就能知道自己是在找不到服务、订阅没有完成，还是业务命令根本没有开始。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://www.bluetooth.com/bluetooth-le-primer/">Bluetooth SIG：Bluetooth Low Energy Primer</a></li><li><a href="https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-61/out/en/host/generic-attribute-profile--gatt-.html">Bluetooth SIG：Generic Attribute Profile</a></li><li><a href="https://www.bluetooth.com/specifications/specs/battery-service/">Bluetooth SIG：Battery Service 1.1</a></li><li><a href="https://docs.zephyrproject.org/latest/services/connectivity/bluetooth/api/gatt.html">Zephyr：Generic Attribute Profile 文档</a></li><li><a href="https://docs.zephyrproject.org/latest/samples/bluetooth/mtu_update/README.html">Zephyr：MTU Update 示例</a></li></ul>]]>
    </content>
    <id>https://oniums.github.io/posts/ble-gatt-connection-basics/</id>
    <link href="https://oniums.github.io/posts/ble-gatt-connection-basics/"/>
    <published>2026-08-02T07:00:00.000Z</published>
    <summary>
      <![CDATA[<p>打开一个蓝牙调试工具，点下 Connect，页面很快显示“Connected”。这是不是说明手机已经能读到设备数据，也能正常控制设备了？</p>
<p>不一定。</p>
<p><strong>BLE 连接成功，只表示两台设备已经建立了一条无线链路。</strong> 后面通常还要发现 Service、找到 Characteristic、确认读写方式、订阅通知，应用数据才真正开始流动。</p>]]>
    </summary>
    <title>BLE GATT 入门：手机连上设备后，到底发生了什么？</title>
    <updated>2026-08-02T15:49:56.060Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Matter" scheme="https://oniums.github.io/tags/matter/"/>
    <category term="Thread" scheme="https://oniums.github.io/tags/thread/"/>
    <category term="Commissioning" scheme="https://oniums.github.io/tags/commissioning/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="入网" scheme="https://oniums.github.io/tags/%E5%85%A5%E7%BD%91/"/>
    <content>
      <![CDATA[<p>Zigbee 设备打开允许加入、搜索网络、获得密钥，似乎就能完成入网。Matter over Thread 为什么还要扫码、连接 Bluetooth LE、建立临时安全通道、验证设备证书、写入长期身份、加入 Thread，再切换到 IP 网络重新连接？</p><p>这些步骤是在把简单问题复杂化，还是在解决不同的问题？</p><span id="more"></span><p>本文跟随一台刚恢复出厂的门磁传感器，从拆箱一直走到 App 能稳定显示“门已打开”或“门已关闭”。每走一步，我们都回答五个问题：</p><ol><li>现在是谁在和谁通信？</li><li>设备得到了什么？</li><li>为什么需要这一步？</li><li>Zigbee 在相近阶段怎样处理？</li><li>成功到这里，还不能证明什么？</li></ol><h2 id="先限定比较范围"><a href="#先限定比较范围" class="headerlink" title="先限定比较范围"></a>先限定比较范围</h2><p>“Matter 入网”和“Zigbee 入网”都不只有一种路径。为了让两条时间线能够真正对齐，本文只比较两个最常见的首次入网场景：</p><ul><li>Matter over Thread：恢复出厂的设备，通过常见的 Bluetooth LE 临时通道，加入已有 Thread 网络和 Matter Fabric；</li><li>Zigbee：Factory New 设备，通过 Network Steering，加入采用集中式安全模型的 Zigbee 网络。</li></ul><p>本文不把下面这些分支混进主线：</p><ul><li>已经连接 IP 网络的 Matter On-network Commissioning；</li><li>Matter 的第二管理员和多 Fabric；</li><li>Zigbee Rejoin、Touchlink、分布式安全网络；</li><li>新版 Zigbee 增加的 Zigbee Direct、动态密钥协商和批量 commissioning。</li></ul><p>这些分支会改变部分消息和安全步骤，但不会改变本文最重要的分析方法：<strong>先分清网络接入、初始信任、长期身份和应用可用是四件不同的事。</strong></p><h2 id="第-0-章：阅读前先认识几个角色"><a href="#第-0-章：阅读前先认识几个角色" class="headerlink" title="第 0 章：阅读前先认识几个角色"></a>第 0 章：阅读前先认识几个角色</h2><p>先不要急着记 PASE、CASE、NOC 等缩写。理解下面这些角色，已经足够开始阅读。</p><table><thead><tr><th>名词</th><th>先用一句人话理解</th><th>不要误解成</th></tr></thead><tbody><tr><td>Matter</td><td>规定设备如何建立信任、描述功能和相互控制的标准</td><td>一种无线信号</td></tr><tr><td>Thread</td><td>面向低功耗设备的 IPv6 Mesh 网络</td><td>一套灯、门锁、传感器应用协议</td></tr><tr><td>Matter over Thread</td><td>Matter 应用运行在 Thread 网络上的组合</td><td>Matter 和 Thread 是同一个协议</td></tr><tr><td>Zigbee</td><td>从 Mesh 网络、安全到设备应用模型的一套协议体系</td><td>Thread 的旧名称</td></tr><tr><td>Commissioning</td><td>把新设备安全登记进家庭系统的完整过程</td><td>只连上无线网络</td></tr><tr><td>Commissioner</td><td>给 Matter 新设备办理登记的一方，可能是手机或家庭中枢</td><td>必然就是 Border Router</td></tr><tr><td>Commissionee</td><td>正在等待办理入网的 Matter 设备</td><td>一台特殊类型的硬件</td></tr><tr><td>Thread Border Router</td><td>在 Thread Mesh 与家庭 Wi-Fi、Ethernet 等 IP 网络之间转发数据</td><td>Zigbee Coordinator 或协议翻译器</td></tr><tr><td>Matter Fabric</td><td>一组共享管理和信任关系的 Matter 节点</td><td>一张 Thread 网络或一个 Zigbee PAN</td></tr><tr><td>Zigbee Coordinator</td><td>创建 Zigbee 网络的核心角色</td><td>所有 Zigbee Router</td></tr><tr><td>Zigbee Trust Center</td><td>管理 Zigbee 设备准入和安全材料的角色</td><td>普通父节点</td></tr></tbody></table><p>在实际产品里，一个家庭中枢可能同时承担 Matter Commissioner、Matter Controller 和 Thread Border Router；一个 Zigbee 网关也可能同时包含 Coordinator、Trust Center 和应用管理功能。</p><p><strong>装在同一个盒子里，不代表这些角色是同一件事。</strong></p><h3 id="两边的简化关系"><a href="#两边的简化关系" class="headerlink" title="两边的简化关系"></a>两边的简化关系</h3><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">Matter over Thread</span><br><span class="line"></span><br><span class="line">手机或家庭中枢</span><br><span class="line">  ├── Commissioner：办理入网</span><br><span class="line">  ├── Controller：日常读取和控制</span><br><span class="line">  └── 可能还包含 Thread Border Router</span><br><span class="line">                         │</span><br><span class="line">                         │ 在 Thread 与家庭 IP 网络间路由</span><br><span class="line">                         ▼</span><br><span class="line">                 Thread Mesh 中的门磁</span><br><span class="line"></span><br><span class="line">Matter Fabric：</span><br><span class="line">记录长期身份、管理关系和访问权限</span><br></pre></td></tr></table></figure><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Zigbee</span><br><span class="line"></span><br><span class="line">Zigbee 网关</span><br><span class="line">  ├── Coordinator：创建网络</span><br><span class="line">  ├── Trust Center：管理准入和安全</span><br><span class="line">  └── 应用管理：识别门磁能力</span><br><span class="line">             │</span><br><span class="line">             ▼</span><br><span class="line">      Router 或直接父节点</span><br><span class="line">             │</span><br><span class="line">             ▼</span><br><span class="line">          Zigbee 门磁</span><br></pre></td></tr></table></figure><p>如果还分不清三者的位置，可以先阅读<a href="/posts/matter-thread-zigbee-layers/">《Matter、Thread 与 Zigbee：先分清它们在哪一层》</a>。</p><h2 id="第-1-章：两台门磁加入的是同一种“网”吗？"><a href="#第-1-章：两台门磁加入的是同一种“网”吗？" class="headerlink" title="第 1 章：两台门磁加入的是同一种“网”吗？"></a>第 1 章：两台门磁加入的是同一种“网”吗？</h2><h3 id="先提出疑问"><a href="#先提出疑问" class="headerlink" title="先提出疑问"></a>先提出疑问</h3><p>Matter over Thread 和 Zigbee 都可能使用 2.4 GHz IEEE 802.15.4。既然无线底层相似，为什么入网流程不能也做成一样？</p><h3 id="Zigbee-更像一套完整的小区系统"><a href="#Zigbee-更像一套完整的小区系统" class="headerlink" title="Zigbee 更像一套完整的小区系统"></a>Zigbee 更像一套完整的小区系统</h3><p>在本文讨论的经典集中式 Zigbee 网络里，设备从扫描网络开始，随后建立父子关系、获得网络地址和 Network Key，最后被网关识别出应用能力。</p><p>Zigbee 自己定义了：</p><ul><li>Mesh 网络怎样形成；</li><li>网络地址怎样使用；</li><li>Trust Center 怎样管理安全准入；</li><li>NWK、APS 层怎样保护通信；</li><li>Endpoint、Cluster、Attribute 怎样表达设备功能。</li></ul><p>它很像一个同时负责道路、门禁、住户管理和物业服务的小区系统。</p><h3 id="Matter-over-Thread-是两层组合"><a href="#Matter-over-Thread-是两层组合" class="headerlink" title="Matter over Thread 是两层组合"></a>Matter over Thread 是两层组合</h3><p>Matter over Thread 则把问题拆开：</p><ul><li>Thread 回答：设备怎样获得一条低功耗 IPv6 通信道路？</li><li>Matter 回答：设备是谁、属于哪个家庭信任域、谁可以控制它、它有哪些标准能力？</li></ul><p>因此，一台 Matter over Thread 门磁要完成两类登记：</p><ol><li>获得 Thread 网络资料并接入 Thread Mesh；</li><li>获得 Matter 运行身份并加入一个 Fabric。</li></ol><p>这两个结果经常在同一次用户操作中完成，但不是同一层协议状态。</p><h3 id="为什么要这样拆开？"><a href="#为什么要这样拆开？" class="headerlink" title="为什么要这样拆开？"></a>为什么要这样拆开？</h3><p>拆开的好处是：</p><ul><li>Matter 身份不必绑定在某个父节点或某个临时 IPv6 路由位置上；</li><li>同一个 Matter Fabric 可以包含 Thread、Wi-Fi 和 Ethernet 设备；</li><li>Thread Border Router 只需要路由 IP 数据，不必理解门磁、灯或门锁；</li><li>Matter 可以在应用层统一身份、权限和数据模型。</li></ul><p>代价也很直接：</p><ul><li>网络凭据和 Matter 身份凭据要分别管理；</li><li>配网要跨越 Bluetooth LE、Thread、IPv6 和 Matter 安全会话；</li><li>“连接成功”出现了更多不同层次。</li></ul><blockquote><p><strong>如果不拆开会怎样？</strong></p><p>网络拓扑、应用身份和生态管理会更加紧密地绑定在一起。系统可能更直接，但跨不同 IP 承载、多管理员和统一访问控制会更依赖网关或厂商自己的设计。</p></blockquote><blockquote><p><strong>到这里证明了什么？</strong></p><p>我们只理解了两种架构的目标，还没有开始真正入网。</p></blockquote><h2 id="第-2-章：为什么要先打开一个“允许加入”的窗口？"><a href="#第-2-章：为什么要先打开一个“允许加入”的窗口？" class="headerlink" title="第 2 章：为什么要先打开一个“允许加入”的窗口？"></a>第 2 章：为什么要先打开一个“允许加入”的窗口？</h2><h3 id="Matter：设备先表示“我现在可以办理登记”"><a href="#Matter：设备先表示“我现在可以办理登记”" class="headerlink" title="Matter：设备先表示“我现在可以办理登记”"></a>Matter：设备先表示“我现在可以办理登记”</h3><p>恢复出厂的 Matter 设备通常会进入可 commissioning 状态，或者由用户按键主动打开 Commissioning Window。设备随后通过支持的发现方式告诉附近的 Commissioner：“我现在接受入网。”</p><p>在本文的典型场景里，这通常包括 Bluetooth LE 广播。</p><p>窗口不会永远开放，因为长期允许陌生控制端尝试建立初始会话，会增加无意义连接和攻击面。用户明确触发配网，也能把一次现实世界中的操作与后续网络操作联系起来。</p><h3 id="Zigbee：网关和设备两边都要准备好"><a href="#Zigbee：网关和设备两边都要准备好" class="headerlink" title="Zigbee：网关和设备两边都要准备好"></a>Zigbee：网关和设备两边都要准备好</h3><p>Zigbee 网关通常先打开 Permit Join。Factory New 设备启动 Network Steering，在配置的信道集合中寻找允许加入的网络。</p><p>这里要区分两件事：</p><ul><li>Permit Join：网络允许新设备提出申请；</li><li>设备最终被接纳：还要经过关联和安全准入。</li></ul><p>看到 Permit Join 已打开，不代表任何附近设备都已经成为网络成员。</p><h3 id="可以怎样类比？"><a href="#可以怎样类比？" class="headerlink" title="可以怎样类比？"></a>可以怎样类比？</h3><table><thead><tr><th>目的</th><th>Matter over Thread</th><th>Zigbee</th></tr></thead><tbody><tr><td>设备表示愿意办理入网</td><td>Commissioning Window &#x2F; Commissionable 广播</td><td>Factory New + Network Steering</td></tr><tr><td>网络侧允许新设备申请</td><td>Commissioner 开始添加流程</td><td>Permit Join</td></tr><tr><td>最终准入</td><td>后续 PASE、Attestation、NOC 等</td><td>Association、Trust Center 安全流程等</td></tr></tbody></table><p>Commissioning Window 和 Permit Join 的目的相近，但它们不是同一条协议命令。</p><blockquote><p><strong>如果没有窗口会怎样？</strong></p><p>设备或网络可能长期接受未经用户触发的入网尝试，既浪费资源，也难以把“用户正在添加这台设备”与无线世界里的请求对应起来。</p></blockquote><blockquote><p><strong>到这里证明了什么？</strong></p><p>只证明双方进入了“可以尝试添加”的状态，还没有找到彼此，更没有建立安全关系。</p></blockquote><h2 id="第-3-章：为什么-Matter-要扫码，而-Zigbee-常常自己搜索网络？"><a href="#第-3-章：为什么-Matter-要扫码，而-Zigbee-常常自己搜索网络？" class="headerlink" title="第 3 章：为什么 Matter 要扫码，而 Zigbee 常常自己搜索网络？"></a>第 3 章：为什么 Matter 要扫码，而 Zigbee 常常自己搜索网络？</h2><h3 id="扫码不是无线连接"><a href="#扫码不是无线连接" class="headerlink" title="扫码不是无线连接"></a>扫码不是无线连接</h3><p>Matter 二维码或手动配对码携带的是 Onboarding Payload，也就是帮助 Commissioner 找到目标设备并建立初始信任的引导信息。</p><p>对本文主线最重要的两项是：</p><ul><li>Discriminator：帮助从附近多个待配网设备中缩小目标；</li><li>Setup Passcode：后面建立初始安全会话时使用的秘密。</li></ul><p>二维码不包含真实家庭的 Thread Dataset，也不会因为被摄像头扫到，就自动连接设备。</p><p>可以把它理解为：</p><blockquote><p>二维码告诉接待员“应该找哪位新住户，以及第一次见面用什么暗号”，而不是直接把小区总钥匙印在包装上。</p></blockquote><h3 id="Matter-怎样发现设备？"><a href="#Matter-怎样发现设备？" class="headerlink" title="Matter 怎样发现设备？"></a>Matter 怎样发现设备？</h3><p>典型过程是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">用户扫描二维码</span><br><span class="line">  -&gt; App 解析引导信息</span><br><span class="line">  -&gt; 手机扫描附近 Commissionable 广播</span><br><span class="line">  -&gt; 使用 Discriminator 等信息筛选目标</span><br><span class="line">  -&gt; 建立 Bluetooth LE 连接</span><br></pre></td></tr></table></figure><p>Bluetooth LE Connected 只说明手机和设备之间有了一条近距离通信链路。</p><h3 id="Zigbee-怎样发现网络？"><a href="#Zigbee-怎样发现网络？" class="headerlink" title="Zigbee 怎样发现网络？"></a>Zigbee 怎样发现网络？</h3><p>经典 Network Steering 更像设备主动找小区：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">设备扫描候选信道</span><br><span class="line">  -&gt; 发送 Beacon Request</span><br><span class="line">  -&gt; 接收周围网络的 Beacon</span><br><span class="line">  -&gt; 判断网络是否允许加入</span><br><span class="line">  -&gt; 比较候选网络和父节点</span><br><span class="line">  -&gt; 选择目标网络</span><br></pre></td></tr></table></figure><p>设备看到 Beacon，只证明它发现了网络。它还没有完成 Association，也没有获得可用的 Network Key。</p><h3 id="为什么两边选择不同？"><a href="#为什么两边选择不同？" class="headerlink" title="为什么两边选择不同？"></a>为什么两边选择不同？</h3><p>Matter 的用户通常是在 App 中明确添加某一台商品设备，二维码能帮助用户指认目标，并提供设备独有的初始秘密。Zigbee Network Steering 则更强调由设备扫描附近符合条件的 Zigbee 网络，再进入网络侧准入流程。</p><p>这不是“扫码一定比扫描安全”，而是两种系统如何把现实世界中的用户操作与网络申请绑定起来的不同选择。</p><blockquote><p><strong>如果 Matter 只有 BLE 广播、没有配对码会怎样？</strong></p><p>手机可能知道附近有待配网设备，却缺少设备独有的初始秘密来建立后续安全会话，也更难确认用户指向的是哪一台同型号设备。</p></blockquote><blockquote><p><strong>到这里证明了什么？</strong></p><p>扫码证明 App 获得了引导信息；BLE 连接证明双方可以传数据。两者都不能证明 Thread 或 Matter 入网成功。</p></blockquote><h2 id="第-4-章：Bluetooth-LE-已经连接，为什么还需要-PASE？"><a href="#第-4-章：Bluetooth-LE-已经连接，为什么还需要-PASE？" class="headerlink" title="第 4 章：Bluetooth LE 已经连接，为什么还需要 PASE？"></a>第 4 章：Bluetooth LE 已经连接，为什么还需要 PASE？</h2><h3 id="“电话接通”不等于“进入保密会议室”"><a href="#“电话接通”不等于“进入保密会议室”" class="headerlink" title="“电话接通”不等于“进入保密会议室”"></a>“电话接通”不等于“进入保密会议室”</h3><p>Bluetooth LE 解决的是近距离传输问题。它不自动证明对端就是二维码对应的设备，也不意味着 Thread 网络资料可以直接明文发送。</p><p>手机和设备会利用 Setup Passcode 建立一条本次 commissioning 使用的临时安全会话。Matter 把这一步称为 <strong>PASE</strong>，全称 Passcode Authenticated Session Establishment。</p><p>先记住一句话：</p><blockquote><p>PASE 是配网阶段的临时安全通道，不是设备日常运行的长期身份证。</p></blockquote><h3 id="PASE-成功后能做什么？"><a href="#PASE-成功后能做什么？" class="headerlink" title="PASE 成功后能做什么？"></a>PASE 成功后能做什么？</h3><p>Commissioner 可以通过受保护的 Matter 会话执行后续 commissioning 操作，例如：</p><ul><li>读取设备的 commissioning 能力；</li><li>启动 Fail-safe；</li><li>配置法规或时间相关信息；</li><li>请求设备认证材料；</li><li>写入 Matter 运行凭据；</li><li>配置 Thread 网络。</li></ul><p>Fail-safe 可以理解为“配网安全绳”：如果流程中途失败或超时，设备能够回退未最终确认的配置，避免长期停在只完成一半的状态。</p><h3 id="Zigbee-中哪个步骤最像-PASE？"><a href="#Zigbee-中哪个步骤最像-PASE？" class="headerlink" title="Zigbee 中哪个步骤最像 PASE？"></a>Zigbee 中哪个步骤最像 PASE？</h3><p>没有一个完全相同的步骤。</p><p>Zigbee Association 主要建立设备的网络成员关系和父子关系；初始 Link Key、Install Code 派生 Key 等则承担初始安全引导的一部分目的。它们与 PASE 有交集，但协议层次和生命周期不同。</p><table><thead><tr><th>问题</th><th>Matter PASE</th><th>Zigbee 经典加入</th></tr></thead><tbody><tr><td>是否建立临时安全会话</td><td>是</td><td>不按 PASE 方式建立</td></tr><tr><td>是否建立父子网络关系</td><td>否</td><td>Association 负责</td></tr><tr><td>初始秘密来源</td><td>Setup Passcode</td><td>预配置 Link Key、Install Code 派生 Key等</td></tr><tr><td>是否用于长期日常单播</td><td>否</td><td>初始 Key 是否继续使用取决于安全策略</td></tr></tbody></table><blockquote><p><strong>如果没有 PASE 会怎样？</strong></p><p>Thread Dataset、证书配置和其他 commissioning 数据将缺少 Matter 定义的初始安全会话保护，初始秘密也难以安全过渡到长期身份。</p></blockquote><blockquote><p><strong>到这里证明了什么？</strong></p><p>PASE 成功只证明双方建立了临时安全会话。设备仍可能没有 Thread 网络资料、没有 NOC，也没有加入 Fabric。</p></blockquote><h2 id="第-5-章：有了配网码，为什么还要验证设备是不是可信产品？"><a href="#第-5-章：有了配网码，为什么还要验证设备是不是可信产品？" class="headerlink" title="第 5 章：有了配网码，为什么还要验证设备是不是可信产品？"></a>第 5 章：有了配网码，为什么还要验证设备是不是可信产品？</h2><h3 id="知道暗号，不等于拥有可信身份证"><a href="#知道暗号，不等于拥有可信身份证" class="headerlink" title="知道暗号，不等于拥有可信身份证"></a>知道暗号，不等于拥有可信身份证</h3><p>Setup Passcode 证明的是双方掌握同一个初始秘密。它不能单独回答：</p><ul><li>设备是否由它声称的厂商制造；</li><li>设备是否持有对应的设备私钥；</li><li>产品身份和合规声明是否能形成可信链条；</li><li>当前响应是否来自一次旧通信的重放。</li></ul><p>Matter 因此还有 <strong>Device Attestation</strong>，也就是设备认证。</p><h3 id="用“身份证链”理解-Attestation"><a href="#用“身份证链”理解-Attestation" class="headerlink" title="用“身份证链”理解 Attestation"></a>用“身份证链”理解 Attestation</h3><p>不用先背缩写，先看角色：</p><ol><li>每台设备有自己的设备证书和私钥；</li><li>设备证书由上级产品认证机构签发；</li><li>Commissioner 信任更上层的根；</li><li>Commissioner 发送一次性随机挑战；</li><li>设备使用私钥对本次挑战相关数据签名；</li><li>Commissioner 验证证书链、签名和产品声明。</li></ol><p>专业名称对应如下：</p><table><thead><tr><th>白话角色</th><th>Matter 名称</th></tr></thead><tbody><tr><td>每台设备的身份证</td><td>Device Attestation Certificate，DAC</td></tr><tr><td>签发设备证书的上级</td><td>Product Attestation Intermediate，PAI</td></tr><tr><td>信任链根</td><td>Product Attestation Authority，PAA</td></tr><tr><td>产品合规声明</td><td>Certification Declaration，CD</td></tr></tbody></table><p>PAA 根证书通常来自 Commissioner 的信任库，不是简单要求设备“自己拿一张根证书证明自己”。</p><h3 id="Attestation-能证明到什么程度？"><a href="#Attestation-能证明到什么程度？" class="headerlink" title="Attestation 能证明到什么程度？"></a>Attestation 能证明到什么程度？</h3><p>它帮助 Commissioner 验证设备持有的认证身份和产品声明。它不自动证明：</p><ul><li>固件永远没有漏洞；</li><li>设备运行环境没有被破坏；</li><li>用户一定应该授权这台设备进入家庭；</li><li>入网后的每一次业务操作都天然允许。</li></ul><p>设备认证是准入证据之一，最终策略仍由 Commissioner 和生态决定。</p><h3 id="Zigbee-有没有完全对应的步骤？"><a href="#Zigbee-有没有完全对应的步骤？" class="headerlink" title="Zigbee 有没有完全对应的步骤？"></a>Zigbee 有没有完全对应的步骤？</h3><p>在本文比较的经典 Zigbee 3.x 集中式安全加入中，没有与 Matter 设备证书链认证完全等价的通用步骤。</p><p>Trust Center 可以根据设备信息和安全策略决定是否接纳；Install Code 可以让设备与 Trust Center 拥有设备唯一的初始秘密。但“证明双方知道唯一秘密”与“验证厂商、产品声明和设备证书链”仍然是不同问题。</p><p>较新的 Zigbee 版本继续扩展 commissioning 和安全能力，因此具体项目必须回到目标 Zigbee Core、BDB 与安全策略确认，不能把本文的经典路径当成所有版本的唯一实现。</p><blockquote><p><strong>如果没有 Attestation 会怎样？</strong></p><p>Commissioner 仍可能依靠配网码建立初始加密，但更难用标准化证书链验证设备声称的产品身份和设备私钥。</p></blockquote><blockquote><p><strong>到这里证明了什么？</strong></p><p>Attestation 成功证明认证材料通过了 Commissioner 的验证流程；它不证明 Thread 已连接，也不证明最终 CommissioningComplete 已完成。</p></blockquote><h2 id="第-6-章：为什么既要-Thread-Dataset，又要-Matter-NOC？"><a href="#第-6-章：为什么既要-Thread-Dataset，又要-Matter-NOC？" class="headerlink" title="第 6 章：为什么既要 Thread Dataset，又要 Matter NOC？"></a>第 6 章：为什么既要 Thread Dataset，又要 Matter NOC？</h2><p>这是整篇文章最重要的问题。</p><h3 id="两份材料回答两个不同问题"><a href="#两份材料回答两个不同问题" class="headerlink" title="两份材料回答两个不同问题"></a>两份材料回答两个不同问题</h3><p><strong>Thread Active Operational Dataset</strong> 回答：</p><blockquote><p>设备怎样进入这张 Thread 网络？</p></blockquote><p>它描述目标 Thread 网络的关键参数和安全材料，例如信道、网络标识、Mesh-Local Prefix 和网络安全信息。它不是适合公开分享的普通配置文本。</p><p><strong>Node Operational Certificate，NOC</strong> 回答：</p><blockquote><p>设备以什么 Node 身份加入哪个 Matter Fabric？</p></blockquote><p>可以把二者理解为：</p><ul><li>Thread Dataset：小区道路和门禁的入场资料；</li><li>NOC：写有住户身份和家庭归属的长期电子证件。</li></ul><h3 id="Matter-长期身份怎样产生？"><a href="#Matter-长期身份怎样产生？" class="headerlink" title="Matter 长期身份怎样产生？"></a>Matter 长期身份怎样产生？</h3><p>典型流程可以简化为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">设备生成运行密钥对</span><br><span class="line">  -&gt; 设备提交 CSR</span><br><span class="line">  -&gt; Fabric 管理侧签发 NOC</span><br><span class="line">  -&gt; Commissioner 写入受信任根</span><br><span class="line">  -&gt; Commissioner 写入 NOC</span><br><span class="line">  -&gt; 建立 Node ID、Fabric 和初始管理关系</span><br></pre></td></tr></table></figure><p>设备的运行私钥应留在设备内部。CSR 是证书签名请求，不是把私钥交给 Commissioner。</p><p>在典型流程中，AddNOC 还会帮助建立初始管理员主体和访问控制关系。日后 Matter 的读取、写入、命令和订阅仍要经过访问控制判断，而不是“知道设备 IP 地址就可以任意操作”。</p><h3 id="Zigbee-用哪些材料？"><a href="#Zigbee-用哪些材料？" class="headerlink" title="Zigbee 用哪些材料？"></a>Zigbee 用哪些材料？</h3><p>Zigbee 没有一张与 NOC 完全等价的单一证件。几个常见概念分别承担不同作用：</p><table><thead><tr><th>Zigbee 概念</th><th>主要作用</th></tr></thead><tbody><tr><td>EUI-64</td><td>设备长期标识</td></tr><tr><td>16 位短地址</td><td>当前网络中的运行地址</td></tr><tr><td>Network Key</td><td>保护 Zigbee NWK 层通信的网络共享密钥</td></tr><tr><td>Trust Center Link Key</td><td>设备与 Trust Center 之间的安全管理材料</td></tr><tr><td>Install Code 派生 Key</td><td>设备唯一的初始安全引导材料</td></tr></tbody></table><p>Network Key 让设备参与受保护的 Zigbee 网络通信，但它不是某台设备独有的运行证书。短地址适合当前网络中的高效寻址，但也不等于长期产品身份。</p><h3 id="最容易记错的几个等式"><a href="#最容易记错的几个等式" class="headerlink" title="最容易记错的几个等式"></a>最容易记错的几个等式</h3><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Thread Dataset ≠ Matter NOC</span><br><span class="line">Matter Fabric ≠ Thread Network</span><br><span class="line">Matter Fabric ≠ Zigbee PAN</span><br><span class="line">Matter NOC ≠ Zigbee Network Key</span><br><span class="line">Matter Node ID ≠ Zigbee Short Address</span><br><span class="line">Setup Passcode ≠ Zigbee Install Code</span><br></pre></td></tr></table></figure><p>它们可以在“网络凭据”“长期身份”“初始秘密”等目的层面比较，但不能在实现、抓包或日志里互换。</p><blockquote><p><strong>如果只有 Dataset、没有 NOC 会怎样？</strong></p><p>设备可以获得 Thread 网络连接，却缺少加入 Matter Fabric 的标准长期身份。</p></blockquote><blockquote><p><strong>如果只有 NOC、没有 Dataset 会怎样？</strong></p><p>设备可能已经准备好 Matter 身份，却没有进入目标 Thread 网络的道路。</p></blockquote><blockquote><p><strong>到这里证明了什么？</strong></p><p>NOC 写入证明 Fabric 运行凭据已经配置到相应阶段。它不等于设备已经成功 Thread Attach，也不等于 CASE 和 CommissioningComplete 已完成。</p></blockquote><h2 id="第-7-章：设备怎样真正加入-Thread？Zigbee-又怎样加入自己的网络？"><a href="#第-7-章：设备怎样真正加入-Thread？Zigbee-又怎样加入自己的网络？" class="headerlink" title="第 7 章：设备怎样真正加入 Thread？Zigbee 又怎样加入自己的网络？"></a>第 7 章：设备怎样真正加入 Thread？Zigbee 又怎样加入自己的网络？</h2><h3 id="Matter：通过-PASE-下发-Thread-入场资料"><a href="#Matter：通过-PASE-下发-Thread-入场资料" class="headerlink" title="Matter：通过 PASE 下发 Thread 入场资料"></a>Matter：通过 PASE 下发 Thread 入场资料</h3><p>在本文场景里，Commissioner 已经从家庭系统获得目标 Thread Dataset，再通过 PASE 保护的 commissioning 会话配置设备的 Thread 网络。</p><p>高层过程是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Commissioner 配置 Thread Dataset</span><br><span class="line">  -&gt; 要求设备连接目标网络</span><br><span class="line">  -&gt; 设备扫描并找到目标 Thread Partition</span><br><span class="line">  -&gt; 设备寻找可用 Router 或 REED</span><br><span class="line">  -&gt; 建立 Parent-Child 关系</span><br><span class="line">  -&gt; 获得 Thread 网络数据和 IPv6 地址能力</span><br><span class="line">  -&gt; 成为 Child</span><br></pre></td></tr></table></figure><p>Thread 使用 Mesh Link Establishment，简称 MLE，发现和维护邻居关系。本文的电池门磁通常作为 End Device，通过一个 Parent Router 通信。即使是 Router-Eligible 设备，首次 Attach 也先以 Child 身份进入网络。</p><p>Thread Border Router 的任务是把 Thread 与相邻 IP 网络连接起来。它不是负责给门磁解释 Matter Cluster 的应用网关，也不一定是门磁当前的无线父节点。</p><h3 id="Zigbee：先建立网络位置，再完成安全准入"><a href="#Zigbee：先建立网络位置，再完成安全准入" class="headerlink" title="Zigbee：先建立网络位置，再完成安全准入"></a>Zigbee：先建立网络位置，再完成安全准入</h3><p>经典首次 Network Steering 可以简化为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">扫描信道和 Beacon</span><br><span class="line">  -&gt; 选择目标 PAN 与父节点</span><br><span class="line">  -&gt; Association Request</span><br><span class="line">  -&gt; Association Response</span><br><span class="line">  -&gt; 获得 16 位短地址</span><br><span class="line">  -&gt; Trust Center 安全准入</span><br><span class="line">  -&gt; 接收并安装 Network Key</span><br><span class="line">  -&gt; 开始受保护的 NWK 通信</span><br></pre></td></tr></table></figure><p>Association 由 IEEE 802.15.4 MAC 层提供，用来建立网络成员关系。获得短地址说明设备已经有了初步网络位置，但它还必须正确接收、验证并安装安全材料。</p><p>所以：</p><ul><li>Transport Key 已经从发送端发出，不等于接收端已经解密；</li><li>收到 MAC ACK，不等于 Network Key 已安装；</li><li>获得短地址，不等于 BDB commissioning 全部完成。</li></ul><h3 id="两条路径真正对应在哪里？"><a href="#两条路径真正对应在哪里？" class="headerlink" title="两条路径真正对应在哪里？"></a>两条路径真正对应在哪里？</h3><table><thead><tr><th>目标</th><th>Matter over Thread</th><th>Zigbee</th></tr></thead><tbody><tr><td>找到无线网络</td><td>Thread 扫描与发现</td><td>信道扫描与 Beacon</td></tr><tr><td>建立父子关系</td><td>Thread MLE Attach</td><td>Association</td></tr><tr><td>获得网络安全资料</td><td>预先通过 PASE 配置 Dataset</td><td>Trust Center 传输 Network Key</td></tr><tr><td>获得网络地址能力</td><td>Thread IPv6 地址与网络数据</td><td>16 位短地址与 Zigbee NWK 状态</td></tr><tr><td>网络层可通信</td><td>Thread&#x2F;IPv6 可用</td><td>受保护 Zigbee NWK 通信可用</td></tr></tbody></table><p>两边都要解决“找网络、选父节点、获得网络安全材料、开始正常通信”，但凭据怎样送到设备、长期身份放在哪一层，设计明显不同。</p><blockquote><p><strong>如果只看到 Thread 设备成为 Child 会怎样？</strong></p><p>可以确认 Thread 网络层已经进展到 Attach 成功附近，但仍不能确认 Matter CASE、CommissioningComplete 和应用订阅。</p></blockquote><blockquote><p><strong>如果只看到 Zigbee Association Success 会怎样？</strong></p><p>可以确认父子关系和短地址分配已经进展，但仍不能确认 Network Key 安装、TCLK 流程和应用发现。</p></blockquote><h2 id="第-8-章：Thread-已经连上，为什么-Matter-还要重新找设备？"><a href="#第-8-章：Thread-已经连上，为什么-Matter-还要重新找设备？" class="headerlink" title="第 8 章：Thread 已经连上，为什么 Matter 还要重新找设备？"></a>第 8 章：Thread 已经连上，为什么 Matter 还要重新找设备？</h2><h3 id="从临时接待通道切换到正式道路"><a href="#从临时接待通道切换到正式道路" class="headerlink" title="从临时接待通道切换到正式道路"></a>从临时接待通道切换到正式道路</h3><p>前面的 Matter 操作主要通过 Bluetooth LE 上的 PASE 会话完成。设备加入 Thread 后，后续日常通信应走 Thread 承载的 IPv6，而不是长期依赖手机与设备之间的 BLE 连接。</p><p>Commissioner 需要在正式 IP 网络中找到设备。这称为 <strong>Operational Discovery</strong>。Matter 使用 DNS-SD 等机制解析设备当前可用的地址和端口。</p><p>这里有一个很重要的设计：</p><blockquote><p>Matter 身份是 Node 与 Fabric 身份，不是某个固定 IPv6 地址。</p></blockquote><p>Thread 节点可能拥有多类 IPv6 地址，某些地址还会随拓扑变化。Controller 应通过运行态发现解析设备，而不是把 commissioning 时看到的某个地址永久当成设备身份。</p><h3 id="为什么还要建立-CASE？"><a href="#为什么还要建立-CASE？" class="headerlink" title="为什么还要建立 CASE？"></a>为什么还要建立 CASE？</h3><p>找到设备地址以后，双方使用 Fabric 的运行证书建立长期单播安全会话。这称为 <strong>CASE</strong>，全称 Certificate Authenticated Session Establishment。</p><p>可以这样区分：</p><table><thead><tr><th>会话</th><th>使用阶段</th><th>主要信任基础</th><th>用完以后</th></tr></thead><tbody><tr><td>PASE</td><td>初次 commissioning</td><td>Setup Passcode</td><td>不作为日常长期身份</td></tr><tr><td>CASE</td><td>日常运行和正式管理</td><td>Fabric 运行证书</td><td>可按需重建和恢复</td></tr></tbody></table><p>PASE 像门口的一次性接待室，CASE 像正式住户凭证建立的长期安全通信。</p><h3 id="Zigbee-为什么没有同样的通道切换？"><a href="#Zigbee-为什么没有同样的通道切换？" class="headerlink" title="Zigbee 为什么没有同样的通道切换？"></a>Zigbee 为什么没有同样的通道切换？</h3><p>经典 Zigbee 入网从扫描、Association 到后续 Cluster 通信，都留在 Zigbee 协议栈中。设备安装 Network Key 后，会继续通过 Zigbee NWK、APS 和 ZCL 交互。</p><p>它不需要经历“BLE 临时通道 → Thread&#x2F;IPv6 正式通道”的相同切换，因此也没有与 Operational Discovery + CASE 完全等价的一组步骤。</p><p>Zigbee 仍然要处理地址映射、Device Announcement、Link Key 和应用发现，只是问题被组织在另一套协议结构里。</p><blockquote><p><strong>如果没有 Operational Discovery 会怎样？</strong></p><p>Commissioner 可能知道设备的 Fabric 身份，却不知道它现在可以通过哪个 IP 地址和端口访问。</p></blockquote><blockquote><p><strong>如果继续只用 PASE、不建立 CASE 会怎样？</strong></p><p>日常通信将依赖初始配网秘密，难以利用 Fabric 证书、Node 身份和访问控制建立长期管理关系。</p></blockquote><blockquote><p><strong>到这里证明了什么？</strong></p><p>CASE 成功证明双方已通过正式网络和 Fabric 身份建立安全会话。最终 commissioning 仍要完成确认。</p></blockquote><h2 id="第-9-章：为什么还需要-CommissioningComplete？"><a href="#第-9-章：为什么还需要-CommissioningComplete？" class="headerlink" title="第 9 章：为什么还需要 CommissioningComplete？"></a>第 9 章：为什么还需要 CommissioningComplete？</h2><h3 id="配置写完，不等于验收结束"><a href="#配置写完，不等于验收结束" class="headerlink" title="配置写完，不等于验收结束"></a>配置写完，不等于验收结束</h3><p>Commissioner 已经完成设备认证、运行凭据配置和 Thread 网络连接，也已经通过正式网络建立 CASE。最后，它会发送 <strong>CommissioningComplete</strong>。</p><p>这一步的意义可以理解为：</p><ul><li>确认新的运行身份和网络路径确实可用；</li><li>正式结束本次 commissioning；</li><li>解除前面用于失败回滚的 Fail-safe；</li><li>不再把设备停留在“正在办理入住”的中间状态。</li></ul><p>它像装修、通电和证件登记都完成后的最终交房签字。</p><h3 id="Zigbee-怎样宣布完成？"><a href="#Zigbee-怎样宣布完成？" class="headerlink" title="Zigbee 怎样宣布完成？"></a>Zigbee 怎样宣布完成？</h3><p>Zigbee 设备安装 Network Key 并开始受保护通信后，通常还会：</p><ul><li>发送 Device Announcement；</li><li>完成由目标 BDB 版本和 Trust Center 策略要求的 Link Key 相关流程；</li><li>由 BDB 状态机报告 commissioning 结果。</li></ul><p>具体的 TCLK 更新或交换路径取决于目标版本和安全策略。不能看到 Network Key 就一概宣称所有安全步骤完成。</p><h3 id="两边的成功阶梯"><a href="#两边的成功阶梯" class="headerlink" title="两边的成功阶梯"></a>两边的成功阶梯</h3><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Matter over Thread</span><br><span class="line"></span><br><span class="line">发现设备</span><br><span class="line">  -&gt; BLE 连接</span><br><span class="line">  -&gt; PASE</span><br><span class="line">  -&gt; Device Attestation</span><br><span class="line">  -&gt; NOC / Fabric 身份</span><br><span class="line">  -&gt; Thread Attach</span><br><span class="line">  -&gt; Operational Discovery</span><br><span class="line">  -&gt; CASE</span><br><span class="line">  -&gt; CommissioningComplete</span><br></pre></td></tr></table></figure><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Zigbee</span><br><span class="line"></span><br><span class="line">发现网络</span><br><span class="line">  -&gt; 选择父节点</span><br><span class="line">  -&gt; Association</span><br><span class="line">  -&gt; 获得短地址</span><br><span class="line">  -&gt; 接收并安装 Network Key</span><br><span class="line">  -&gt; 受保护的 NWK 通信</span><br><span class="line">  -&gt; Device Announcement</span><br><span class="line">  -&gt; TCLK / BDB 相应流程完成</span><br></pre></td></tr></table></figure><blockquote><p><strong>如果没有最终完成确认会怎样？</strong></p><p>设备可能保留只完成一部分的网络或身份配置，系统也难以区分“仍在办理”与“已经正式可用”。Matter 的 Fail-safe 正是为了约束这种中间状态。</p></blockquote><blockquote><p><strong>到这里证明了什么？</strong></p><p>CommissioningComplete 成功证明本次 Matter commissioning 正式闭环。它仍不保证生态已经建立稳定订阅，也不保证 App 的设备模型完全正确。</p></blockquote><h2 id="第-10-章：为什么-App-显示“添加成功”，设备仍可能不可用？"><a href="#第-10-章：为什么-App-显示“添加成功”，设备仍可能不可用？" class="headerlink" title="第 10 章：为什么 App 显示“添加成功”，设备仍可能不可用？"></a>第 10 章：为什么 App 显示“添加成功”，设备仍可能不可用？</h2><h3 id="入网成功与业务可用是两个阶段"><a href="#入网成功与业务可用是两个阶段" class="headerlink" title="入网成功与业务可用是两个阶段"></a>入网成功与业务可用是两个阶段</h3><p>一台门磁真正好用，至少还要让平台知道：</p><ul><li>它有哪些 Endpoint；</li><li>它是什么 Device Type；</li><li>它实现了哪些 Cluster；</li><li>门开、门关状态从哪里读取；</li><li>状态变化怎样持续上报；</li><li>当前 Controller 是否有读取或订阅权限。</li></ul><p>Matter Controller 通常会读取设备结构和属性，并建立 Subscribe。订阅建立后，设备才能在约定条件下持续报告状态变化。</p><p>如果 CommissioningComplete 已成功，但从未建立 Subscribe，门磁可能“在网、可信、可寻址”，App 却无法持续得到门状态。</p><h3 id="Zigbee-网关也要做设备-Interview"><a href="#Zigbee-网关也要做设备-Interview" class="headerlink" title="Zigbee 网关也要做设备 Interview"></a>Zigbee 网关也要做设备 Interview</h3><p>Zigbee 网络接入完成后，网关通常还会：</p><ul><li>查询 Node Descriptor；</li><li>查询 Active Endpoints；</li><li>读取 Simple Descriptor；</li><li>读取 Basic 属性；</li><li>识别支持的 Cluster；</li><li>配置 Reporting；</li><li>按需要建立 Binding。</li></ul><p>所以 Zigbee 设备获得短地址或发送 Device Announcement，也不等于网关已经完整识别它。</p><h3 id="把“成功”分成四级"><a href="#把“成功”分成四级" class="headerlink" title="把“成功”分成四级"></a>把“成功”分成四级</h3><table><thead><tr><th>成功级别</th><th>Matter over Thread 示例</th><th>Zigbee 示例</th><th>还不能证明</th></tr></thead><tbody><tr><td>发现成功</td><td>找到 BLE 广播</td><td>看到 Beacon</td><td>已建立安全或网络成员关系</td></tr><tr><td>网络接入成功</td><td>Thread Attach &#x2F; IPv6 可用</td><td>Association + 安全 NWK 通信</td><td>长期应用关系和平台识别完成</td></tr><tr><td>长期信任成功</td><td>NOC、CASE、CommissioningComplete</td><td>BDB 和目标安全流程完成</td><td>状态订阅、Reporting 一定正常</td></tr><tr><td>业务可用</td><td>Read&#x2F;Subscribe&#x2F;Report 正常</td><td>Interview&#x2F;Binding&#x2F;Reporting 正常</td><td>所有异常和长期稳定性都已验证</td></tr></tbody></table><p>这四级比一个模糊的“配网成功”更适合分析真实问题。</p><blockquote><p><strong>到这里证明了什么？</strong></p><p>只有平台成功识别设备并稳定收发业务状态，才能说门磁对用户真正可用。</p></blockquote><h2 id="第-11-章：断线以后，为什么有时自动回来，有时必须重新配网？"><a href="#第-11-章：断线以后，为什么有时自动回来，有时必须重新配网？" class="headerlink" title="第 11 章：断线以后，为什么有时自动回来，有时必须重新配网？"></a>第 11 章：断线以后，为什么有时自动回来，有时必须重新配网？</h2><p>设备离线不等于所有身份和密钥都已丢失。</p><h3 id="Matter-over-Thread"><a href="#Matter-over-Thread" class="headerlink" title="Matter over Thread"></a>Matter over Thread</h3><p>如果设备仍保存 Thread Dataset 和 Fabric 凭据：</p><ul><li>Thread 可以重新选择父节点并 Reattach；</li><li>IPv6 地址或路由位置可能变化；</li><li>Controller 可以重新执行 Operational Discovery；</li><li>CASE 会话可以按需重新建立；</li><li>原有 Fabric 身份不需要因为一次无线掉线重新签发。</li></ul><p>因此：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Thread Reattach ≠ Matter Recommissioning</span><br><span class="line">CASE 重建 ≠ 重新扫码入网</span><br><span class="line">IPv6 地址变化 ≠ Matter Node 身份变化</span><br></pre></td></tr></table></figure><p>只有凭据被清除、Fabric 被移除、网络资料失效或设备恢复出厂等情况，才可能需要重新走完整 commissioning。</p><h3 id="Zigbee"><a href="#Zigbee" class="headerlink" title="Zigbee"></a>Zigbee</h3><p>设备如果保留网络参数和安全材料，可以通过 Rejoin 或恢复父子关系重新接入。Rejoin 与 Factory New 设备的首次 Network Steering 不是同一条路径。</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Zigbee Rejoin ≠ 首次 Network Steering</span><br><span class="line">父节点变化 ≠ EUI-64 身份变化</span><br><span class="line">短地址变化 ≠ 换了一台物理设备</span><br></pre></td></tr></table></figure><p>具体 Rejoin 安全条件仍取决于网络和 BDB 策略。</p><h2 id="第-12-章：Matter-这样设计，究竟比-Zigbee-好在哪里？"><a href="#第-12-章：Matter-这样设计，究竟比-Zigbee-好在哪里？" class="headerlink" title="第 12 章：Matter 这样设计，究竟比 Zigbee 好在哪里？"></a>第 12 章：Matter 这样设计，究竟比 Zigbee 好在哪里？</h2><p>不能用“步骤更多”直接推出“协议更安全”，也不能用“步骤更少”直接推出“体验更好”。更公平的比较是看两边选择解决什么问题，以及复杂度放在哪里。</p><h3 id="Matter-over-Thread-的主要设计收益"><a href="#Matter-over-Thread-的主要设计收益" class="headerlink" title="Matter over Thread 的主要设计收益"></a>Matter over Thread 的主要设计收益</h3><ul><li>把低功耗 IP 网络与 Matter 应用身份分开；</li><li>把初始配网秘密与长期运行证书分开；</li><li>定义设备认证和产品身份验证流程；</li><li>使用 Fabric、Node 身份和访问控制管理权限；</li><li>允许同一应用协议运行在 Thread、Wi-Fi 和 Ethernet 等 IP 承载上；</li><li>Thread Border Router 不需要翻译 Matter 应用数据；</li><li>每个阶段都有相对清晰的成功边界。</li></ul><h3 id="Matter-over-Thread-付出的成本"><a href="#Matter-over-Thread-付出的成本" class="headerlink" title="Matter over Thread 付出的成本"></a>Matter over Thread 付出的成本</h3><ul><li>角色、凭据和状态更多；</li><li>需要从 Bluetooth LE 临时通道切换到 Thread&#x2F;IP；</li><li>要处理 PASE、Attestation、证书、CASE 和访问控制；</li><li>Border Router、DNS-SD 或 IPv6 路径问题也会影响 commissioning；</li><li>日志里会出现更多“部分成功”状态。</li></ul><h3 id="经典-Zigbee-架构的主要特点"><a href="#经典-Zigbee-架构的主要特点" class="headerlink" title="经典 Zigbee 架构的主要特点"></a>经典 Zigbee 架构的主要特点</h3><ul><li>网络、安全和应用模型在同一套协议体系中；</li><li>典型网关架构下，首次加入链路比较集中；</li><li>Coordinator、Trust Center 和应用管理经常由同一网关统一提供；</li><li>低功耗 Mesh、Cluster、Binding 和 Reporting 形成成熟体系；</li><li>跨 IP 网络或跨 Matter 生态时，通常由网关或 Bridge 终止两边协议并做应用语义转换。</li></ul><h3 id="Zigbee-的复杂度并没有消失"><a href="#Zigbee-的复杂度并没有消失" class="headerlink" title="Zigbee 的复杂度并没有消失"></a>Zigbee 的复杂度并没有消失</h3><p>Zigbee 用户界面可能只显示“正在搜索设备”，但底层仍可能经历：</p><ul><li>多信道扫描；</li><li>父节点选择；</li><li>Association；</li><li>Trust Center 准入；</li><li>Transport Key；</li><li>Network Key 安装；</li><li>Frame Counter 与 Key Sequence 管理；</li><li>TCLK 相关流程；</li><li>Descriptor 查询；</li><li>Binding 与 Reporting。</li></ul><p>很多复杂度被网关集中处理，不代表它不存在。</p><h2 id="第-13-章：如果把每个-Matter-步骤删掉，会失去什么？"><a href="#第-13-章：如果把每个-Matter-步骤删掉，会失去什么？" class="headerlink" title="第 13 章：如果把每个 Matter 步骤删掉，会失去什么？"></a>第 13 章：如果把每个 Matter 步骤删掉，会失去什么？</h2><table><thead><tr><th>被删掉的步骤</th><th>直接失去的能力</th></tr></thead><tbody><tr><td>Commissioning Window</td><td>难以把用户意图与本次入网尝试绑定</td></tr><tr><td>QR &#x2F; Manual Code</td><td>缺少目标筛选和设备初始秘密</td></tr><tr><td>Bluetooth LE 临时通道</td><td>未加入 Thread 的普通设备难以直接从手机获得网络资料</td></tr><tr><td>PASE</td><td>缺少基于 Setup Passcode 的临时安全会话</td></tr><tr><td>Device Attestation</td><td>难以标准化验证设备认证身份和产品声明</td></tr><tr><td>CSR &#x2F; NOC</td><td>缺少设备独有的 Matter Fabric 运行身份</td></tr><tr><td>Thread Dataset</td><td>设备不知道怎样加入目标 Thread 网络</td></tr><tr><td>Operational Discovery</td><td>Controller 难以找到设备当前正式 IP 地址和端口</td></tr><tr><td>CASE</td><td>缺少基于 Fabric 证书的长期单播安全会话</td></tr><tr><td>CommissioningComplete</td><td>难以安全确认并结束本次配置，Fail-safe 无法正常闭环</td></tr><tr><td>Subscribe</td><td>平台可能无法持续获得门状态变化</td></tr></tbody></table><p>这张表也回答了开篇问题：Matter over Thread 的长流程并不是围绕一个“无线连接”问题反复加步骤，而是连续解决不同问题。</p><h2 id="第-14-章：用两个故障练习检查是否真的看懂"><a href="#第-14-章：用两个故障练习检查是否真的看懂" class="headerlink" title="第 14 章：用两个故障练习检查是否真的看懂"></a>第 14 章：用两个故障练习检查是否真的看懂</h2><h3 id="故障一：Matter-设备已经成为-Thread-Child，但-App-最后超时"><a href="#故障一：Matter-设备已经成为-Thread-Child，但-App-最后超时" class="headerlink" title="故障一：Matter 设备已经成为 Thread Child，但 App 最后超时"></a>故障一：Matter 设备已经成为 Thread Child，但 App 最后超时</h3><p>已经证明：</p><ul><li>设备找到了 Thread 网络；</li><li>Parent-Child 关系已经建立；</li><li>Thread Attach 至少进展到网络层成功附近。</li></ul><p>还要继续检查：</p><ol><li>设备是否发布或代理了运行态发现信息；</li><li>Commissioner 是否完成 Operational Discovery；</li><li>CASE 是否建立；</li><li>CommissioningComplete 是否成功；</li><li>后续 Read、Subscribe 和 Report 是否出现。</li></ol><p>不能因为 Thread Attached 就直接修改应用 Cluster，也不能把超时一概归因于射频。</p><h3 id="故障二：Zigbee-门磁已经获得短地址，但网关没有显示设备"><a href="#故障二：Zigbee-门磁已经获得短地址，但网关没有显示设备" class="headerlink" title="故障二：Zigbee 门磁已经获得短地址，但网关没有显示设备"></a>故障二：Zigbee 门磁已经获得短地址，但网关没有显示设备</h3><p>已经证明：</p><ul><li>Association 至少进展到地址分配；</li><li>设备与父节点之间有基本链路。</li></ul><p>还要继续检查：</p><ol><li>Transport Key 是否到达接收端；</li><li>设备是否成功验证并安装 Network Key；</li><li>是否开始受保护的 NWK 通信；</li><li>BDB 和目标 TCLK 流程是否完成；</li><li>Device Announcement 和 Descriptor 查询是否成功；</li><li>Reporting 是否正确配置。</li></ol><p>不能把 MAC ACK、Association Response 或短地址当成完整业务成功。</p><h2 id="一张最终时间线"><a href="#一张最终时间线" class="headerlink" title="一张最终时间线"></a>一张最终时间线</h2><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">Matter over Thread</span><br><span class="line"></span><br><span class="line">用户打开配网</span><br><span class="line">  -&gt; 扫码获得引导信息</span><br><span class="line">  -&gt; 发现并连接 BLE 设备</span><br><span class="line">  -&gt; PASE 临时安全会话</span><br><span class="line">  -&gt; Fail-safe 与基础配置</span><br><span class="line">  -&gt; Device Attestation</span><br><span class="line">  -&gt; CSR / NOC / Fabric 身份</span><br><span class="line">  -&gt; 配置 Thread Dataset</span><br><span class="line">  -&gt; Thread Attach</span><br><span class="line">  -&gt; Operational Discovery</span><br><span class="line">  -&gt; CASE 正式安全会话</span><br><span class="line">  -&gt; CommissioningComplete</span><br><span class="line">  -&gt; 读取设备能力并建立 Subscribe</span><br><span class="line">  -&gt; 门磁业务可用</span><br></pre></td></tr></table></figure><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">经典 Zigbee 集中式首次加入</span><br><span class="line"></span><br><span class="line">网关打开 Permit Join</span><br><span class="line">  -&gt; 设备启动 Network Steering</span><br><span class="line">  -&gt; 扫描信道与 Beacon</span><br><span class="line">  -&gt; 选择网络和父节点</span><br><span class="line">  -&gt; Association</span><br><span class="line">  -&gt; 获得短地址</span><br><span class="line">  -&gt; Trust Center 安全准入</span><br><span class="line">  -&gt; 接收并安装 Network Key</span><br><span class="line">  -&gt; 受保护的 Zigbee 通信</span><br><span class="line">  -&gt; Device Announcement</span><br><span class="line">  -&gt; TCLK / BDB 相应流程</span><br><span class="line">  -&gt; Descriptor / Cluster 识别</span><br><span class="line">  -&gt; Binding / Reporting</span><br><span class="line">  -&gt; 门磁业务可用</span><br></pre></td></tr></table></figure><h2 id="最后只记住三句话"><a href="#最后只记住三句话" class="headerlink" title="最后只记住三句话"></a>最后只记住三句话</h2><blockquote><p>Thread 解决设备怎样接入低功耗 IPv6 网络。</p></blockquote><blockquote><p>Matter 解决设备是谁、属于哪个信任域、谁能控制它，以及怎样表达设备能力。</p></blockquote><blockquote><p>Zigbee 使用另一套更集中的网络、安全和应用体系完成相似的产品目标。</p></blockquote><p>Matter over Thread 不是简单地把 Zigbee 的一次入网拆成更多消息。它把网络接入、初始秘密、产品身份、长期 Fabric 身份、正式安全会话和应用权限分别建模。</p><p>它的优势来自这种拆分，它的复杂度也来自这种拆分。</p><h2 id="术语速查"><a href="#术语速查" class="headerlink" title="术语速查"></a>术语速查</h2><table><thead><tr><th>术语</th><th>所属体系</th><th>一句话解释</th></tr></thead><tbody><tr><td>Commissioning</td><td>Matter</td><td>把设备安全登记进 Fabric 并配置运行网络的完整过程</td></tr><tr><td>Commissioner</td><td>Matter</td><td>负责办理 commissioning 的一方</td></tr><tr><td>Commissionee</td><td>Matter</td><td>正在被 commissioning 的设备</td></tr><tr><td>Fabric</td><td>Matter</td><td>共享运行信任根和管理关系的 Matter 信任域</td></tr><tr><td>PASE</td><td>Matter</td><td>基于 Setup Passcode 的初始安全会话</td></tr><tr><td>Device Attestation</td><td>Matter</td><td>验证设备认证身份、签名和产品声明的流程</td></tr><tr><td>DAC &#x2F; PAI &#x2F; PAA</td><td>Matter</td><td>从设备证书到信任根的认证链角色</td></tr><tr><td>CSR</td><td>Matter</td><td>设备为运行证书提交的签名请求</td></tr><tr><td>NOC</td><td>Matter</td><td>Matter Node 在某个 Fabric 中的运行证书</td></tr><tr><td>CASE</td><td>Matter</td><td>基于 Fabric 运行凭据的正式单播安全会话</td></tr><tr><td>Operational Discovery</td><td>Matter</td><td>在正式 IP 网络中解析已入网 Node 的地址和端口</td></tr><tr><td>Active Operational Dataset</td><td>Thread</td><td>描述 Thread 网络参数和安全材料的配置集合</td></tr><tr><td>MLE Attach</td><td>Thread</td><td>发现 Parent 并建立 Thread 邻居关系的过程</td></tr><tr><td>Border Router</td><td>Thread</td><td>在 Thread 与相邻 IP 网络之间转发数据</td></tr><tr><td>Permit Join</td><td>Zigbee</td><td>网络暂时允许新设备申请加入</td></tr><tr><td>Network Steering</td><td>Zigbee</td><td>设备寻找并尝试加入合适网络的 BDB 方法</td></tr><tr><td>Association</td><td>IEEE 802.15.4 &#x2F; Zigbee 路径</td><td>建立网络成员和父子关系的 MAC 服务</td></tr><tr><td>Network Key</td><td>Zigbee</td><td>保护 Zigbee NWK 层通信的网络共享密钥</td></tr><tr><td>TCLK</td><td>Zigbee</td><td>设备与 Trust Center 之间的 Link Key</td></tr><tr><td>Device Announcement</td><td>Zigbee</td><td>设备向网络公告地址映射等信息</td></tr><tr><td>Reporting</td><td>Zigbee</td><td>按条件或周期报告属性变化</td></tr></tbody></table><h2 id="继续阅读与资料来源"><a href="#继续阅读与资料来源" class="headerlink" title="继续阅读与资料来源"></a>继续阅读与资料来源</h2><p>站内延伸：</p><ul><li><a href="/posts/matter-foundations/">Matter 基础概念：Node、Fabric、Endpoint 与安全会话</a></li><li><a href="/posts/thread-foundations/">Thread 基础概念：IPv6 Mesh、设备角色与 Border Router</a></li><li><a href="/posts/matter-zigbee-concept-mapping/">Matter 与 Zigbee 关系映射：相似名词不等于相同协议</a></li><li><a href="/posts/zigbee-network-joining-flow/">Zigbee 入网流程：从 Network Steering 到可用设备</a></li><li><a href="/posts/zigbee-security-key-scope/">Zigbee 各类 Key 的作用范围</a></li></ul><p>公开技术资料：</p><ul><li><a href="https://csa-iot.org/developer-resource/specifications-download-request/">CSA 规范下载入口</a></li><li><a href="https://project-chip.github.io/connectedhomeip-doc/development_controllers/chip-tool/chip_tool_guide.html">Matter 官方开源 SDK：CHIP Tool Commissioning Guide</a></li><li><a href="https://project-chip.github.io/connectedhomeip-doc/guides/access-control-guide.html">Matter SDK Access Control Guide</a></li><li><a href="https://openthread.io/guides/thread-primer/network-discovery">OpenThread：Network Discovery and Formation</a></li><li><a href="https://openthread.io/guides/thread-primer/node-roles-and-types">OpenThread：Node Roles and Types</a></li><li><a href="https://threadgroup.org/Newsroom/Blog/what-is-a-thread-border-router-and-how-is-it-different-from-a-hub-or-a-bridge">Thread Group：Border Router 的职责</a></li><li><a href="https://csa-iot.org/wp-content/uploads/2023/04/05-3474-23-csg-zigbee-specification-compressed.pdf">CSA Zigbee Specification</a></li></ul><p>本文描述的是便于理解的高层主线，不替代目标版本的 Matter Core、Thread、Zigbee Core、BDB 与生态实现要求。认证、量产和故障定责还应结合目标版本规范、设备日志、Controller 或网关日志与抓包证据。</p>]]>
    </content>
    <id>https://oniums.github.io/posts/matter-over-thread-zigbee-commissioning-comparison/</id>
    <link href="https://oniums.github.io/posts/matter-over-thread-zigbee-commissioning-comparison/"/>
    <published>2026-07-30T02:00:00.000Z</published>
    <summary>
      <![CDATA[<p>Zigbee 设备打开允许加入、搜索网络、获得密钥，似乎就能完成入网。Matter over Thread 为什么还要扫码、连接 Bluetooth LE、建立临时安全通道、验证设备证书、写入长期身份、加入 Thread，再切换到 IP 网络重新连接？</p>
<p>这些步骤是在把简单问题复杂化，还是在解决不同的问题？</p>]]>
    </summary>
    <title>Matter over Thread 入网为什么这么复杂？与 Zigbee 一步一步对照看懂</title>
    <updated>2026-07-30T02:54:09.518Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="生态适配" scheme="https://oniums.github.io/categories/%E7%94%9F%E6%80%81%E9%80%82%E9%85%8D/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="SmartThings" scheme="https://oniums.github.io/tags/smartthings/"/>
    <category term="Edge Driver" scheme="https://oniums.github.io/tags/edge-driver/"/>
    <category term="Lua" scheme="https://oniums.github.io/tags/lua/"/>
    <category term="CLI" scheme="https://oniums.github.io/tags/cli/"/>
    <content>
      <![CDATA[<p>写完 SmartThings Edge Driver 只是第一步。要让测试者通过一个链接安装 Driver，还要完成云端上传、Channel 版本绑定、Hub 注册、Driver 安装和邀请创建。这些动作处在不同层级，任何一步“成功”都不能替代下一步验证。</p><span id="more"></span><h2 id="先理解完整链路"><a href="#先理解完整链路" class="headerlink" title="先理解完整链路"></a>先理解完整链路</h2><p>一条可复用的发布链是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">本地源码</span><br><span class="line">  -&gt; fingerprint / profile / handler 静态检查</span><br><span class="line">  -&gt; 本地 build-only 打包</span><br><span class="line">  -&gt; 上传 Driver，产生 Driver ID 与 Version</span><br><span class="line">  -&gt; 创建或选择 Driver Channel</span><br><span class="line">  -&gt; 把指定 Driver Version 分配到 Channel</span><br><span class="line">  -&gt; 将测试 Hub 注册到 Channel</span><br><span class="line">  -&gt; 从 Channel 安装 Driver 到 Hub</span><br><span class="line">  -&gt; 创建 Channel Invitation</span><br><span class="line">  -&gt; 通过真实设备完成运行验证</span><br></pre></td></tr></table></figure><p>其中有三个容易混淆的对象：</p><table><thead><tr><th>对象</th><th>作用</th><th>是否秘密</th></tr></thead><tbody><tr><td>PAT &#x2F; OAuth Token</td><td>允许 CLI 调用 SmartThings API</td><td>是</td></tr><tr><td>Driver Channel</td><td>固定并分发一组 Driver 版本</td><td>通常不公开其管理信息</td></tr><tr><td>Invitation URL</td><td>邀请其他账号加入 Channel</td><td>按分享范围管理</td></tr></tbody></table><p>邀请链接不是登录 Token。创建邀请后，不需要把 PAT 一起发给测试者；PAT 过期也不等于已经创建的邀请立即失效。</p><h2 id="CLI-认证：PAT-适合临时操作"><a href="#CLI-认证：PAT-适合临时操作" class="headerlink" title="CLI 认证：PAT 适合临时操作"></a>CLI 认证：PAT 适合临时操作</h2><p>SmartThings CLI 默认支持浏览器 OAuth 登录，也支持使用 Personal Access Token。当前新建 PAT 的有效期是 24 小时，不能设置成永久 Token。</p><p>在临时测试或无图形界面的开发机上，可以把 PAT 放进 CLI 配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">default:</span></span><br><span class="line">  <span class="attr">token:</span> <span class="string">&quot;&lt;YOUR_PAT&gt;&quot;</span></span><br></pre></td></tr></table></figure><p>配置文件通常位于：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~/.config/@smartthings/cli/config.yaml</span><br></pre></td></tr></table></figure><p>限制文件权限：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> 600 ~/.config/@smartthings/cli/config.yaml</span><br></pre></td></tr></table></figure><p>不要把真实 Token 写进 Git、脚本、截图或文章，也尽量不要通过 <code>--token</code> 直接放在命令行中，以免进入 Shell history。出现 <code>401 Unauthorized</code> 时，先检查 Token 是否已过期，以及创建 PAT 时是否选择了所需 Scope。管理 Driver Channel 时，至少要注意官方文档要求的 Channel 读写权限。</p><p>如果是长期运行的服务集成，应使用 OAuth 2.0 和刷新机制，而不是定期手工更换 PAT。对于偶尔执行一次的 CLI 发布任务，短期 PAT 更简单，但要接受每天重新签发的限制。</p><h2 id="设备身份：允许别名，但不要放宽匹配"><a href="#设备身份：允许别名，但不要放宽匹配" class="headerlink" title="设备身份：允许别名，但不要放宽匹配"></a>设备身份：允许别名，但不要放宽匹配</h2><p>实际项目中，同一类硬件可能因固件批次、销售渠道或身份迁移而报告不同 Model。此时可以让多个精确 fingerprint 指向同一个 Profile：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">zigbeeManufacturer:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">&quot;example/motion-a&quot;</span></span><br><span class="line">    <span class="attr">manufacturer:</span> <span class="string">&quot;Example&quot;</span></span><br><span class="line">    <span class="attr">model:</span> <span class="string">&quot;MOTION-A&quot;</span></span><br><span class="line">    <span class="attr">deviceProfileName:</span> <span class="string">&quot;motion-light-battery&quot;</span></span><br><span class="line">    <span class="attr">deviceLabel:</span> <span class="string">&quot;Motion sensor&quot;</span></span><br><span class="line"></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">&quot;example/motion-b&quot;</span></span><br><span class="line">    <span class="attr">manufacturer:</span> <span class="string">&quot;Example&quot;</span></span><br><span class="line">    <span class="attr">model:</span> <span class="string">&quot;MOTION-B&quot;</span></span><br><span class="line">    <span class="attr">deviceProfileName:</span> <span class="string">&quot;motion-light-battery&quot;</span></span><br><span class="line">    <span class="attr">deviceLabel:</span> <span class="string">&quot;Motion sensor&quot;</span></span><br></pre></td></tr></table></figure><p>这表示两个身份共享同一组平台能力，不表示它们的 Zigbee 行为必然完全相同。至少要逐项比较：</p><ul><li>Endpoint、Device ID 与 Server&#x2F;Client Cluster；</li><li>Attribute 类型、单位、倍率和无效值；</li><li>上报方式、Reporting 配置与休眠行为；</li><li>厂商自定义 Cluster、Attribute 和 Command；</li><li>首次加入、rejoin、重启和恢复出厂行为。</li></ul><p>Sub-driver 的 <code>can_handle</code> 也要覆盖同一组身份，并同时检查 Manufacturer 和 Model：</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> supported_models = &#123;</span><br><span class="line">  [<span class="string">&quot;MOTION-A&quot;</span>] = <span class="literal">true</span>,</span><br><span class="line">  [<span class="string">&quot;MOTION-B&quot;</span>] = <span class="literal">true</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> <span class="function"><span class="keyword">function</span> <span class="title">can_handle</span><span class="params">(_, _, device)</span></span></span><br><span class="line">  <span class="keyword">return</span> device:get_manufacturer() == <span class="string">&quot;Example&quot;</span></span><br><span class="line">    <span class="keyword">and</span> supported_models[device:get_model()] == <span class="literal">true</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>只检查 Model、只检查 Manufacturer，或让 fingerprint 与 <code>can_handle</code> 使用不同的身份集合，都会造成“选中了 Profile，却没有进入预期 Handler”的隐蔽故障。</p><h2 id="Profile-只声明真实支持的能力"><a href="#Profile-只声明真实支持的能力" class="headerlink" title="Profile 只声明真实支持的能力"></a>Profile 只声明真实支持的能力</h2><p>一个运动传感器 Profile 可能包含：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">motion-light-battery</span></span><br><span class="line"><span class="attr">components:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">main</span></span><br><span class="line">    <span class="attr">capabilities:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">motionSensor</span></span><br><span class="line">        <span class="attr">version:</span> <span class="number">1</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">illuminanceMeasurement</span></span><br><span class="line">        <span class="attr">version:</span> <span class="number">1</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">battery</span></span><br><span class="line">        <span class="attr">version:</span> <span class="number">1</span></span><br><span class="line">    <span class="attr">categories:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">MotionSensor</span></span><br></pre></td></tr></table></figure><p>如果 Driver 还支持检测保持时间、照度补偿等参数，可以通过 Preference 或自定义 Capability 暴露，但必须确认：</p><ol><li>Driver 能把 App 命令正确转换成 Zigbee Write 或 Command；</li><li>设备会接受该值；</li><li>Read Back 或后续 Report 能校正平台状态；</li><li>参数范围和单位与固件一致。</li></ol><p>UI 中出现一个字段，只证明 Profile 被平台接受，不证明设备端功能已经闭环。</p><h2 id="本地门禁：先打包，不上传"><a href="#本地门禁：先打包，不上传" class="headerlink" title="本地门禁：先打包，不上传"></a>本地门禁：先打包，不上传</h2><p>在执行任何云端写操作前，先完成本地检查：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">luac -p src/init.lua</span><br><span class="line">luac -p src/sub_drivers/example/init.lua</span><br></pre></td></tr></table></figure><p>再检查 YAML、JSON、fingerprint、Profile 和 Handler 的一致性。最关键的交叉关系包括：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">fingerprint.deviceProfileName -&gt; profiles/&lt;name&gt;.yml</span><br><span class="line">fingerprint Manufacturer/Model -&gt; can_handle 支持集合</span><br><span class="line">profile capability -&gt; 默认 Handler 或自定义 Handler</span><br><span class="line">config.yml permissions -&gt; 实际使用的 Zigbee Cluster</span><br></pre></td></tr></table></figure><p>SmartThings CLI 的无上传构建命令是：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:drivers:package \</span><br><span class="line">  --build-only /tmp/example-driver.zip \</span><br><span class="line">  &lt;driver-directory&gt;</span><br></pre></td></tr></table></figure><p>注意命令中的 <code>drivers</code> 是复数。<code>--build-only</code> 只生成 Zip，用于验证目录和包结构，不会创建云端 Driver 版本。</p><p>本地打包通过仍然不能证明：</p><ul><li>fingerprint 能匹配真实设备；</li><li>Hub 上能加载 Driver；</li><li>Zigbee 上报能转成正确 Capability event；</li><li>App 下发能到达设备；</li><li>rejoin、重启和低功耗场景正常。</li></ul><h2 id="上传-Driver"><a href="#上传-Driver" class="headerlink" title="上传 Driver"></a>上传 Driver</h2><p>确认本地门禁通过后，再执行上传：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:drivers:package --json &lt;driver-directory&gt;</span><br></pre></td></tr></table></figure><p>保存返回结果中的：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Driver ID</span><br><span class="line">Driver Version</span><br></pre></td></tr></table></figure><p>同一个 Driver 每次重新打包上传都会产生新 Version。后续给 Channel 分配时要使用这次返回的准确版本，不要只记 Driver ID。</p><p>执行云端写操作前，建议先列出现有资源：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:drivers --json</span><br><span class="line">smartthings edge:channels --json</span><br><span class="line">smartthings devices --<span class="built_in">type</span>=HUB --json</span><br></pre></td></tr></table></figure><p>这样可以避免重复创建 Channel，也能确认当前账号、区域和 Hub 是否正确。</p><h2 id="创建-Channel-并绑定版本"><a href="#创建-Channel-并绑定版本" class="headerlink" title="创建 Channel 并绑定版本"></a>创建 Channel 并绑定版本</h2><p>第一次发布时创建 Driver Channel：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:channels:create</span><br></pre></td></tr></table></figure><p>CLI 会交互式询问名称、描述和服务条款链接。也可以先准备 JSON&#x2F;YAML，再使用 <code>--input</code>。</p><p>将刚上传的指定版本分配到 Channel：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:channels:assign \</span><br><span class="line">  &lt;DRIVER_ID&gt; \</span><br><span class="line">  &lt;DRIVER_VERSION&gt; \</span><br><span class="line">  --channel &lt;CHANNEL_ID&gt;</span><br></pre></td></tr></table></figure><p>Channel 锁定的是一个具体 Driver Version。以后上传新版本，不会自动替换 Channel 中的旧版本，也不会自动更新 Hub 上已安装的版本。</p><p>如果创建 Channel 时遇到 <code>502</code> 或响应体为空，不要立即连续重试。先重新查询：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:channels --json</span><br></pre></td></tr></table></figure><p>确认服务端是否已经创建成功，再决定是否重试，避免产生重复 Channel。</p><h2 id="Enroll-Hub-并安装-Driver"><a href="#Enroll-Hub-并安装-Driver" class="headerlink" title="Enroll Hub 并安装 Driver"></a>Enroll Hub 并安装 Driver</h2><p>先把自己的测试 Hub 注册到 Channel：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:channels:enroll \</span><br><span class="line">  &lt;HUB_ID&gt; \</span><br><span class="line">  --channel &lt;CHANNEL_ID&gt;</span><br></pre></td></tr></table></figure><p>然后从该 Channel 安装 Driver：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:drivers:install \</span><br><span class="line">  &lt;DRIVER_ID&gt; \</span><br><span class="line">  --hub &lt;HUB_ID&gt; \</span><br><span class="line">  --channel &lt;CHANNEL_ID&gt;</span><br></pre></td></tr></table></figure><p>这两步解决的问题不同：</p><ul><li><code>enroll</code>：让 Hub 能看到 Channel；</li><li><code>install</code>：把 Channel 中的某个 Driver 安装到 Hub。</li></ul><p>只有 Channel 中存在正确版本、Hub 已注册、Driver 已安装，真实设备加入时才有机会匹配该 Driver。</p><p>更新版本时，可以重新 Assign 并 Install，也可以使用官方提供的组合方式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:drivers:package \</span><br><span class="line">  &lt;driver-directory&gt; \</span><br><span class="line">  --install \</span><br><span class="line">  --channel &lt;CHANNEL_ID&gt; \</span><br><span class="line">  --hub &lt;HUB_ID&gt;</span><br></pre></td></tr></table></figure><p>组合命令更方便，拆分命令则更适合首次发布和故障定位，因为每一步的输入与结果更清楚。</p><h2 id="生成并核对邀请链接"><a href="#生成并核对邀请链接" class="headerlink" title="生成并核对邀请链接"></a>生成并核对邀请链接</h2><p>为指定 Channel 创建邀请：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:channels:invites:create \</span><br><span class="line">  --channel &lt;CHANNEL_ID&gt;</span><br></pre></td></tr></table></figure><p>CLI 会根据交互选项创建 Invitation，并返回分享 URL。创建后再查询一次：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:channels:invites \</span><br><span class="line">  --channel &lt;CHANNEL_ID&gt;</span><br></pre></td></tr></table></figure><p>核对邀请是否存在、状态是否符合预期、URL 是否对应目标 Channel。不要只以“命令没有报错”作为成功证据。</p><p>收到链接的测试者通常需要：</p><ol><li>打开 Invitation URL 并登录 Samsung Account；</li><li>接受邀请；</li><li>选择自己的 Hub 加入 Channel；</li><li>在可用 Driver 中选择并安装；</li><li>在 SmartThings App 中重新添加或迁移目标设备。</li></ol><p>第三方 Edge Driver 不等于 SmartThings 官方审核或认证 Driver。邀请页可访问、Driver 可安装，也不能替代真实设备验证和正式生态认证。</p><h2 id="验证结果必须分层"><a href="#验证结果必须分层" class="headerlink" title="验证结果必须分层"></a>验证结果必须分层</h2><p>建议用下面的矩阵记录结果：</p><table><thead><tr><th>层级</th><th>最小证据</th><th>能证明什么</th></tr></thead><tbody><tr><td>静态</td><td>Lua、YAML、JSON 检查通过</td><td>文件可解析、引用基本一致</td></tr><tr><td>本地构建</td><td><code>--build-only</code> 成功生成 Zip</td><td>Driver 包结构可构建</td></tr><tr><td>云端上传</td><td>返回 Driver ID 与 Version</td><td>云端接受该版本</td></tr><tr><td>Channel</td><td>查询到准确 Driver Version</td><td>目标版本已进入分发通道</td></tr><tr><td>Hub</td><td>安装列表中存在 Driver</td><td>Driver 已部署到目标 Hub</td></tr><tr><td>匹配</td><td>Live log 显示目标设备选中 Driver</td><td>fingerprint 与 Handler 生效</td></tr><tr><td>上行</td><td>运动、照度、电量事件正确</td><td>Zigbee 到 Capability 路径成立</td></tr><tr><td>下行</td><td>参数写入、响应与 Read Back 正确</td><td>Capability 到 Zigbee 路径成立</td></tr><tr><td>稳定性</td><td>重启、rejoin、休眠恢复通过</td><td>生命周期和低功耗行为可用</td></tr></tbody></table><p>查看 Hub 运行日志可使用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:drivers:logcat --hub-address=&lt;HUB_IP&gt;</span><br></pre></td></tr></table></figure><p>日志公开前要删除设备 EUI、Hub 地址、Location、账号信息、Channel&#x2F;Driver UUID 和任何网络密钥。</p><h2 id="发布前隐私清单"><a href="#发布前隐私清单" class="headerlink" title="发布前隐私清单"></a>发布前隐私清单</h2><p>提交 Driver 或技术文章前，至少搜索：</p><ul><li>PAT、OAuth Client Secret、Refresh Token；</li><li>Invitation URL 与短码；</li><li>Hub、Location、Channel、Driver、Device UUID；</li><li>设备 EUI、Network Key、Install Code；</li><li>内部仓库地址、本机绝对路径和公司环境名称；</li><li>未公开产品型号、固件版本和测试记录。</li></ul><p>示例应使用 <code>&lt;CHANNEL_ID&gt;</code>、<code>&lt;DRIVER_ID&gt;</code>、<code>&lt;HUB_ID&gt;</code> 等占位符。邀请链接虽然不是账号 Token，但获得链接的人可能因此访问测试 Channel，仍应按预期受众控制传播。</p><h2 id="最后形成可重复的发布节奏"><a href="#最后形成可重复的发布节奏" class="headerlink" title="最后形成可重复的发布节奏"></a>最后形成可重复的发布节奏</h2><p>一次稳妥的迭代可以压缩为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">精确身份匹配</span><br><span class="line">  -&gt; 本地静态检查</span><br><span class="line">  -&gt; build-only</span><br><span class="line">  -&gt; 上传新 Version</span><br><span class="line">  -&gt; Channel 重新 Assign</span><br><span class="line">  -&gt; Hub 重新 Install</span><br><span class="line">  -&gt; Live log + 真实设备回归</span><br><span class="line">  -&gt; 最后才分享 Invitation URL</span><br></pre></td></tr></table></figure><p>把每一步的 ID、Version、命令结果和验证结论保存在私有发布记录中；公开文章只保留方法、占位符和经过脱敏的错误现象。这样既能让发布流程可追溯，也不会把认证凭证和测试环境暴露出去。</p><p>官方资料：</p><ul><li><a href="https://developer.smartthings.com/docs/sdks/cli">SmartThings CLI 与认证</a></li><li><a href="https://developer.smartthings.com/docs/devices/hub-connected/driver-channels">Driver Channels</a></li><li><a href="https://developer.smartthings.com/docs/devices/hub-connected/enroll-in-a-shared-channel">接受共享 Channel 并安装 Driver</a></li><li><a href="https://developer.smartthings.com/docs/getting-started/authorization-and-permissions">Authorization and Permissions</a></li><li><a href="https://developer.smartthings.com/docs/devices/hub-connected/driver-components-and-structure">Edge Driver 结构</a></li></ul>]]>
    </content>
    <id>https://oniums.github.io/posts/smartthings-edge-driver-channel-invitation-workflow/</id>
    <link href="https://oniums.github.io/posts/smartthings-edge-driver-channel-invitation-workflow/"/>
    <published>2026-07-30T01:30:00.000Z</published>
    <summary>
      <![CDATA[<p>写完 SmartThings Edge Driver 只是第一步。要让测试者通过一个链接安装 Driver，还要完成云端上传、Channel 版本绑定、Hub 注册、Driver 安装和邀请创建。这些动作处在不同层级，任何一步“成功”都不能替代下一步验证。</p>]]>
    </summary>
    <title>SmartThings Edge Driver 发布实战：从本地打包到 Channel 邀请链接</title>
    <updated>2026-07-30T01:39:23.355Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="优化实践" scheme="https://oniums.github.io/categories/%E4%BC%98%E5%8C%96%E5%AE%9E%E8%B7%B5/"/>
    <category term="低功耗" scheme="https://oniums.github.io/tags/%E4%BD%8E%E5%8A%9F%E8%80%97/"/>
    <category term="I2C" scheme="https://oniums.github.io/tags/i2c/"/>
    <category term="传感器" scheme="https://oniums.github.io/tags/%E4%BC%A0%E6%84%9F%E5%99%A8/"/>
    <category term="状态机" scheme="https://oniums.github.io/tags/%E7%8A%B6%E6%80%81%E6%9C%BA/"/>
    <content>
      <![CDATA[<p>在电池供电设备里，环境光传感器通常只需要偶尔提供一个 lux 值。此时“传感器带 IRQ”并不代表 IRQ 一定比定时读取省电。真正决定功耗的是：等待 IRQ 时传感器处于什么模式、转换期间 MCU 能否休眠，以及业务究竟需要周期数据还是实时阈值事件。</p><span id="more"></span><h2 id="先确认-IRQ-的真实语义"><a href="#先确认-IRQ-的真实语义" class="headerlink" title="先确认 IRQ 的真实语义"></a>先确认 IRQ 的真实语义</h2><p>环境光传感器常见的 IRQ 至少有两种：</p><ul><li><strong>Data Ready IRQ</strong>：一次转换完成后通知 MCU 读取结果；</li><li><strong>Threshold IRQ</strong>：光照超过高阈值或低于低阈值时通知 MCU。</li></ul><p>两者看起来都是一根中断线，功耗含义却完全不同。</p><p>Data Ready IRQ 如果能配合 single-shot 使用，传感器完成一次转换后可能自动回到 Standby，适合低功耗周期采集。Threshold IRQ 往往要求传感器保持连续测量；MCU 虽然可以睡眠，传感器本身却持续消耗 Active 电流。</p><p>因此，决定是否使用 IRQ 前应先回答：</p><ol><li>IRQ 是转换完成中断，还是阈值中断？</li><li>IRQ 开启后，传感器能否自动回到 Standby？</li><li>传感器等待 IRQ 时的典型和最大电流是多少？</li><li>业务能接受多大的光照变化检测延迟？</li></ol><p>如果数据手册只描述“Active 模式下设置上下阈值并等待中断”，它通常不是低功耗 single-shot 完成中断。</p><h2 id="常见的四类设计"><a href="#常见的四类设计" class="headerlink" title="常见的四类设计"></a>常见的四类设计</h2><h3 id="Single-shot-或-Forced-Mode"><a href="#Single-shot-或-Forced-Mode" class="headerlink" title="Single-shot 或 Forced Mode"></a>Single-shot 或 Forced Mode</h3><p>主机发起一次转换，传感器完成后自动回到低功耗模式。MCU 可以通过定时器或 Data Ready IRQ 回收结果：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">MCU 启动转换</span><br><span class="line">  -&gt; MCU 休眠</span><br><span class="line">  -&gt; 转换完成</span><br><span class="line">  -&gt; 定时器或 Data Ready 唤醒</span><br><span class="line">  -&gt; 读取结果</span><br><span class="line">  -&gt; 传感器已回到 Standby</span><br></pre></td></tr></table></figure><p>这是周期采集最理想的模式，因为不需要 MCU 额外发命令关闭传感器，也不容易因错误路径遗漏休眠动作。</p><h3 id="超低功耗自主阈值监测"><a href="#超低功耗自主阈值监测" class="headerlink" title="超低功耗自主阈值监测"></a>超低功耗自主阈值监测</h3><p>部分器件有独立的低功耗比较器或专用监测引擎。它可以用较低电流持续判断阈值，只在环境发生明显变化时拉起 IRQ。</p><p>这种方案适合“平时不关心具体 lux，只在由亮变暗时唤醒”的产品，但必须确认数据手册给出的电流属于低功耗监测引擎，而不是普通连续测量模式。</p><h3 id="MCU-定时按需采样"><a href="#MCU-定时按需采样" class="headerlink" title="MCU 定时按需采样"></a>MCU 定时按需采样</h3><p>如果器件只有 Standby 和连续 Active，没有 single-shot 自动回休眠能力，常见做法是由 MCU 把一次连续模式裁剪成一次采样：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">传感器保持 Standby</span><br><span class="line">  -&gt; 软件定时器到期</span><br><span class="line">  -&gt; MCU 设置 Active</span><br><span class="line">  -&gt; MCU 等待转换，条件允许时再次休眠</span><br><span class="line">  -&gt; 检查 Data Valid / New Data</span><br><span class="line">  -&gt; 读取结果</span><br><span class="line">  -&gt; 设置 Standby</span><br></pre></td></tr></table></figure><p>这种设计需要状态机保证任何成功、超时或 I2C 错误路径最终都能让传感器回到 Standby，但其平均电流通常远低于长期连续 Active。</p><h3 id="连续测量加阈值-IRQ"><a href="#连续测量加阈值-IRQ" class="headerlink" title="连续测量加阈值 IRQ"></a>连续测量加阈值 IRQ</h3><p>如果产品必须在较短时间内发现明暗变化，连续模式可能无法避免。此时可以：</p><ul><li>使用业务可接受的最长 Measurement Rate；</li><li>设置高低阈值迟滞，避免在临界点反复触发；</li><li>使用 persistence，要求连续多次越界后才中断；</li><li>MCU 被唤醒后批量处理必要工作，再尽快睡眠。</li></ul><p>这是用功耗换取响应速度，不应包装成单纯的“IRQ 低功耗优化”。</p><h2 id="用占空比估算方向"><a href="#用占空比估算方向" class="headerlink" title="用占空比估算方向"></a>用占空比估算方向</h2><p>假设某颗环境光传感器的参数为：</p><ul><li>Active 电流：266 µA；</li><li>Standby 电流：5 µA；</li><li>每 5 秒采集一次；</li><li>每次从唤醒到读取完成需要 25 ms。</li></ul><p>只考虑传感器自身，平均电流约为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(266 µA × 25 ms + 5 µA × 4975 ms) / 5000 ms</span><br><span class="line">≈ 6.3 µA</span><br></pre></td></tr></table></figure><p>如果异常情况下等待和重试使 Active 时间增加到 55 ms：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(266 µA × 55 ms + 5 µA × 4945 ms) / 5000 ms</span><br><span class="line">≈ 7.9 µA</span><br></pre></td></tr></table></figure><p>而长期连续 Active 可能仍是数百微安量级。这个简单估算足以判断架构方向，但不能代替测量：</p><ul><li>手册电流可能只对应某组 Integration Time 和 Measurement Rate；</li><li>Active 内部可能包含测量和 idle，平均电流未必能线性外推；</li><li>MCU、I2C 上拉、稳压器和无线唤醒也会影响整机结果；</li><li>Standby 电流应同时关注典型值、最大值和温度范围。</li></ul><h2 id="优先优化采样策略，而不是一两毫秒"><a href="#优先优化采样策略，而不是一两毫秒" class="headerlink" title="优先优化采样策略，而不是一两毫秒"></a>优先优化采样策略，而不是一两毫秒</h2><p>低功耗优化最容易陷入“把等待时间减少 2 ms”的局部问题。对于几秒甚至几十秒才采一次的数据，减少采样次数通常更有效。</p><h3 id="合并周期请求和事件请求"><a href="#合并周期请求和事件请求" class="headerlink" title="合并周期请求和事件请求"></a>合并周期请求和事件请求</h3><p>设备可能同时有固定周期采样和外部事件触发采样。如果事件刚好发生在周期采样之后，重复启动一次转换没有必要。</p><p>可以维护：</p><ul><li>最近一次有效样本的时间；</li><li>当前是否正在采样；</li><li>哪些业务正在等待本次结果；</li><li>是否还存在必须重新采样的 pending 请求。</li></ul><p>如果样本足够新就直接复用；如果采样正在进行，就让新的使用者等待当前结果，而不是再次启动传感器。</p><h3 id="使用自适应采样周期"><a href="#使用自适应采样周期" class="headerlink" title="使用自适应采样周期"></a>使用自适应采样周期</h3><p>固定 5 秒并不总是合理。更常见的策略是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">环境稳定、无事件</span><br><span class="line">  -&gt; 30 至 60 秒慢速采样</span><br><span class="line"></span><br><span class="line">检测到人体或其他业务事件</span><br><span class="line">  -&gt; 立即采样一次</span><br><span class="line"></span><br><span class="line">光照接近业务判断阈值</span><br><span class="line">  -&gt; 短时间提高采样频率</span><br><span class="line"></span><br><span class="line">环境重新稳定</span><br><span class="line">  -&gt; 退回慢速周期</span><br></pre></td></tr></table></figure><p>如果业务只需要“事件发生时的光照”，甚至可以采用事件触发采样加慢速健康检查，而不是永久维持高频周期采样。</p><h3 id="对齐已有唤醒窗口"><a href="#对齐已有唤醒窗口" class="headerlink" title="对齐已有唤醒窗口"></a>对齐已有唤醒窗口</h3><p>传感器采样可以尽量对齐：</p><ul><li>外部 GPIO 或 PIR 唤醒；</li><li>无线设备的 poll；</li><li>电池测量；</li><li>其他周期维护任务。</li></ul><p>这样不仅降低传感器占空比，还能减少 MCU 从 retention 或 deep sleep 独立唤醒的次数。</p><h2 id="采样频率和上报频率要解耦"><a href="#采样频率和上报频率要解耦" class="headerlink" title="采样频率和上报频率要解耦"></a>采样频率和上报频率要解耦</h2><p>采到新数据不代表必须立即发无线消息。常见策略是：</p><ul><li>本地按业务需要采样；</li><li>只有变化超过阈值时更新并上报；</li><li>设置最大报告间隔，保证长期稳定时仍有周期状态；</li><li>事件发生时可以复用最近的新鲜样本。</li></ul><p>无线发送的能量往往高于一次短 I2C 事务。只优化传感器模式，却让每次采样都触发无线报告，整体收益可能很有限。</p><h2 id="MCU-在转换期间是否真的睡着"><a href="#MCU-在转换期间是否真的睡着" class="headerlink" title="MCU 在转换期间是否真的睡着"></a>MCU 在转换期间是否真的睡着</h2><p>使用异步定时器只能说明系统具备休眠机会，并不保证每次等待都会进入低功耗状态。还要检查：</p><ul><li>协议栈是否繁忙；</li><li>是否有尚未完成的任务或日志输出；</li><li>最近的软件定时器是否允许进入目标睡眠模式；</li><li>I2C、GPIO 和传感器状态是否满足休眠门控；</li><li>唤醒后是否继续原状态机，而不是重复初始化采样。</li></ul><p>因此验证时不仅要看传感器寄存器，还要同时观察 MCU 的睡眠进入率和整机电流波形。</p><h2 id="是否值得电源门控"><a href="#是否值得电源门控" class="headerlink" title="是否值得电源门控"></a>是否值得电源门控</h2><p>如果传感器 Standby 仍占据不可接受的电流，可以考虑用 load switch 或受控电源完全断电。但这通常不是第一优先级，因为它会引入：</p><ul><li>每次采样重新上电和初始化；</li><li>I2C SDA&#x2F;SCL 通过上拉反向供电的风险；</li><li>GPIO 上电时序和高阻态要求；</li><li>额外器件、板级面积和故障路径；</li><li>校准或寄存器配置丢失。</li></ul><p>只有当整机预算确实需要节省最后几微安，并完成板级验证后，电源门控才可能值得。</p><h2 id="建议的验证矩阵"><a href="#建议的验证矩阵" class="headerlink" title="建议的验证矩阵"></a>建议的验证矩阵</h2><p>至少比较三种 profile：</p><table><thead><tr><th>Profile</th><th>配置</th><th>重点观察</th></tr></thead><tbody><tr><td>固定周期</td><td>每 5 秒按需 Active 一次</td><td>基准平均电流、数据新鲜度</td></tr><tr><td>事件优先</td><td>事件触发，加 30–60 秒 fallback</td><td>日常平均电流、事件响应</td></tr><tr><td>连续 IRQ</td><td>最长可接受 Measurement Rate，加阈值迟滞</td><td>阈值延迟、连续工作电流</td></tr></tbody></table><p>同时测量传感器电源轨和整机输入电流，覆盖：</p><ul><li>环境长期稳定；</li><li>事件频繁发生；</li><li>光照在阈值附近波动；</li><li>明暗快速切换；</li><li>I2C 失败和数据未就绪重试。</li></ul><p>静止十分钟的平均值只能代表一种场景。低功耗设计最终要在功耗、数据新鲜度、事件延迟和可靠恢复之间取得平衡。</p><h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><p>环境光采集没有统一的“IRQ 一定更省电”答案。合理的选择顺序是：</p><ol><li>先确认传感器是否支持 single-shot 或低功耗阈值引擎；</li><li>再明确业务需要周期 lux，还是实时明暗事件；</li><li>不支持 single-shot 时，用状态机实现按需 Active 和可靠回 Standby；</li><li>优先合并请求、降低采样频率并对齐已有唤醒；</li><li>最后通过整机电流波形验证，而不是只依赖手册典型值。</li></ol><p>对于只需偶尔获取照度的电池设备，MCU 定时按需采样通常比连续 Active 加阈值 IRQ 更合适；而真正需要快速明暗检测时，连续 IRQ 的功耗代价应作为产品需求的一部分明确接受。</p>]]>
    </content>
    <id>https://oniums.github.io/posts/ambient-light-sensor-low-power-sampling/</id>
    <link href="https://oniums.github.io/posts/ambient-light-sensor-low-power-sampling/"/>
    <published>2026-07-29T07:30:00.000Z</published>
    <summary>
      <![CDATA[<p>在电池供电设备里，环境光传感器通常只需要偶尔提供一个 lux 值。此时“传感器带 IRQ”并不代表 IRQ 一定比定时读取省电。真正决定功耗的是：等待 IRQ 时传感器处于什么模式、转换期间 MCU 能否休眠，以及业务究竟需要周期数据还是实时阈值事件。</p>]]>
    </summary>
    <title>电池设备的环境光传感器：IRQ、单次采样与低功耗取舍</title>
    <updated>2026-07-29T07:49:30.676Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="生态适配" scheme="https://oniums.github.io/categories/%E7%94%9F%E6%80%81%E9%80%82%E9%85%8D/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="SmartThings" scheme="https://oniums.github.io/tags/smartthings/"/>
    <category term="ZHA" scheme="https://oniums.github.io/tags/zha/"/>
    <category term="Zigbee2MQTT" scheme="https://oniums.github.io/tags/zigbee2mqtt/"/>
    <content>
      <![CDATA[<p>同一台 Zigbee 设备在 ZHA、Zigbee2MQTT 和 SmartThings 中可能需要三套适配代码，但协议事实应该只有一份。高质量兼容工作的关键，是先建立产品中立的 Zigbee 行为矩阵，再把它映射到各平台的数据模型。</p><span id="more"></span><h2 id="三个平台分别在做什么"><a href="#三个平台分别在做什么" class="headerlink" title="三个平台分别在做什么"></a>三个平台分别在做什么</h2><table><thead><tr><th>平台</th><th>适配载体</th><th>主要语言</th><th>平台输出</th></tr></thead><tbody><tr><td>ZHA</td><td>Device Handler &#x2F; Quirk</td><td>Python</td><td>Home Assistant Entity、Device Automation</td></tr><tr><td>Zigbee2MQTT</td><td>zigbee-herdsman converter</td><td>JavaScript &#x2F; TypeScript</td><td>MQTT State、Command、Exposes</td></tr><tr><td>SmartThings</td><td>Edge Driver</td><td>Lua</td><td>SmartThings Capability 与 Component</td></tr></tbody></table><p>它们都要解决三件事：</p><ol><li>识别是哪一类设备；</li><li>把 Zigbee 消息转换成平台状态；</li><li>把平台操作转换成 Zigbee 命令或属性写入。</li></ol><h2 id="先判断是否真的需要第三方脚本"><a href="#先判断是否真的需要第三方脚本" class="headerlink" title="先判断是否真的需要第三方脚本"></a>先判断是否真的需要第三方脚本</h2><p>如果设备正确实现标准 Device Type、Cluster、Attribute、Command 和 Reporting，平台的通用处理通常已经能够覆盖主要功能。</p><p>适配脚本更适合处理：</p><ul><li>Basic Cluster 身份或描述符与实际行为不一致；</li><li>厂商自定义 Cluster、Attribute 或 Command；</li><li>标准位图需要拆成多个平台实体；</li><li>原始数值需要单位、比例或枚举转换；</li><li>特定固件版本存在兼容差异；</li><li>标准功能存在，但平台没有自动生成期望的实体或 Capability。</li></ul><p>能在固件端修复的标准合规问题，不应长期依赖三个平台分别打补丁。</p><h2 id="建立一份协议事实矩阵"><a href="#建立一份协议事实矩阵" class="headerlink" title="建立一份协议事实矩阵"></a>建立一份协议事实矩阵</h2><p>每项能力至少记录：</p><table><thead><tr><th>字段</th><th>内容</th></tr></thead><tbody><tr><td>身份</td><td>Manufacturer、Model、固件版本</td></tr><tr><td>拓扑</td><td>Endpoint、Profile ID、Device ID</td></tr><tr><td>Cluster</td><td>Server &#x2F; Client 方向</td></tr><tr><td>数据源</td><td>Attribute、Command 或 ZDO 消息</td></tr><tr><td>原始类型</td><td>bitmap、enum、signed、unsigned、string</td></tr><tr><td>转换</td><td>单位、倍率、范围、无效值</td></tr><tr><td>上行</td><td>Report、Read Response、Cluster Command</td></tr><tr><td>下行</td><td>Write、Command、Configure Reporting</td></tr><tr><td>生命周期</td><td>首次加入、重启、rejoin、恢复出厂</td></tr><tr><td>低功耗</td><td>Poll、唤醒窗口和配置时机</td></tr></tbody></table><p>平台代码只消费这份事实，不重新猜测协议含义。</p><h2 id="平台映射表"><a href="#平台映射表" class="headerlink" title="平台映射表"></a>平台映射表</h2><table><thead><tr><th>Zigbee 事实</th><th>ZHA</th><th>Zigbee2MQTT</th><th>SmartThings</th></tr></thead><tbody><tr><td>Manufacturer &#x2F; Model</td><td>QuirkBuilder matcher</td><td><code>zigbeeModel</code> &#x2F; fingerprint</td><td><code>fingerprints.yml</code></td></tr><tr><td>Attribute Report</td><td>Cluster update &#x2F; Entity</td><td><code>fromZigbee</code> &#x2F; modern extend</td><td><code>zigbee_handlers.attr</code></td></tr><tr><td>Cluster Command</td><td>automation trigger</td><td><code>fromZigbee</code> action</td><td><code>zigbee_handlers.cluster</code></td></tr><tr><td>平台控制</td><td>Entity write&#x2F;command</td><td><code>toZigbee</code></td><td>Capability handler</td></tr><tr><td>对外能力</td><td>Entity metadata</td><td><code>exposes</code></td><td>Profile Capability</td></tr><tr><td>Reporting</td><td>ReportingConfig</td><td><code>configure</code> &#x2F; modern extend</td><td>configured&#x2F;monitored attribute</td></tr><tr><td>设备变体</td><td>firmware filter &#x2F; matcher</td><td>fingerprint &#x2F; meta</td><td>sub-driver <code>can_handle</code></td></tr></tbody></table><p>名字相近不表示行为自动相同。例如同一个 IAS Zone 位图，在一个平台可能拆成多个 Binary Sensor，在另一个平台可能形成一组 MQTT 属性，在 SmartThings 中则对应多个 Capability。</p><h2 id="推荐开发顺序"><a href="#推荐开发顺序" class="headerlink" title="推荐开发顺序"></a>推荐开发顺序</h2><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">抓取真实 interview 与空口行为</span><br><span class="line">  -&gt; 建立协议事实矩阵</span><br><span class="line">  -&gt; 先验证标准能力</span><br><span class="line">  -&gt; 实现最小身份匹配</span><br><span class="line">  -&gt; 实现上行状态</span><br><span class="line">  -&gt; 实现下行控制</span><br><span class="line">  -&gt; 配置 reporting / binding</span><br><span class="line">  -&gt; 验证重启、rejoin 与低功耗</span><br><span class="line">  -&gt; 三平台对照回归</span><br></pre></td></tr></table></figure><p>不要一开始就复制相似设备脚本。先比较 Endpoint、Cluster 方向、属性类型和命令载荷，确认差异后再复用。</p><h2 id="身份匹配要足够窄"><a href="#身份匹配要足够窄" class="headerlink" title="身份匹配要足够窄"></a>身份匹配要足够窄</h2><p>过宽匹配会让行为不同的设备误用同一脚本：</p><ul><li>只按常见 Model ID 匹配，可能碰到多个厂商共用；</li><li>只按 Cluster 集合匹配，可能覆盖大量标准设备；</li><li>忽略固件版本，可能把旧固件 workaround 应用于新固件；</li><li>忽略 Endpoint，可能把多路设备映射到错误 Component。</li></ul><p>优先使用稳定的 Manufacturer + Model；必要时叠加 Endpoint、Cluster 或固件版本条件。更换身份字符串会破坏已经发布的三平台兼容代码，应视为协议接口变更。</p><h2 id="上行与下行必须成对验证"><a href="#上行与下行必须成对验证" class="headerlink" title="上行与下行必须成对验证"></a>上行与下行必须成对验证</h2><p>一个“可见的开关”不代表完整兼容：</p><ul><li>设备主动变化能否更新平台；</li><li>平台写入能否改变设备；</li><li>Read Back 是否与平台状态一致；</li><li>失败是否返回真实错误；</li><li>重启后状态能否恢复；</li><li>reporting 丢失后能否重新配置；</li><li>Sleepy End Device 是否在唤醒窗口处理配置。</li></ul><p>如果只验证 UI 点击成功，很容易漏掉设备侧主动变化和离线恢复。</p><h2 id="三个平台的状态命名"><a href="#三个平台的状态命名" class="headerlink" title="三个平台的状态命名"></a>三个平台的状态命名</h2><p>建议先定义平台无关语义：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">contact: open / closed</span><br><span class="line">tamper: detected / clear</span><br><span class="line">battery: percentage</span><br><span class="line">alarm: active / inactive</span><br></pre></td></tr></table></figure><p>然后分别映射为：</p><ul><li>ZHA 的 Entity 和 Device Class；</li><li>Z2M 的 <code>exposes</code> property；</li><li>SmartThings 的 Capability attribute&#x2F;event。</li></ul><p>协议枚举值保持不变，显示文案可以平台化。不要为了让 UI 好看而改变空口数值。</p><h2 id="低功耗设备专项"><a href="#低功耗设备专项" class="headerlink" title="低功耗设备专项"></a>低功耗设备专项</h2><p>兼容脚本常在设备刚加入时集中执行 bind、read 和 configure reporting。对休眠设备，应检查：</p><ul><li>配置发生时设备是否仍处于 Fast Poll；</li><li>平台是否会自动重试；</li><li>rejoin 后 reporting 是否保留；</li><li>父节点更换后是否需要重新绑定；</li><li>多条配置是否超出单次唤醒窗口；</li><li>失败是否被静默忽略。</li></ul><p>“第一次加入能用”不足以证明长期兼容。</p><h2 id="验证证据分层"><a href="#验证证据分层" class="headerlink" title="验证证据分层"></a>验证证据分层</h2><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">脚本能加载</span><br><span class="line">  != 匹配到目标设备</span><br><span class="line">  != Entity / Exposes / Capability 正确</span><br><span class="line">  != 上行状态正确</span><br><span class="line">  != 下行控制正确</span><br><span class="line">  != 重启和 rejoin 正确</span><br><span class="line">  != 平台正式收录</span><br><span class="line">  != Zigbee 认证或生态认证</span><br></pre></td></tr></table></figure><p>每个平台都应保存版本、脚本提交、interview、调试日志、测试动作和预期结果。公开问题单要脱敏设备唯一地址、Network Key、install code 和私有仓库信息。</p><p>继续阅读：</p><ul><li><a href="https://github.com/zigpy/zha-device-handlers">ZHA Device Handlers</a></li><li><a href="https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html">Zigbee2MQTT 新设备支持</a></li><li><a href="https://developer.smartthings.com/docs/devices/hub-connected/edge-architecture">SmartThings Edge 架构</a></li></ul>]]>
    </content>
    <id>https://oniums.github.io/posts/zigbee-third-party-platform-compatibility/</id>
    <link href="https://oniums.github.io/posts/zigbee-third-party-platform-compatibility/"/>
    <published>2026-07-29T02:40:00.000Z</published>
    <summary>
      <![CDATA[<p>同一台 Zigbee 设备在 ZHA、Zigbee2MQTT 和 SmartThings 中可能需要三套适配代码，但协议事实应该只有一份。高质量兼容工作的关键，是先建立产品中立的 Zigbee 行为矩阵，再把它映射到各平台的数据模型。</p>]]>
    </summary>
    <title>Zigbee 第三方平台适配：ZHA、Z2M 与 SmartThings 的统一方法</title>
    <updated>2026-07-29T02:49:46.132Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="生态适配" scheme="https://oniums.github.io/categories/%E7%94%9F%E6%80%81%E9%80%82%E9%85%8D/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="ZHA" scheme="https://oniums.github.io/tags/zha/"/>
    <category term="Home Assistant" scheme="https://oniums.github.io/tags/home-assistant/"/>
    <category term="Python" scheme="https://oniums.github.io/tags/python/"/>
    <content>
      <![CDATA[<p>ZHA Quirk 是 Zigpy 与 Home Assistant 之间的设备适配层。它适合修正非标准描述符、补充厂商 Cluster，或把已有 Zigbee 属性映射成正确的 Home Assistant Entity。</p><span id="more"></span><h2 id="Quirk-的边界"><a href="#Quirk-的边界" class="headerlink" title="Quirk 的边界"></a>Quirk 的边界</h2><p>一个 Quirk 通常包含：</p><ul><li>Matcher：识别 Manufacturer、Model 和可选的设备条件；</li><li>Cluster modification：增加、移除或替换 Cluster；</li><li>Entity metadata：把属性或命令映射成 Entity；</li><li>Automation trigger：把遥控器命令映射成设备动作；</li><li>Reporting configuration：声明需要配置的属性上报。</li></ul><p>Quirk 不会修复射频、入网安全或设备没有发送报文的问题。先确认目标报文确实到达 Zigpy，再处理映射。</p><h2 id="优先使用-V2-QuirkBuilder"><a href="#优先使用-V2-QuirkBuilder" class="headerlink" title="优先使用 V2 QuirkBuilder"></a>优先使用 V2 QuirkBuilder</h2><p>当前 zha-device-handlers 推荐新适配使用声明式 <code>QuirkBuilder</code>。一个只补充 IAS Zone Tamper 实体的通用示例：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> zigpy.zcl.clusters.security <span class="keyword">import</span> IasZone</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> zhaquirks.builder <span class="keyword">import</span> BinarySensorDeviceClass, QuirkBuilder</span><br><span class="line"></span><br><span class="line">(</span><br><span class="line">    QuirkBuilder(<span class="string">&quot;Example Manufacturer&quot;</span>, <span class="string">&quot;GENERIC-CONTACT-01&quot;</span>)</span><br><span class="line">    .binary_sensor(</span><br><span class="line">        attribute_name=IasZone.AttributeDefs.zone_status.name,</span><br><span class="line">        cluster_id=IasZone.cluster_id,</span><br><span class="line">        device_class=BinarySensorDeviceClass.TAMPER,</span><br><span class="line">        attribute_converter=<span class="keyword">lambda</span> value: <span class="built_in">bool</span>(</span><br><span class="line">            value &amp; IasZone.ZoneStatus.Tamper</span><br><span class="line">        ),</span><br><span class="line">        unique_id_suffix=<span class="string">&quot;tamper&quot;</span>,</span><br><span class="line">        fallback_name=<span class="string">&quot;Tamper&quot;</span>,</span><br><span class="line">    )</span><br><span class="line">    .add_to_registry()</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>这个示例假设设备已经正确提供 IAS Zone Cluster，只是平台需要把 <code>zone_status</code> 的 Tamper 位拆成独立实体。它没有虚构 Cluster，也没有改变空口数据。</p><h2 id="Matcher-为什么经常失败"><a href="#Matcher-为什么经常失败" class="headerlink" title="Matcher 为什么经常失败"></a>Matcher 为什么经常失败</h2><p>先从 ZHA Device Signature 或调试日志确认：</p><ul><li>Basic Cluster 的 Manufacturer；</li><li>Model；</li><li>Endpoint 列表；</li><li>每个 Endpoint 的 Profile、Device Type；</li><li>Server &#x2F; Client Cluster。</li></ul><p>字符串必须与设备实际返回完全一致。营销名称、包装型号和 Basic Cluster Model 可能不同。</p><p>如果多个固件共享 Manufacturer&#x2F;Model 但行为不同，可使用固件版本过滤或更具体条件，避免一个 workaround 覆盖所有版本。</p><h2 id="什么时候替换-Cluster"><a href="#什么时候替换-Cluster" class="headerlink" title="什么时候替换 Cluster"></a>什么时候替换 Cluster</h2><p>只有出现以下情况才需要自定义 Cluster：</p><ul><li>厂商 Cluster 未在 Zigpy 中定义；</li><li>属性类型、Manufacturer Code 或访问方式需要补充；</li><li>报告值需要稳定、可证明的转换；</li><li>标准 Cluster 的设备实现存在已确认偏差。</li></ul><p>自定义属性应明确：</p><ul><li>Attribute ID；</li><li>Zigbee 数据类型；</li><li>读写权限；</li><li>Manufacturer Code；</li><li>单位与缩放；</li><li>无效值；</li><li>reporting 行为。</li></ul><p>不要根据一条抓包就猜测类型。至少验证边界值、重启和多个样机。</p><h2 id="Server-与-Client-方向"><a href="#Server-与-Client-方向" class="headerlink" title="Server 与 Client 方向"></a>Server 与 Client 方向</h2><p>ZHA 中：</p><ul><li><code>in_clusters</code> 对应设备提供的 Server Cluster；</li><li><code>out_clusters</code> 对应设备使用的 Client Cluster。</li></ul><p>命令定义的方向也要与 Zigpy 当前 API 一致。升级 Zigpy 时，如果命令定义模型发生变化，应保持 Command ID、载荷类型和空口序列化不变。</p><h2 id="Entity-映射注意点"><a href="#Entity-映射注意点" class="headerlink" title="Entity 映射注意点"></a>Entity 映射注意点</h2><p>建立 Entity 时核对：</p><ul><li><code>endpoint_id</code>；</li><li><code>cluster_id</code>；</li><li><code>attribute_name</code> 或 <code>command_name</code>；</li><li>Device Class；</li><li>Entity Type：Standard、Config 或 Diagnostic；</li><li>单位、倍率和显示精度；</li><li><code>unique_id_suffix</code> 是否稳定且无冲突；</li><li>是否默认禁用。</li></ul><p>同一个位图拆成多个实体时，每个实体都要使用稳定且不同的 suffix。已有用户依赖 Entity ID 后，不应随意更换。</p><h2 id="本地加载"><a href="#本地加载" class="headerlink" title="本地加载"></a>本地加载</h2><p>Home Assistant 可通过 ZHA 配置加载自定义 quirks：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">zha:</span></span><br><span class="line">  <span class="attr">custom_quirks_path:</span> <span class="string">/config/custom_zha_quirks</span></span><br></pre></td></tr></table></figure><p>把 Python 文件放入该目录后重启 Home Assistant。随后在设备信息与日志中确认实际加载的 Quirk 类。</p><p>如果 Quirk 已匹配但新实体仍未出现，可先执行重新配置；只有在确认平台没有重建设备实体时，再评估删除并重新配对。重新配对不是验证 Matcher 的替代方法。</p><h2 id="最小验证"><a href="#最小验证" class="headerlink" title="最小验证"></a>最小验证</h2><p>开发仓库中的常见检查：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">python -m py_compile path/to/quirk.py</span><br><span class="line">ruff check path/to/quirk.py</span><br><span class="line">pytest path/to/relevant_tests.py</span><br></pre></td></tr></table></figure><p>还需要设备侧验证：</p><ol><li>Quirk 被目标设备匹配；</li><li>相似设备不被误匹配；</li><li>属性报告更新正确实体；</li><li>写入或命令使用正确 Endpoint 和方向；</li><li>重启 Home Assistant 后仍可加载；</li><li>设备 rejoin 后 reporting 正常；</li><li>未知值不会导致异常或错误状态。</li></ol><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><table><thead><tr><th>症状</th><th>先检查</th></tr></thead><tbody><tr><td>Quirk 没加载</td><td>Manufacturer、Model、签名与加载路径</td></tr><tr><td>Quirk 加载但无实体</td><td>Entity metadata、Cluster 方向、Entity registry</td></tr><tr><td>实体不更新</td><td>Attribute ID、报告方向、converter</td></tr><tr><td>写入失败</td><td>权限、Manufacturer Code、Endpoint、设备唤醒</td></tr><tr><td>多个 Quirk 冲突</td><td>Matcher 是否过宽、版本是否重复注册</td></tr><tr><td>升级后警告</td><td>Zigpy&#x2F;ZHA API 与命令定义版本</td></tr></tbody></table><p>正式贡献前应遵守仓库当前的格式、类型检查、测试和贡献说明。官方入口：</p><ul><li><a href="https://github.com/zigpy/zha-device-handlers">zha-device-handlers</a></li><li><a href="https://www.home-assistant.io/integrations/zha/">Home Assistant ZHA 集成</a></li></ul>]]>
    </content>
    <id>https://oniums.github.io/posts/zha-custom-quirk-development/</id>
    <link href="https://oniums.github.io/posts/zha-custom-quirk-development/"/>
    <published>2026-07-29T02:30:00.000Z</published>
    <summary>
      <![CDATA[<p>ZHA Quirk 是 Zigpy 与 Home Assistant 之间的设备适配层。它适合修正非标准描述符、补充厂商 Cluster，或把已有 Zigbee 属性映射成正确的 Home Assistant Entity。</p>]]>
    </summary>
    <title>ZHA 第三方设备兼容：用 V2 Quirk 精确匹配和映射实体</title>
    <updated>2026-07-29T02:49:46.133Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="生态适配" scheme="https://oniums.github.io/categories/%E7%94%9F%E6%80%81%E9%80%82%E9%85%8D/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="Zigbee2MQTT" scheme="https://oniums.github.io/tags/zigbee2mqtt/"/>
    <category term="JavaScript" scheme="https://oniums.github.io/tags/javascript/"/>
    <category term="MQTT" scheme="https://oniums.github.io/tags/mqtt/"/>
    <content>
      <![CDATA[<p>Zigbee2MQTT 使用 zigbee-herdsman-converters 把 Zigbee 消息转换成 MQTT 状态和命令。External Converter 可以在不修改正式安装包的情况下开发和验证新设备支持。</p><span id="more"></span><h2 id="开始前先做三件事"><a href="#开始前先做三件事" class="headerlink" title="开始前先做三件事"></a>开始前先做三件事</h2><ol><li>检查设备是否已在 Zigbee2MQTT 开发分支获得支持；</li><li>把日志级别设为 Debug；</li><li>完成设备 interview，并保存 Endpoint、Cluster 与 Basic 信息。</li></ol><p>能加入网络只代表 Zigbee2MQTT 可以访问设备，不代表已有 converter 能理解它。</p><h2 id="先生成外部定义"><a href="#先生成外部定义" class="headerlink" title="先生成外部定义"></a>先生成外部定义</h2><p>当前 Zigbee2MQTT 前端可以在设备的 Dev console 中生成 external definition。生成结果基于发现到的标准 Cluster，应先验证 Exposes 页面中的能力是否真实可用。</p><p>自动生成的定义是起点，不是最终结论：</p><ul><li>设备可能没有上报全部能力；</li><li>厂商 Cluster 无法自动理解；</li><li>reporting 可能尚未配置；</li><li>命令型遥控器可能没有普通状态属性；</li><li>相同 Model ID 可能对应多个厂商变体。</li></ul><h2 id="Modern-Extend-示例"><a href="#Modern-Extend-示例" class="headerlink" title="Modern Extend 示例"></a>Modern Extend 示例</h2><p>标准温湿度与电池设备可以从最小定义开始：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;</span><br><span class="line">    battery,</span><br><span class="line">    humidity,</span><br><span class="line">    temperature,</span><br><span class="line">&#125; <span class="keyword">from</span> <span class="string">&quot;zigbee-herdsman-converters/lib/modernExtend&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">    <span class="attr">zigbeeModel</span>: [<span class="string">&quot;GENERIC-ENV-01&quot;</span>],</span><br><span class="line">    <span class="attr">model</span>: <span class="string">&quot;GENERIC-ENV-01&quot;</span>,</span><br><span class="line">    <span class="attr">vendor</span>: <span class="string">&quot;Example Vendor&quot;</span>,</span><br><span class="line">    <span class="attr">description</span>: <span class="string">&quot;Temperature and humidity sensor&quot;</span>,</span><br><span class="line">    <span class="attr">extend</span>: [</span><br><span class="line">        <span class="title function_">temperature</span>(),</span><br><span class="line">        <span class="title function_">humidity</span>(),</span><br><span class="line">        <span class="title function_">battery</span>(),</span><br><span class="line">    ],</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>Modern extends 会组合常见的 <code>fromZigbee</code>、<code>toZigbee</code>、<code>exposes</code> 和配置逻辑。能复用时优先复用，减少重复的解析和 reporting 代码。</p><h2 id="External-Converter-放在哪里"><a href="#External-Converter-放在哪里" class="headerlink" title="External Converter 放在哪里"></a>External Converter 放在哪里</h2><p>文件通常放在与 Zigbee2MQTT <code>configuration.yaml</code> 同级的 <code>external_converters</code> 目录，例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">data/</span><br><span class="line">├── configuration.yaml</span><br><span class="line">└── external_converters/</span><br><span class="line">    └── generic-env.mjs</span><br></pre></td></tr></table></figure><p>也可以从前端的 Settings → Dev console → External converters 加载和更新。</p><p>当前版本对外部 JavaScript 有额外安全控制。它会在 Zigbee2MQTT 进程中执行任意代码，只应在隔离的开发环境中启用，并只加载经过审查的来源。</p><h2 id="什么时候使用-fromZigbee"><a href="#什么时候使用-fromZigbee" class="headerlink" title="什么时候使用 fromZigbee"></a>什么时候使用 fromZigbee</h2><p><code>fromZigbee</code> 负责把设备发来的消息转换成平台状态或 action：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Attribute Report / Read Response / Cluster Command</span><br><span class="line">  -&gt; 匹配 cluster 与 message type</span><br><span class="line">  -&gt; 解析原始字段</span><br><span class="line">  -&gt; 校验范围和特殊值</span><br><span class="line">  -&gt; 返回 MQTT property</span><br></pre></td></tr></table></figure><p>如果日志持续出现 <code>No converter available</code>，先确认：</p><ul><li>Cluster 名称；</li><li>Message type；</li><li>Endpoint；</li><li>Attribute 或 Command ID；</li><li>是否已存在可复用 converter；</li><li>是否已经被某个 modern extend 覆盖。</li></ul><p>不要用一个接受所有消息的 converter 隐藏未知数据。</p><h2 id="什么时候使用-toZigbee"><a href="#什么时候使用-toZigbee" class="headerlink" title="什么时候使用 toZigbee"></a>什么时候使用 toZigbee</h2><p><code>toZigbee</code> 把 MQTT Set&#x2F;Get 转换成 Attribute Read&#x2F;Write 或 Cluster Command。必须定义：</p><ul><li>支持的 property；</li><li>输入类型和范围；</li><li>枚举字符串与协议数值；</li><li>目标 Endpoint；</li><li>Manufacturer Code；</li><li>写入后的状态确认策略；</li><li>错误返回。</li></ul><p>下发函数返回成功不等于设备状态已经改变。应结合响应、Read Back 或后续 Report。</p><h2 id="Exposes-是公开契约"><a href="#Exposes-是公开契约" class="headerlink" title="Exposes 是公开契约"></a>Exposes 是公开契约</h2><p><code>exposes</code> 决定前端和 MQTT 使用者看到什么。每个 property 应明确：</p><ul><li>名称与显示 Label；</li><li>读、写、状态访问；</li><li>数据类型；</li><li>单位；</li><li>最小值、最大值和步进；</li><li>枚举；</li><li>Endpoint 后缀；</li><li>描述。</li></ul><p>一旦用户用 property 编写自动化，随意改名会造成兼容破坏。显示文案可以优化，但 MQTT property 应保持稳定。</p><h2 id="Configure-与-reporting"><a href="#Configure-与-reporting" class="headerlink" title="Configure 与 reporting"></a>Configure 与 reporting</h2><p>设备没有主动上报时，可能需要在 <code>configure</code> 中：</p><ul><li>bind 到 Coordinator Endpoint；</li><li>Configure Reporting；</li><li>读取倍率、除数或初始状态；</li><li>设置厂商初始化属性。</li></ul><p>对 Sleepy End Device，配置必须落在唤醒窗口，并验证失败重试。不要假设一次 <code>configure</code> 成功就会永久保留；还要测试断电、rejoin 和恢复出厂。</p><h2 id="匹配多个设备变体"><a href="#匹配多个设备变体" class="headerlink" title="匹配多个设备变体"></a>匹配多个设备变体</h2><p><code>zigbeeModel</code> 简单但可能过宽。多个厂商共用同一 Model ID 时，应使用更精确的 fingerprint 条件，并把真正相同的行为复用到公共 extend。</p><p>兼容层次建议：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">标准 modern extend</span><br><span class="line">  -&gt; 品类公共 converter</span><br><span class="line">  -&gt; 厂商公共逻辑</span><br><span class="line">  -&gt; 设备或固件差异</span><br></pre></td></tr></table></figure><p>不要把所有变体堆进一个充满 Model 判断的转换函数。</p><h2 id="验证清单"><a href="#验证清单" class="headerlink" title="验证清单"></a>验证清单</h2><ol><li>Converter 成功加载；</li><li>只匹配预期设备；</li><li>Exposes 完整且访问方向正确；</li><li>所有上行属性和命令均可解析；</li><li>Set&#x2F;Get 有真实设备结果；</li><li>reporting、单位和缩放正确；</li><li>多 Endpoint property 不冲突；</li><li>Zigbee2MQTT 重启后仍可加载；</li><li>设备断电和 rejoin 后恢复；</li><li>Debug 日志没有未解释的消息和异常。</li></ol><p>External Converter 验证完成后，可按 zigbee-herdsman-converters 当前结构转成正式 TypeScript definition 并提交上游。发布版本已经包含支持后，应移除本地 converter，避免重复匹配。</p><p>官方资料：</p><ul><li><a href="https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html">支持新设备</a></li><li><a href="https://www.zigbee2mqtt.io/advanced/more/external_converters.html">External Converters</a></li><li><a href="https://www.zigbee2mqtt.io/guide/usage/exposes.html">Exposes</a></li><li><a href="https://www.zigbee2mqtt.io/advanced/zigbee/03_secure_network.html">外部脚本安全说明</a></li></ul>]]>
    </content>
    <id>https://oniums.github.io/posts/zigbee2mqtt-external-converter-development/</id>
    <link href="https://oniums.github.io/posts/zigbee2mqtt-external-converter-development/"/>
    <published>2026-07-29T02:20:00.000Z</published>
    <summary>
      <![CDATA[<p>Zigbee2MQTT 使用 zigbee-herdsman-converters 把 Zigbee 消息转换成 MQTT 状态和命令。External Converter 可以在不修改正式安装包的情况下开发和验证新设备支持。</p>]]>
    </summary>
    <title>Zigbee2MQTT 第三方设备兼容：External Converter 开发与验证</title>
    <updated>2026-07-29T02:49:46.134Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="生态适配" scheme="https://oniums.github.io/categories/%E7%94%9F%E6%80%81%E9%80%82%E9%85%8D/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="SmartThings" scheme="https://oniums.github.io/tags/smartthings/"/>
    <category term="Edge Driver" scheme="https://oniums.github.io/tags/edge-driver/"/>
    <category term="Lua" scheme="https://oniums.github.io/tags/lua/"/>
    <content>
      <![CDATA[<p>SmartThings Edge Driver 在 Hub 本地运行，用 Lua 把 Zigbee 设备行为转换成 SmartThings Capability。兼容工作的核心不是写很多 Handler，而是让 fingerprint、profile、默认处理和设备差异保持一致。</p><span id="more"></span><h2 id="Driver-目录结构"><a href="#Driver-目录结构" class="headerlink" title="Driver 目录结构"></a>Driver 目录结构</h2><p>一个 Zigbee Edge Driver 通常包含：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">zigbee-example/</span><br><span class="line">├── config.yml</span><br><span class="line">├── fingerprints.yml</span><br><span class="line">├── profiles/</span><br><span class="line">│   └── sensor.yml</span><br><span class="line">└── src/</span><br><span class="line">    ├── init.lua</span><br><span class="line">    └── sub_drivers/</span><br><span class="line">        └── device_variant/</span><br><span class="line">            ├── can_handle.lua</span><br><span class="line">            └── init.lua</span><br></pre></td></tr></table></figure><ul><li><code>config.yml</code>：包信息、权限和 Edge API 版本；</li><li><code>fingerprints.yml</code>：设备匹配与 Profile 选择；</li><li><code>profiles/</code>：Component 和 Capability；</li><li><code>src/</code>：Zigbee Handler、Capability Handler 与生命周期逻辑；</li><li><code>sub_drivers/</code>：只覆盖特定设备差异。</li></ul><h2 id="Fingerprint"><a href="#Fingerprint" class="headerlink" title="Fingerprint"></a>Fingerprint</h2><p>Manufacturer-specific fingerprint 适合已知设备：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">zigbeeManufacturer:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">&quot;example/generic-contact-v1&quot;</span></span><br><span class="line">    <span class="attr">manufacturer:</span> <span class="string">&quot;Example Manufacturer&quot;</span></span><br><span class="line">    <span class="attr">model:</span> <span class="string">&quot;GENERIC-CONTACT-01&quot;</span></span><br><span class="line">    <span class="attr">deviceProfileName:</span> <span class="string">&quot;contact-battery&quot;</span></span><br><span class="line">    <span class="attr">deviceLabel:</span> <span class="string">&quot;Generic contact sensor&quot;</span></span><br></pre></td></tr></table></figure><p>字段必须来自设备实际报告，而不是包装名称。</p><p>SmartThings 也支持按 Profile、Device ID 和 Server&#x2F;Client Cluster 匹配的 generic fingerprint。它适合真正遵循标准且行为一致的设备；设备存在特殊处理时，精确 Manufacturer&#x2F;Model 匹配更安全。</p><h2 id="Profile-是平台契约"><a href="#Profile-是平台契约" class="headerlink" title="Profile 是平台契约"></a>Profile 是平台契约</h2><p>Profile 决定 App 和自动化看到的 Capability：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">contact-battery</span></span><br><span class="line"><span class="attr">components:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">main</span></span><br><span class="line">    <span class="attr">capabilities:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">contactSensor</span></span><br><span class="line">        <span class="attr">version:</span> <span class="number">1</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">battery</span></span><br><span class="line">        <span class="attr">version:</span> <span class="number">1</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">tamperAlert</span></span><br><span class="line">        <span class="attr">version:</span> <span class="number">1</span></span><br><span class="line">    <span class="attr">categories:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">ContactSensor</span></span><br></pre></td></tr></table></figure><p>只有 Driver 能稳定产生和处理的 Capability 才应加入 Profile。删除或重命名已部署 Profile 会影响现有设备，应作为兼容性变更评估。</p><h2 id="先使用默认-Zigbee-Handler"><a href="#先使用默认-Zigbee-Handler" class="headerlink" title="先使用默认 Zigbee Handler"></a>先使用默认 Zigbee Handler</h2><p>SmartThings Zigbee library 已提供常见 ZCL 到 Capability 的默认映射：</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> capabilities = <span class="built_in">require</span> <span class="string">&quot;st.capabilities&quot;</span></span><br><span class="line"><span class="keyword">local</span> defaults = <span class="built_in">require</span> <span class="string">&quot;st.zigbee.defaults&quot;</span></span><br><span class="line"></span><br><span class="line">defaults.register_for_default_handlers(</span><br><span class="line">  driver_template,</span><br><span class="line">  &#123;</span><br><span class="line">    capabilities.contactSensor,</span><br><span class="line">    capabilities.battery,</span><br><span class="line">  &#125;</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>标准功能优先使用默认处理。自定义 <code>zigbee_handlers</code> 会覆盖相同 Cluster&#x2F;Attribute 的默认 Handler，因此只覆盖设备真正偏离标准的部分。</p><h2 id="自定义-Zigbee-Handler"><a href="#自定义-Zigbee-Handler" class="headerlink" title="自定义 Zigbee Handler"></a>自定义 Zigbee Handler</h2><p>Handler 按消息类型组织：</p><ul><li><code>attr</code>：Attribute Report 或 Read Response；</li><li><code>cluster</code>：Cluster-specific Command；</li><li><code>global</code>：ZCL Global Command；</li><li><code>zdo</code>：ZDO 消息。</li></ul><p>一个属性处理的结构：</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> clusters = <span class="built_in">require</span> <span class="string">&quot;st.zigbee.zcl.clusters&quot;</span></span><br><span class="line"><span class="keyword">local</span> OnOff = clusters.OnOff</span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> <span class="function"><span class="keyword">function</span> <span class="title">on_off_handler</span><span class="params">(driver, device, value, zb_rx)</span></span></span><br><span class="line">  <span class="comment">-- 校验 value，再产生对应 Capability event</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">driver_template.zigbee_handlers = &#123;</span><br><span class="line">  attr = &#123;</span><br><span class="line">    [OnOff.ID] = &#123;</span><br><span class="line">      [OnOff.attributes.OnOff.ID] = on_off_handler,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>真实实现还应处理数据类型、Endpoint、多 Component 映射和未知值。</p><h2 id="Capability-Handler"><a href="#Capability-Handler" class="headerlink" title="Capability Handler"></a>Capability Handler</h2><p>App 发出的 Capability command 需要转换成 Zigbee Command 或 Attribute Write：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Capability command</span><br><span class="line">  -&gt; 校验参数</span><br><span class="line">  -&gt; 选择 Component / Endpoint</span><br><span class="line">  -&gt; 构造 ZCL message</span><br><span class="line">  -&gt; device:send(...)</span><br><span class="line">  -&gt; 等待设备响应或后续 report</span><br></pre></td></tr></table></figure><p>不要在发送后无条件伪造成功状态，除非平台交互明确要求 optimistic update，并且随后有校正机制。</p><h2 id="Reporting-与-doConfigure"><a href="#Reporting-与-doConfigure" class="headerlink" title="Reporting 与 doConfigure"></a>Reporting 与 doConfigure</h2><p>ZigbeeDevice 可以登记 configured 或 monitored attributes。设备配置时，库可执行 bind 和 Configure Reporting；特殊设备也可以在 <code>doConfigure</code> 中发送自定义配置。</p><p>核对：</p><ul><li>Cluster、Attribute 和数据类型；</li><li>Minimum &#x2F; Maximum Interval；</li><li>Reportable Change；</li><li>Hub Endpoint；</li><li>Sleepy End Device 的唤醒窗口；</li><li>配置失败后的重试；</li><li>rejoin 后是否需要重新配置。</li></ul><p>过度频繁的 reporting 会增加网络负载和电池消耗。</p><h2 id="Sub-driver-隔离设备差异"><a href="#Sub-driver-隔离设备差异" class="headerlink" title="Sub-driver 隔离设备差异"></a>Sub-driver 隔离设备差异</h2><p>同一品类 Driver 可以支持多种设备。标准行为放在主 Driver，只有特定设备的差异放入 sub-driver，并通过 <code>can_handle</code> 精确匹配。</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">主 Driver：通用 Capability 与默认 Handler</span><br><span class="line">  -&gt; sub-driver A：特殊属性转换</span><br><span class="line">  -&gt; sub-driver B：不同 Endpoint 映射</span><br><span class="line">  -&gt; sub-driver C：旧固件 workaround</span><br></pre></td></tr></table></figure><p>避免在每个 Handler 中堆叠大量 Manufacturer&#x2F;Model 判断。</p><h2 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h2><p>常见生命周期包括：</p><ul><li><code>added</code>：设备首次创建；</li><li><code>init</code>：Driver 启动或设备初始化；</li><li><code>doConfigure</code>：配置 binding 和 reporting；</li><li><code>infoChanged</code>：Preferences 变化；</li><li><code>removed</code>：设备移除。</li></ul><p>不要把所有网络操作都放在 <code>init</code>，否则每次 Driver 重启可能重复配置大量设备。</p><h2 id="本地打包与设备验证"><a href="#本地打包与设备验证" class="headerlink" title="本地打包与设备验证"></a>本地打包与设备验证</h2><p>SmartThings CLI 可以只构建 zip、不上传：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:drivers:package \</span><br><span class="line">  --build-only driver.zip \</span><br><span class="line">  path/to/driver</span><br></pre></td></tr></table></figure><p>这个步骤验证包结构，但不证明 Hub 运行正确。上传、Channel 分配和 Hub 安装会改变云端或设备状态，应在明确的测试环境中单独执行。</p><p>Hub 验证时使用 live log：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">smartthings edge:drivers:logcat --hub-address=&lt;HUB_IP&gt;</span><br></pre></td></tr></table></figure><p>Hub 地址、Channel ID、Driver ID 和认证 Token 不应提交到公开仓库。</p><h2 id="测试矩阵"><a href="#测试矩阵" class="headerlink" title="测试矩阵"></a>测试矩阵</h2><p>至少覆盖：</p><ol><li>fingerprint 选中正确 Profile；</li><li>相似设备不会误匹配；</li><li>Attribute Report 产生正确 Capability event；</li><li>Cluster Command 产生正确 action；</li><li>Capability command 发送正确 ZCL 消息；</li><li>reporting 配置与低功耗行为；</li><li>多 Endpoint 到 Component 的映射；</li><li>Driver 重启、设备 rejoin 和恢复出厂；</li><li>Preferences 更新；</li><li>未知值和发送失败。</li></ol><p>涉及公开 SmartThingsEdgeDrivers 仓库与认证的变更，还应提供官方要求的集成测试。Package build、Hub 实测、公开仓库合入和 Works with SmartThings 认证是四个不同结果。</p><p>官方资料：</p><ul><li><a href="https://developer.smartthings.com/docs/devices/hub-connected/driver-components-and-structure">Driver 结构与 Fingerprint</a></li><li><a href="https://developer.smartthings.com/docs/edge-device-drivers/zigbee/driver.html">Zigbee Driver Structures</a></li><li><a href="https://developer.smartthings.com/docs/edge-device-drivers/zigbee/defaults.html">Zigbee 默认 Handler</a></li><li><a href="https://developer.smartthings.com/docs/devices/hub-connected/test-your-driver">Edge Driver 测试</a></li></ul>]]>
    </content>
    <id>https://oniums.github.io/posts/smartthings-edge-zigbee-driver-development/</id>
    <link href="https://oniums.github.io/posts/smartthings-edge-zigbee-driver-development/"/>
    <published>2026-07-29T02:10:00.000Z</published>
    <summary>
      <![CDATA[<p>SmartThings Edge Driver 在 Hub 本地运行，用 Lua 把 Zigbee 设备行为转换成 SmartThings Capability。兼容工作的核心不是写很多 Handler，而是让 fingerprint、profile、默认处理和设备差异保持一致。</p>]]>
    </summary>
    <title>SmartThings Zigbee 第三方设备兼容：Edge Driver 的匹配、映射与测试</title>
    <updated>2026-07-29T02:49:46.135Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="IEEE 802.15.4" scheme="https://oniums.github.io/tags/ieee-802-15-4/"/>
    <category term="ZCL" scheme="https://oniums.github.io/tags/zcl/"/>
    <category term="Mesh" scheme="https://oniums.github.io/tags/mesh/"/>
    <content>
      <![CDATA[<p>理解 Zigbee 最有效的方法，不是先背命令，而是把“无线、组网、传输、设备管理、应用功能”分层。这样看日志或抓包时，才能知道一条成功信息究竟证明了哪一步。</p><span id="more"></span><h2 id="协议栈分层"><a href="#协议栈分层" class="headerlink" title="协议栈分层"></a>协议栈分层</h2><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">应用逻辑</span><br><span class="line">  │</span><br><span class="line">ZCL：Cluster、Attribute、Command</span><br><span class="line">  │</span><br><span class="line">ZDO / ZDP：设备与服务发现、网络管理</span><br><span class="line">  │</span><br><span class="line">APS：应用数据传输、绑定、组寻址、应用层安全</span><br><span class="line">  │</span><br><span class="line">NWK：组网、路由、网络地址、网络层安全</span><br><span class="line">  │</span><br><span class="line">IEEE 802.15.4 MAC / PHY：信道、帧、确认、射频</span><br></pre></td></tr></table></figure><ul><li>IEEE 802.15.4 提供低速率无线链路；</li><li>NWK 负责 Zigbee Mesh 网络；</li><li>APS 把上层应用消息送到指定设备和 Endpoint；</li><li>ZDO&#x2F;ZDP 提供节点、Endpoint 和服务发现；</li><li>ZCL 定义设备功能的通用数据模型。</li></ul><p>因此，收到 MAC ACK 只说明链路层收到了帧，不代表 NWK 解密、APS 接受或 ZCL 命令处理成功。</p><h2 id="三种逻辑设备角色"><a href="#三种逻辑设备角色" class="headerlink" title="三种逻辑设备角色"></a>三种逻辑设备角色</h2><table><thead><tr><th>角色</th><th>主要职责</th><th>常见限制</th></tr></thead><tbody><tr><td>Coordinator</td><td>建立网络，通常也是集中式网络的 Trust Center</td><td>一个 Zigbee 网络只有一个 Coordinator</td></tr><tr><td>Router</td><td>转发报文并允许子设备接入</td><td>通常需要保持接收能力</td></tr><tr><td>End Device</td><td>通过父节点通信，不为其他节点路由</td><td>可设计成休眠设备</td></tr></tbody></table><p>Coordinator 是网络形成时的角色；网络形成后，Mesh 路由并不要求所有报文都经过它。Router 或 Coordinator 都可以成为 End Device 的父节点。</p><h2 id="地址、Endpoint-与设备功能"><a href="#地址、Endpoint-与设备功能" class="headerlink" title="地址、Endpoint 与设备功能"></a>地址、Endpoint 与设备功能</h2><p>一个 Zigbee 节点通常同时涉及：</p><ul><li>EUI-64：设备的全局扩展地址；</li><li>16 位 NWK 地址：加入网络后使用的短地址，可能变化；</li><li>PAN ID &#x2F; Extended PAN ID：识别网络；</li><li>Endpoint：节点内部的应用端点；</li><li>Profile ID 与 Device ID：声明应用配置和设备类型；</li><li>Cluster：Endpoint 提供或使用的标准功能。</li></ul><p>排障时不要把短地址当作永久身份。跨重启、离网重入或地址冲突处理后，应使用 EUI-64、时间线和设备公告重新确认节点。</p><h2 id="Cluster、Attribute-与-Command"><a href="#Cluster、Attribute-与-Command" class="headerlink" title="Cluster、Attribute 与 Command"></a>Cluster、Attribute 与 Command</h2><p>ZCL 把设备能力拆成 Cluster：</p><ul><li>Server 保存状态、提供属性或接收命令；</li><li>Client 发起命令或消费 Server 产生的信息；</li><li>Attribute 表示状态或配置；</li><li>Command 表示一次操作或协议动作；</li><li>Reporting 让属性按配置主动上报。</li></ul><p>同一个 Cluster ID 的 Server 和 Client 是两个方向，不能因为 Endpoint 列出了 Cluster 就认为双向能力都存在。</p><h2 id="单播、广播、组播与绑定"><a href="#单播、广播、组播与绑定" class="headerlink" title="单播、广播、组播与绑定"></a>单播、广播、组播与绑定</h2><ul><li>单播：发送给一个目标节点；</li><li>广播：发送给一类或全部节点，成本较高；</li><li>组播：发送给 Group ID 对应的一组 Endpoint；</li><li>Binding：在源 Endpoint 中保存逻辑目标，使应用不必每次指定完整目的地址。</li></ul><p>广播不是“更可靠的单播”。它会增加全网负载，且不同广播范围、重试和确认语义并不相同。</p><h2 id="一条应用消息经过什么"><a href="#一条应用消息经过什么" class="headerlink" title="一条应用消息经过什么"></a>一条应用消息经过什么</h2><p>以属性上报为例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">传感器状态变化</span><br><span class="line">  -&gt; ZCL 生成 Report Attributes</span><br><span class="line">  -&gt; APS 选择目的 Endpoint、绑定或地址</span><br><span class="line">  -&gt; NWK 选择下一跳并进行网络层保护</span><br><span class="line">  -&gt; MAC 在当前信道发送</span><br><span class="line">  -&gt; 目标逐层解密、分发并处理</span><br></pre></td></tr></table></figure><p>每一层都有独立失败点。可靠分析应记录：</p><ol><li>应用是否生成消息；</li><li>APS 是否接受发送请求；</li><li>NWK 是否找到路由；</li><li>MAC 是否发出并收到确认；</li><li>对端是否通过安全校验；</li><li>对端 Endpoint 与 Cluster 是否接受；</li><li>应用状态是否真正改变。</li></ol><h2 id="规范边界"><a href="#规范边界" class="headerlink" title="规范边界"></a>规范边界</h2><p>Zigbee 仍在演进。学习基础概念可以使用公开资料，但实现和认证必须冻结对应的 Zigbee Core、BDB、ZCL、Device Type Library、Errata、PICS 和测试规范，不能把不同 Revision 的要求拼在一起。</p><p>可从 <a href="https://csa-iot.org/all-solutions/zigbee/">Zigbee 官方概览</a>、<a href="https://csa-iot.org/all-solutions/zigbee/zigbee-faq/">Zigbee FAQ</a> 和 <a href="https://csa-iot.org/developer-resource/specifications-download-request/">CSA 规范下载入口</a>继续阅读。</p>]]>
    </content>
    <id>https://oniums.github.io/posts/zigbee-foundations/</id>
    <link href="https://oniums.github.io/posts/zigbee-foundations/"/>
    <published>2026-07-28T13:30:00.000Z</published>
    <summary>
      <![CDATA[<p>理解 Zigbee 最有效的方法，不是先背命令，而是把“无线、组网、传输、设备管理、应用功能”分层。这样看日志或抓包时，才能知道一条成功信息究竟证明了哪一步。</p>]]>
    </summary>
    <title>Zigbee 基础概念：从无线帧到 Endpoint 与 Cluster</title>
    <updated>2026-07-28T13:36:40.299Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Commissioning" scheme="https://oniums.github.io/tags/commissioning/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="BDB" scheme="https://oniums.github.io/tags/bdb/"/>
    <category term="问题排查" scheme="https://oniums.github.io/tags/%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/"/>
    <content>
      <![CDATA[<p>“设备已经入网”经常被过早下结论。扫描到网络、拿到短地址、收到 Network Key、完成 Trust Center Link Key 交换和网关识别出应用能力，是不同阶段。</p><span id="more"></span><h2 id="先区分三条路径"><a href="#先区分三条路径" class="headerlink" title="先区分三条路径"></a>先区分三条路径</h2><ul><li>首次加入：Factory New 设备通过 Network Steering 寻找允许加入的网络；</li><li>Rejoin：设备保留网络信息后重新连接，流程和安全条件不同；</li><li>Touchlink：近距离发现和引导入网的另一套 commissioning 方法。</li></ul><p>本文讨论经典的首次 Network Steering。实际实现应以目标 BDB、Zigbee Core 和安全策略版本为准。</p><h2 id="阶段一：打开网络与扫描"><a href="#阶段一：打开网络与扫描" class="headerlink" title="阶段一：打开网络与扫描"></a>阶段一：打开网络与扫描</h2><p>网络侧进入 Permit Join 状态。待入网设备在配置的主信道集扫描 Beacon，必要时再扫描次信道集，并根据网络容量、信号和安全信息选择候选网络。</p><p>这一阶段的成功锚点是：</p><ul><li>找到目标 PAN；</li><li>目标网络允许加入；</li><li>设备选择了预期信道与 Extended PAN ID。</li></ul><p>只看到 Beacon 不能证明设备已经发起关联。</p><h2 id="阶段二：关联并获得网络地址"><a href="#阶段二：关联并获得网络地址" class="headerlink" title="阶段二：关联并获得网络地址"></a>阶段二：关联并获得网络地址</h2><p>设备向父节点候选发起关联，父节点返回状态和 16 位网络地址。此后设备具备拓扑位置，但还不代表已经拿到可用的安全材料。</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Scan</span><br><span class="line">  -&gt; Select network</span><br><span class="line">  -&gt; Association Request</span><br><span class="line">  -&gt; Association Response</span><br><span class="line">  -&gt; Short address assigned</span><br></pre></td></tr></table></figure><p>MAC 层关联成功只证明链路和父子关系初步建立。</p><h2 id="阶段三：安全准入与-Network-Key"><a href="#阶段三：安全准入与-Network-Key" class="headerlink" title="阶段三：安全准入与 Network Key"></a>阶段三：安全准入与 Network Key</h2><p>集中式安全网络中，Trust Center 决定设备是否被接纳，并通过受保护的密钥传输把 Network Key 交给新设备。设备必须：</p><ol><li>接收到正确的 Transport Key；</li><li>使用匹配的预配置或 install-code 派生 Link Key 验证并解密；</li><li>安装正确的 Network Key 及其序列号；</li><li>启动受保护的 NWK 通信。</li></ol><p>所以“Transport Key 已发出”不等于“设备已安装 Network Key”。决定性证据在接收端的 APS 安全处理和密钥安装结果。</p><h2 id="阶段四：设备公告与-Trust-Center-Link-Key"><a href="#阶段四：设备公告与-Trust-Center-Link-Key" class="headerlink" title="阶段四：设备公告与 Trust Center Link Key"></a>阶段四：设备公告与 Trust Center Link Key</h2><p>设备通常会发送 Device Announcement，让网络获知其地址映射。根据 BDB 版本和 Trust Center 策略，后续还可能执行 Trust Center Link Key 的更新或交换。</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Network Key installed</span><br><span class="line">  -&gt; secure network communication</span><br><span class="line">  -&gt; Device Announcement</span><br><span class="line">  -&gt; Trust Center Link Key procedure</span><br><span class="line">  -&gt; commissioning complete</span><br></pre></td></tr></table></figure><p>如果日志显示网络层通信已经成功，但 BDB 最终报告 Trust Center Link Key 交换失败，就不应把问题归回“没有收到 Network Key”。</p><h2 id="阶段五：应用发现"><a href="#阶段五：应用发现" class="headerlink" title="阶段五：应用发现"></a>阶段五：应用发现</h2><p>网络接入完成后，控制端通常还会：</p><ul><li>查询 Node Descriptor；</li><li>查询 Active Endpoints；</li><li>读取 Simple Descriptor；</li><li>读取 Basic 属性；</li><li>配置 Reporting；</li><li>建立 Binding 或执行专用 Cluster 初始化。</li></ul><p>设备在网络里可达，不等于已经被生态完整识别。应用发现失败应与基础入网失败分开记录。</p><h2 id="推荐排障时间线"><a href="#推荐排障时间线" class="headerlink" title="推荐排障时间线"></a>推荐排障时间线</h2><table><thead><tr><th>阶段</th><th>最小成功证据</th><th>常见误判</th></tr></thead><tbody><tr><td>扫描</td><td>选择目标网络</td><td>把看到 Beacon 当作入网</td></tr><tr><td>关联</td><td>成功状态与短地址</td><td>把 MAC ACK 当作安全成功</td></tr><tr><td>密钥传输</td><td>接收端安装 Network Key</td><td>只看发送端“delivered”</td></tr><tr><td>安全通信</td><td>能发送并接收受保护 NWK 帧</td><td>不检查 frame counter 与 key sequence</td></tr><tr><td>TCLK</td><td>BDB 明确完成</td><td>把 Network Key 成功等同 TCLK 成功</td></tr><tr><td>应用发现</td><td>Endpoint、Cluster、属性交互完成</td><td>把可 ping 或可寻址当作设备可用</td></tr></tbody></table><h2 id="抓包与日志怎么对齐"><a href="#抓包与日志怎么对齐" class="headerlink" title="抓包与日志怎么对齐"></a>抓包与日志怎么对齐</h2><p>记录统一时间基准，并同时保留：</p><ul><li>设备串口日志；</li><li>Coordinator &#x2F; Trust Center 日志；</li><li>802.15.4 抓包；</li><li>测试工具或网关事件；</li><li>固件版本和本次安全策略。</li></ul><p>密钥、install code、地址和抓包解密材料只应在授权的本地环境使用，不应复制到公开问题单或文章。</p><p>规范入口可参考 <a href="https://csa-iot.org/developer-resource/specifications-download-request/">CSA Zigbee 规范下载页</a>；认证自测还应使用目标版本的 BDB PICS 和正式测试规范。</p>]]>
    </content>
    <id>https://oniums.github.io/posts/zigbee-network-joining-flow/</id>
    <link href="https://oniums.github.io/posts/zigbee-network-joining-flow/"/>
    <published>2026-07-28T13:20:00.000Z</published>
    <summary>
      <![CDATA[<p>“设备已经入网”经常被过早下结论。扫描到网络、拿到短地址、收到 Network Key、完成 Trust Center Link Key 交换和网关识别出应用能力，是不同阶段。</p>]]>
    </summary>
    <title>Zigbee 入网流程：从 Network Steering 到可用设备</title>
    <updated>2026-07-28T13:36:40.300Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="Security" scheme="https://oniums.github.io/tags/security/"/>
    <category term="Network Key" scheme="https://oniums.github.io/tags/network-key/"/>
    <category term="Link Key" scheme="https://oniums.github.io/tags/link-key/"/>
    <content>
      <![CDATA[<p>Zigbee 排障中最常见的安全误区，是把所有 128-bit Key 都理解成同一种东西。Key 的长度相同，不代表作用层、通信对象、生命周期和泄露影响相同。</p><span id="more"></span><h2 id="一张作用域表"><a href="#一张作用域表" class="headerlink" title="一张作用域表"></a>一张作用域表</h2><table><thead><tr><th>Key</th><th>典型持有者</th><th>保护范围</th><th>主要用途</th></tr></thead><tbody><tr><td>Network Key</td><td>网络内节点</td><td>整个 Zigbee 网络的 NWK 层</td><td>日常网络层帧保护</td></tr><tr><td>Trust Center Link Key</td><td>单个设备与 Trust Center</td><td>两个节点之间的 APS 层</td><td>安全管理、密钥传输与认证</td></tr><tr><td>Install-code 派生 Key</td><td>新设备与 Trust Center</td><td>初次准入阶段</td><td>为设备提供唯一的初始信任</td></tr><tr><td>Application Link Key</td><td>两个应用节点</td><td>端到端 APS 层</td><td>保护特定设备间应用通信</td></tr><tr><td>Touchlink commissioning Key</td><td>支持 Touchlink 的设备</td><td>Touchlink 入网阶段</td><td>保护网络参数传输</td></tr><tr><td>Green Power Key</td><td>Green Power 角色</td><td>Green Power 子系统</td><td>保护对应的精简设备通信</td></tr></tbody></table><p>表格描述的是概念边界。具体必选能力和密钥派生方法必须回到目标 Zigbee Core、BDB、ZCL、Green Power 与安全策略版本确认。</p><h2 id="Network-Key"><a href="#Network-Key" class="headerlink" title="Network Key"></a>Network Key</h2><p>Network Key 是网络范围的共享密钥，主要用于 NWK 层的机密性、完整性和重放保护。它让节点能够参与正常 Zigbee 网络通信。</p><p>要点：</p><ul><li>同一时刻会有活动 Key Sequence；</li><li>网络可以执行 Network Key Update 和 Switch；</li><li>拿到 Network Key 不等于拥有与 Trust Center 的唯一信任关系；</li><li>Network Key 泄露的影响通常覆盖整个网络。</li></ul><p>抓包能用 Network Key 解密 NWK 帧，也不代表内部 APS 负载一定能继续解密。</p><h2 id="Trust-Center-Link-Key"><a href="#Trust-Center-Link-Key" class="headerlink" title="Trust Center Link Key"></a>Trust Center Link Key</h2><p>Trust Center Link Key 是设备与 Trust Center 之间的 APS Link Key，用于安全管理和特定 APS 加密流程。</p><p>它可能经历：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">初始 bootstrap key</span><br><span class="line">  -&gt; 安全加入</span><br><span class="line">  -&gt; Trust Center Link Key 更新或交换</span><br><span class="line">  -&gt; 唯一设备级 TCLK</span><br></pre></td></tr></table></figure><p>具体路径由 BDB 版本、设备能力和 Trust Center 策略决定。调试时要分开记录“初始 Key 可用”和“最终 TCLK 交换完成”。</p><h2 id="Install-code-的位置"><a href="#Install-code-的位置" class="headerlink" title="Install code 的位置"></a>Install code 的位置</h2><p>Install code 本身不是日常 NWK 通信使用的 Network Key。它经过规范定义的处理后，形成设备唯一的初始 Link Key，用于安全加入。</p><p>它改善了共享 bootstrap key 的风险，但同时要求：</p><ul><li>生产、标签或扫码数据正确绑定到设备；</li><li>Trust Center 预先获得匹配信息；</li><li>长度、校验和、字节序与派生过程一致；</li><li>测试数据不能进入量产。</li></ul><p>任何 install code、派生 Key 或二维码内容都不应出现在公开日志中。</p><h2 id="Application-Link-Key"><a href="#Application-Link-Key" class="headerlink" title="Application Link Key"></a>Application Link Key</h2><p>Application Link Key 用于两个应用节点之间的端到端 APS 安全。它不能替代 Network Key，因为 Zigbee 路由仍依赖受保护的 NWK 层；它也不应被笼统称为 TCLK，因为通信对端可能不是 Trust Center。</p><p>分析加密帧时可按顺序判断：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MAC frame</span><br><span class="line">  -&gt; NWK security：Network Key</span><br><span class="line">  -&gt; APS security：对应 Link Key</span><br><span class="line">  -&gt; ZCL payload</span><br></pre></td></tr></table></figure><h2 id="全局-bootstrap-key-为什么要谨慎"><a href="#全局-bootstrap-key-为什么要谨慎" class="headerlink" title="全局 bootstrap key 为什么要谨慎"></a>全局 bootstrap key 为什么要谨慎</h2><p>某些兼容路径使用规范定义的全局共享初始 Link Key。因为它不是每台设备唯一的秘密，安全强度依赖后续策略，例如尽快升级到唯一 TCLK、限制 Permit Join 时间和执行设备鉴权。</p><p>公开文章无需、也不应抄写这个 Key 的实际值。知道“它是共享 bootstrap 材料”已经足够用于设计和排障。</p><h2 id="Touchlink-与-Green-Power-不要混入普通路径"><a href="#Touchlink-与-Green-Power-不要混入普通路径" class="headerlink" title="Touchlink 与 Green Power 不要混入普通路径"></a>Touchlink 与 Green Power 不要混入普通路径</h2><p>Touchlink commissioning Key 服务于 Touchlink 的网络参数交换；Green Power 还有自己的共享、组或单设备安全材料。它们不应被当成普通 Zigbee 设备的 Network Key 或 TCLK。</p><h2 id="排障时至少记录什么"><a href="#排障时至少记录什么" class="headerlink" title="排障时至少记录什么"></a>排障时至少记录什么</h2><p>不要记录 Key 内容，而是记录元数据：</p><ul><li>安全层：NWK 还是 APS；</li><li>Key 类型和逻辑槽位；</li><li>Key Sequence；</li><li>对端角色；</li><li>frame counter 是否前进；</li><li>解密、MIC 验证和重放检查结果；</li><li>Key 安装、更新、切换或删除事件。</li></ul><p>“发送成功”只能证明本端协议栈接收了请求或下层完成传输；只有对端安全层验证通过，才能证明使用了匹配的 Key。</p><p>可从 <a href="https://csa-iot.org/developer-resource/specifications-download-request/">Zigbee 官方规范入口</a>获取当前公开规范。认证或量产策略必须再与芯片平台安全指南及实验室冻结版本核对。</p>]]>
    </content>
    <id>https://oniums.github.io/posts/zigbee-security-key-scope/</id>
    <link href="https://oniums.github.io/posts/zigbee-security-key-scope/"/>
    <published>2026-07-28T13:10:00.000Z</published>
    <summary>
      <![CDATA[<p>Zigbee 排障中最常见的安全误区，是把所有 128-bit Key 都理解成同一种东西。Key 的长度相同，不代表作用层、通信对象、生命周期和泄露影响相同。</p>]]>
    </summary>
    <title>Zigbee 各类 Key 的作用范围：不要把 Network Key 和 Link Key 混为一谈</title>
    <updated>2026-07-28T13:36:40.300Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="Trust Center" scheme="https://oniums.github.io/tags/trust-center/"/>
    <category term="集中式网络" scheme="https://oniums.github.io/tags/%E9%9B%86%E4%B8%AD%E5%BC%8F%E7%BD%91%E7%BB%9C/"/>
    <category term="分布式网络" scheme="https://oniums.github.io/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E7%BD%91%E7%BB%9C/"/>
    <content>
      <![CDATA[<p>“集中式”和“分布式”首先描述安全与网络管理模型，不是简单描述数据包是否经过 Coordinator。集中式 Zigbee 网络同样可以由 Router 之间直接多跳转发。</p><span id="more"></span><h2 id="核心对比"><a href="#核心对比" class="headerlink" title="核心对比"></a>核心对比</h2><table><thead><tr><th>维度</th><th>集中式安全网络</th><th>分布式安全网络</th></tr></thead><tbody><tr><td>信任中心</td><td>有 Trust Center</td><td>没有中央 Trust Center</td></tr><tr><td>准入决策</td><td>由 Trust Center 控制</td><td>由分布式网络规则协同</td></tr><tr><td>Link Key</td><td>可建立设备唯一 TCLK</td><td>不依赖中央 TCLK 模型</td></tr><tr><td>网络管理</td><td>关键安全策略集中</td><td>形成和管理更分散</td></tr><tr><td>常见场景</td><td>网关型生态、统一管理</td><td>特定照明或去中心化场景</td></tr></tbody></table><p>这只是概念表。允许的设备类型、加入方式、密钥交换和兼容条件随 Zigbee&#x2F;BDB 版本变化，认证时必须看冻结规范。</p><h2 id="集中式网络"><a href="#集中式网络" class="headerlink" title="集中式网络"></a>集中式网络</h2><p>集中式网络通常由 Coordinator 建网并承担 Trust Center 角色。Trust Center 负责：</p><ul><li>决定是否允许新设备加入；</li><li>管理初始信任与设备鉴权；</li><li>传输或更新 Network Key；</li><li>管理设备级 Trust Center Link Key；</li><li>移除不再可信的设备。</li></ul><p>路由仍由 Mesh 完成。设备 A 到设备 B 的正常业务帧不必每次穿过 Trust Center。</p><h2 id="分布式网络"><a href="#分布式网络" class="headerlink" title="分布式网络"></a>分布式网络</h2><p>分布式网络没有一个持续承担中央信任决策的 Trust Center。形成网络、扩展网络和安全管理依赖分布式规则及共享的安全基础。</p><p>它减少了对中央角色的依赖，但不代表：</p><ul><li>没有 Network Key；</li><li>没有加密和重放保护；</li><li>任意设备都可无条件加入；</li><li>与集中式设备天然完全兼容。</li></ul><p>分布式是另一套受规范约束的安全模型，不是“关闭安全”。</p><h2 id="Coordinator、Trust-Center-与-Router-的关系"><a href="#Coordinator、Trust-Center-与-Router-的关系" class="headerlink" title="Coordinator、Trust Center 与 Router 的关系"></a>Coordinator、Trust Center 与 Router 的关系</h2><p>三个概念容易混淆：</p><ul><li>Coordinator：形成 PAN 的逻辑设备；</li><li>Trust Center：集中式安全网络中的信任管理角色；</li><li>Router：参与 Mesh 路由并可能允许子节点关联。</li></ul><p>集中式网络中 Coordinator 通常兼任 Trust Center，但这不等于它是所有应用报文的中心转发器。</p><h2 id="选择时看什么"><a href="#选择时看什么" class="headerlink" title="选择时看什么"></a>选择时看什么</h2><ol><li>目标生态是否要求中央 Trust Center；</li><li>是否需要 install code 和设备唯一 TCLK；</li><li>目标设备及历史 Profile 是否支持该网络类型；</li><li>是否需要 Touchlink；</li><li>网络形成、备份、迁移与恢复策略；</li><li>对失窃节点、共享 bootstrap 材料和密钥轮换的风险接受度；</li><li>对应认证计划和 PICS 允许什么。</li></ol><p>不要只因为“想避免单点故障”就选择分布式网络。集中式安全角色与 Mesh 数据路径是不同问题；Zigbee Router 的多路径转发仍可提供网络韧性。</p><h2 id="抓包如何识别"><a href="#抓包如何识别" class="headerlink" title="抓包如何识别"></a>抓包如何识别</h2><p>单靠一帧普通 NWK 数据通常不足以判断网络模型。应结合：</p><ul><li>网络形成日志；</li><li>Trust Center 地址与策略；</li><li>Beacon 或网络安全相关信息；</li><li>加入时的密钥传输路径；</li><li>TCLK 交换行为；</li><li>BDB 配置与 PICS。</li></ul><h2 id="版本与兼容边界"><a href="#版本与兼容边界" class="headerlink" title="版本与兼容边界"></a>版本与兼容边界</h2><p>旧的 Zigbee Light Link、Home Automation 与 Zigbee 3.x&#x2F;4.x 之间存在特定兼容规则，不能用“都是 Zigbee”代替逐项核对。CSA 的 <a href="https://csa-iot.org/wp-content/uploads/2021/12/04-2017-Interoperability-ORIGINAL-White-Paper-Final-Musa-and-Shashank-1.pdf">互操作白皮书</a>可帮助理解历史关系；当前实现仍应以 <a href="https://csa-iot.org/developer-resource/specifications-download-request/">CSA 规范下载入口</a>和认证实验室确认的版本为准。</p>]]>
    </content>
    <id>https://oniums.github.io/posts/zigbee-centralized-distributed-networks/</id>
    <link href="https://oniums.github.io/posts/zigbee-centralized-distributed-networks/"/>
    <published>2026-07-28T13:00:00.000Z</published>
    <summary>
      <![CDATA[<p>“集中式”和“分布式”首先描述安全与网络管理模型，不是简单描述数据包是否经过 Coordinator。集中式 Zigbee 网络同样可以由 Router 之间直接多跳转发。</p>]]>
    </summary>
    <title>Zigbee 集中式网络与分布式网络：差异主要在信任模型</title>
    <updated>2026-07-28T13:36:40.300Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="无线协议" scheme="https://oniums.github.io/categories/%E6%97%A0%E7%BA%BF%E5%8D%8F%E8%AE%AE/"/>
    <category term="Commissioning" scheme="https://oniums.github.io/tags/commissioning/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="ZCL" scheme="https://oniums.github.io/tags/zcl/"/>
    <category term="Touchlink" scheme="https://oniums.github.io/tags/touchlink/"/>
    <content>
      <![CDATA[<p>Touchlink 是 Zigbee 的一种近距离 commissioning 机制，最初广泛用于照明设备。它不是普通的 Network Steering，也不是“按键后信号强就自动入网”这么简单。</p><span id="more"></span><h2 id="两个角色"><a href="#两个角色" class="headerlink" title="两个角色"></a>两个角色</h2><ul><li>Initiator：发起 Touchlink 的控制设备；</li><li>Target：被发现、识别、复位或加入网络的设备。</li></ul><p>两者通过 Touchlink Commissioning Cluster 交互。是否支持某条命令、某种设备角色和网络类型，应由目标版本的 PICS 决定。</p><h2 id="高层流程"><a href="#高层流程" class="headerlink" title="高层流程"></a>高层流程</h2><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Initiator 扫描信道</span><br><span class="line">  -&gt; Scan Request</span><br><span class="line">  &lt;- Scan Response</span><br><span class="line">  -&gt; 根据距离、能力和状态选择 Target</span><br><span class="line">  -&gt; 可选 Identify / Device Information</span><br><span class="line">  -&gt; Network Start 或 Network Join</span><br><span class="line">  -&gt; 受保护地传输网络参数</span><br><span class="line">  -&gt; Target 切换信道并启动网络</span><br><span class="line">  -&gt; 后续应用发现与配置</span><br></pre></td></tr></table></figure><p>Touchlink 还可能提供 Reset to Factory New 等管理动作，因此触发条件和物理在场约束非常重要。</p><h2 id="扫描与“近距离”判断"><a href="#扫描与“近距离”判断" class="headerlink" title="扫描与“近距离”判断"></a>扫描与“近距离”判断</h2><p>Initiator 会在规定信道上发送扫描请求，Target 返回能力、网络状态等信息。实现通常根据接收信号强度或协议定义的 proximity 条件筛选目标。</p><p>RSSI 只是一项输入：</p><ul><li>不同硬件的接收链路和校准不同；</li><li>环境反射会造成波动；</li><li>强信号不等于身份可信；</li><li>过宽阈值可能误选附近其他设备。</li></ul><p>因此还应结合用户动作、设备状态、响应事务标识和超时窗口。</p><h2 id="Identify-不是入网"><a href="#Identify-不是入网" class="headerlink" title="Identify 不是入网"></a>Identify 不是入网</h2><p>Identify 让用户确认正在操作哪台设备，例如闪灯或执行可见动作。它不表示网络参数已经写入，也不表示 Target 已完成加入。</p><p>推荐成功锚点：</p><ol><li>Scan Response 匹配当前事务；</li><li>选择了唯一目标；</li><li>Identify 行为可被用户确认；</li><li>Network Start&#x2F;Join 响应成功；</li><li>Target 在目标信道启动；</li><li>后续受保护 Zigbee 通信成功。</li></ol><h2 id="网络参数传输"><a href="#网络参数传输" class="headerlink" title="网络参数传输"></a>网络参数传输</h2><p>Touchlink 使用专门的 commissioning 安全过程保护 Network Key 等网络参数。其 commissioning Key 只服务于这一阶段，不应与日常 NWK 使用的 Network Key 混淆。</p><p>公开日志中不应输出：</p><ul><li>commissioning Key；</li><li>Network Key；</li><li>未脱敏的设备唯一地址；</li><li>可用于复现实网加入的数据。</li></ul><h2 id="与经典加入的区别"><a href="#与经典加入的区别" class="headerlink" title="与经典加入的区别"></a>与经典加入的区别</h2><table><thead><tr><th>项目</th><th>Touchlink</th><th>Network Steering</th></tr></thead><tbody><tr><td>发现方式</td><td>主动近距离扫描目标</td><td>扫描允许加入的网络</td></tr><tr><td>主要角色</td><td>Initiator &#x2F; Target</td><td>Trust Center、父节点、Joiner</td></tr><tr><td>典型触发</td><td>用户近距离操作</td><td>设备进入入网模式</td></tr><tr><td>安全路径</td><td>Touchlink commissioning 安全</td><td>BDB 加入与 Link Key 策略</td></tr><tr><td>后续结果</td><td>建网、加入或管理动作</td><td>加入现有网络</td></tr></tbody></table><p>Touchlink 成功后仍需要正常 Zigbee 网络和应用层交互；它不是独立的日常通信协议。</p><h2 id="常见失败分类"><a href="#常见失败分类" class="headerlink" title="常见失败分类"></a>常见失败分类</h2><ul><li>扫描不到：信道集、时序、角色能力或距离门限；</li><li>响应被丢弃：事务标识、重复帧或状态不匹配；</li><li>Identify 成功但 Join 失败：网络状态、目标角色或安全参数；</li><li>参数写入后未启动：信道切换、持久化或状态机；</li><li>入网后不可用：Endpoint 发现、Cluster、binding 或 reporting；</li><li>Reset 行为异常：Factory New 判定和持久化清理不完整。</li></ul><h2 id="规范边界"><a href="#规范边界" class="headerlink" title="规范边界"></a>规范边界</h2><p>旧 ZCL 中可以找到 Touchlink Commissioning Cluster，但认证实现不能只照抄旧章节。应同时冻结目标 Zigbee Core、BDB、ZCL、PICS、Errata 和测试用例，并确认当前认证计划是否接受所选 Touchlink 能力。</p><p>公开入口包括 <a href="https://csa-iot.org/developer-resource/specifications-download-request/">CSA Zigbee 规范下载页</a>与 <a href="https://csa-iot.org/certification/tools/">Zigbee 认证工具页</a>。</p>]]>
    </content>
    <id>https://oniums.github.io/posts/zigbee-touchlink-commissioning/</id>
    <link href="https://oniums.github.io/posts/zigbee-touchlink-commissioning/"/>
    <published>2026-07-28T12:50:00.000Z</published>
    <summary>
      <![CDATA[<p>Touchlink 是 Zigbee 的一种近距离 commissioning 机制，最初广泛用于照明设备。它不是普通的 Network Steering，也不是“按键后信号强就自动入网”这么简单。</p>]]>
    </summary>
    <title>Zigbee Touchlink 流程：近距离发现、识别与入网</title>
    <updated>2026-07-28T13:36:40.300Z</updated>
  </entry>
  <entry>
    <author>
      <name>Oniums</name>
    </author>
    <category term="认证实践" scheme="https://oniums.github.io/categories/%E8%AE%A4%E8%AF%81%E5%AE%9E%E8%B7%B5/"/>
    <category term="PICS" scheme="https://oniums.github.io/tags/pics/"/>
    <category term="Zigbee" scheme="https://oniums.github.io/tags/zigbee/"/>
    <category term="Zigbee 3.0" scheme="https://oniums.github.io/tags/zigbee-3-0/"/>
    <category term="ZUTH" scheme="https://oniums.github.io/tags/zuth/"/>
    <content>
      <![CDATA[<p>认证自测的目标不是证明“功能大致能用”，而是在送测前尽量复现正式测试的输入、声明、环境和判定方式，提前找出会阻断认证的问题。</p><span id="more"></span><h2 id="第一步不是运行工具"><a href="#第一步不是运行工具" class="headerlink" title="第一步不是运行工具"></a>第一步不是运行工具</h2><p>先向 CSA 或授权实验室确认并冻结：</p><ul><li>认证计划和 Zigbee 版本；</li><li>Zigbee Core &#x2F; PRO、BDB、ZCL、Device Type Library；</li><li>Approved Errata；</li><li>PICS &#x2F; PIXIT 模板；</li><li>Test Specification 与测试用例版本；</li><li>ZUTH、脚本、dongle 和固件版本。</li></ul><p>“Zigbee 3.0”是一个过于宽泛的标签。公开工具可能同时保留多个 BDB、ZCL 和 PRO 输入；不能自行把最新版文档和旧测试脚本拼在一起。</p><h2 id="建立声明矩阵"><a href="#建立声明矩阵" class="headerlink" title="建立声明矩阵"></a>建立声明矩阵</h2><p>从每个 Endpoint 展开：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Profile ID / Device ID</span><br><span class="line">  -&gt; Server / Client Cluster</span><br><span class="line">  -&gt; Mandatory / Optional / Conditional</span><br><span class="line">  -&gt; Attribute 类型、范围、默认值和权限</span><br><span class="line">  -&gt; Reporting</span><br><span class="line">  -&gt; Commands Received / Generated</span><br><span class="line">  -&gt; PICS / PIXIT</span><br><span class="line">  -&gt; 对应测试用例</span><br></pre></td></tr></table></figure><p>一旦实现了 Optional 能力，它通常也会进入声明和测试范围。为了减少测试，不应把已经对外可见的能力错误标成不支持。</p><h2 id="四层自测"><a href="#四层自测" class="headerlink" title="四层自测"></a>四层自测</h2><h3 id="静态核对"><a href="#静态核对" class="headerlink" title="静态核对"></a>静态核对</h3><ul><li>Simple Descriptor 与实际 Cluster 注册一致；</li><li>ClusterRevision 和 ZCLVersion 对应冻结版本；</li><li>属性类型、默认值、访问权限和范围正确；</li><li>接收与发送命令方向正确；</li><li>PICS 与固件能力一致；</li><li>认证配置确实进入最终镜像。</li></ul><h3 id="功能与状态机"><a href="#功能与状态机" class="headerlink" title="功能与状态机"></a>功能与状态机</h3><ul><li>标准读写、命令和 reporting；</li><li>默认响应与异常状态；</li><li>初始化、重启、掉电与恢复出厂；</li><li>入网、退网、rejoin、换父与丢网恢复；</li><li>binding、group、scene 等已声明能力；</li><li>OTA、低电量和持久化边界。</li></ul><h3 id="安全与-BDB"><a href="#安全与-BDB" class="headerlink" title="安全与 BDB"></a>安全与 BDB</h3><ul><li>Network Steering 和 Permit Join；</li><li>install-code 策略；</li><li>Network Key 安装、更新和切换；</li><li>Trust Center Link Key 流程；</li><li>frame counter 与重放拒绝；</li><li>集中式或分布式网络声明；</li><li>Touchlink 等可选 commissioning 能力。</li></ul><h3 id="正式工具预跑"><a href="#正式工具预跑" class="headerlink" title="正式工具预跑"></a>正式工具预跑</h3><p>CSA 的 ZUTH 是 Zigbee 正式认证测试工具，也向成员提供预认证测试能力。导入已审查的 PICS，运行所有被选择用例，并保留原始结果。</p><h2 id="Sleepy-End-Device-专项"><a href="#Sleepy-End-Device-专项" class="headerlink" title="Sleepy End Device 专项"></a>Sleepy End Device 专项</h2><p>低功耗设备常在正式测试中暴露时序问题：</p><ul><li>Poll Control 行为；</li><li>Fast Poll 窗口；</li><li>父节点缓存与间接传输；</li><li>reporting 延迟；</li><li>enrollment 或 commissioning 期间的接收窗口；</li><li>测试刺激与设备睡眠状态的同步。</li></ul><p>不要通过永久关闭休眠来“通过认证”，除非认证固件与量产行为差异得到明确允许并被记录。</p><h2 id="失败归类"><a href="#失败归类" class="headerlink" title="失败归类"></a>失败归类</h2><table><thead><tr><th>类别</th><th>例子</th><th>下一步证据</th></tr></thead><tbody><tr><td>声明错误</td><td>PICS 与 Endpoint 不一致</td><td>描述符、PICS、构建配置</td></tr><tr><td>协议错误</td><td>命令方向或状态码错误</td><td>空口与 DUT 日志</td></tr><tr><td>时序错误</td><td>超时、休眠错过命令</td><td>统一时间线、poll 日志</td></tr><tr><td>环境错误</td><td>dongle、脚本或版本不匹配</td><td>环境清单与工具日志</td></tr><tr><td>用例解释</td><td>前置条件理解不同</td><td>测试规范、实验室确认</td></tr></tbody></table><p>修复后要回归相关用例集合，不能只重跑最初失败的一步。</p><h2 id="自测证据包"><a href="#自测证据包" class="headerlink" title="自测证据包"></a>自测证据包</h2><p>每轮保存：</p><ul><li>固件版本、源码提交和构建哈希；</li><li>PICS &#x2F; PIXIT；</li><li>环境和工具版本；</li><li>测试用例列表；</li><li>原始 ZUTH 报告；</li><li>DUT、测试端与抓包日志；</li><li>失败分析、修复提交和回归结果；</li><li>尚未验证的限制。</li></ul><p>证据层级必须分开：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">规范要求</span><br><span class="line">  != PICS 声明</span><br><span class="line">  != 静态实现</span><br><span class="line">  != 设备运行通过</span><br><span class="line">  != ZUTH 自测通过</span><br><span class="line">  != 授权实验室通过</span><br><span class="line">  != CSA 正式认证</span><br></pre></td></tr></table></figure><p>可从 <a href="https://csa-iot.org/certification/tools/">CSA 认证工具页</a>获取 PICS Tool 和 ZUTH 说明。当前版本和送测范围仍应由 <a href="https://csa-iot.org/certification/testing-providers/">授权测试机构</a>确认。</p>]]>
    </content>
    <id>https://oniums.github.io/posts/zigbee-3-certification-self-test/</id>
    <link href="https://oniums.github.io/posts/zigbee-3-certification-self-test/"/>
    <published>2026-07-28T12:40:00.000Z</published>
    <summary>
      <![CDATA[<p>认证自测的目标不是证明“功能大致能用”，而是在送测前尽量复现正式测试的输入、声明、环境和判定方式，提前找出会阻断认证的问题。</p>]]>
    </summary>
    <title>Zigbee 3.0 认证自测：从版本冻结到证据包</title>
    <updated>2026-07-28T13:36:40.301Z</updated>
  </entry>
</feed>
