Arduino ESP32 Matter 设备身份与配对码自定义指南:MatterDeviceIdentity 示例深度解析
【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32
导读
本篇文章围绕 arduino-esp32 仓库中的 MatterDeviceIdentity 示例展开,讲解如何在调用Matter.begin()之前,通过Matter单例设置 Matter 节点的身份信息(厂商名、产品名、节点标签、序列号、硬件版本)与配对凭据(discriminator 与 PIN),并理解配对码生成、状态机判定与烧录流程。读完本文,你将掌握在 ESP32 系列 SoC 上自定义 Matter 设备出厂身份、正确使用生成的配对码完成配网,以及基于isDeviceCommissioned()/isDeviceConnected()/isOnline()三个状态标志构建稳健应用的方法。
示例概览:在 begin() 之前定制节点身份
MatterDeviceIdentity 示例基于与 Matter On/Off Light 完全相同的硬件(一个 LED 加一个按键),但在Matter.begin()之前额外设置了 VendorName、ProductName、DeviceName(即 NodeLabel)、SerialNumber、硬件版本,以及一组非默认的 discriminator/PIN。它的核心价值在于演示一个经常被忽视的时序约束:所有身份与配网凭据的 setter 都必须在Matter.begin()之前调用,begin()之后调用会打印警告并被忽略。
示例源码位于 MatterDeviceIdentity.ino,其setup()中的身份配置片段如下:
Matter.setVendorName("Espressif"); Matter.setProductName("KitchenLight"); Matter.setDeviceName("KitchenHub"); // Basic Information NodeLabel Matter.setSerialNumber("KH-000123"); Matter.setHardwareVersion(7); Matter.setHardwareVersionString("RevA"); Matter.setSetupDiscriminator(0xF01); // 0–0xFFF; test default is 0xF00 Matter.setSetupPasscode(20202024); // valid PIN; test default is 20202021 OnOffLight.begin(lastOnOffState); Matter.begin(); // applies identity, regenerates SPAKE2+ if the PIN changed, prints live codes注意示例在烧录并运行后,必须使用Matter.begin()之后打印的"新生成"配对码进行配网,而不是记忆中的 Arduino 测试码34970112332——因为一旦 PIN 或 discriminator 被修改,旧的测试配对码便不再有效。
支持的目标芯片与配网方式差异
| SoC | Wi-Fi | Thread | BLE Commissioning | LED | 状态 |
|---|---|---|---|---|---|
| ESP32 | ✅ | ❌ | ❌ | 必需 | 完全支持 |
| ESP32-S2 | ✅ | ❌ | ❌ | 必需 | 完全支持 |
| ESP32-S3 | ✅ | ❌ | ✅ | 必需 | 完全支持 |
| ESP32-C3 | ✅ | ❌ | ✅ | 必需 | 完全支持 |
| ESP32-C5 | ❌ | ✅ | ✅ | 必需 | 支持(仅 Thread) |
| ESP32-C6 | ✅ | ❌ | ✅ | 必需 | 完全支持 |
| ESP32-H2 | ❌ | ✅ | ✅ | 必需 | 支持(仅 Thread) |
配网方式上有三点需要特别留意:
- ESP32 与 ESP32-S2 不支持通过蓝牙低功耗(BLE)配网。示例代码中用
#if !CONFIG_ENABLE_CHIPOBLE包裹了 Wi-Fi 连接逻辑(见 MatterDeviceIdentity.ino),这两款芯片必须把 Wi-Fi 凭据直接写在 sketch 中,让设备先连上家庭网络再走 On-Network 配网。 - ESP32-C6的 Arduino Matter 预编译版本仅包含 Wi-Fi 传输。若需要 Thread-only 模式,必须改用 Arduino as an IDF Component 的方式构建项目。
- ESP32-C5的 Arduino Matter 预编译版本则相反,仅包含 Thread。若需要 Wi-Fi 模式,同样需要以 ESP-IDF 组件方式构建并禁用 Thread、仅保留 Wi-Fi Station。
这些能力差异可以在运行时通过Matter.isWiFiStationEnabled()、Matter.isThreadEnabled()、Matter.isBLECommissioningEnabled()等能力查询函数确认,其实现同时检查 SoC 硬件能力宏(如SOC_WIFI_SUPPORTED、SOC_BLE_SUPPORTED)与 Matter 编译配置(如CONFIG_ENABLE_CHIPOBLE),见 Matter.cpp。
调用顺序与 setter 的边界约束
身份 setter 的时序约束由源码层面的两阶段生命周期保证:ArduinoMatter::begin()只有在MatterLifecycle::NodeCreated状态(即至少一个 Endpoint 已begin()创建节点)之后才能启动 CHIP 协议栈,并随后进入StackStarted状态(见 Matter.cpp 与 Matter.cpp)。
ensureSetBeforeBegin()会在栈已启动时打印警告并拒绝写入:
bool ArduinoMatter::ensureSetBeforeBegin(const char *apiName) { if (isStackStarted()) { log_w("Matter.%s() has no effect after Matter.begin(); call it before Matter.begin().", apiName); return false; } return true; }该实现位于 MatterIdentity.cpp。所有字符串 setter 都会把文本拷贝进库内部的静态缓冲区(而非保存指针),因此字面量、栈上临时缓冲区乃至String对象都是安全的,调用返回后参数即可释放。长度限制由 MatterIdentity.h 中的常量定义:
- 厂商名、产品名、设备名(NodeLabel):最长32字符
- 序列号:最长32字符
- 硬件版本字符串:最长64字符
拷贝由copyBounded()完成,空字符串或超长输入会被拒绝并返回false(见 MatterIdentity.h)。数值型 setter 也有校验:setSetupDiscriminator()只接受0x000–0xFFF,setSetupPasscode()必须通过 Matter 规范的IsValidSetupPIN()校验(见 MatterIdentity.cpp)。
另外两条重要约束:
- 不要从 sketch 修改 Vendor ID / Product ID,除非设备的 DAC(设备证明证书)与之匹配;SoftwareVersion 属于编译期 CHIP 配置,同样不应在运行时改动。
- 量产设备应当为每台设备生成唯一的随机 PIN 与 discriminator(通常预置在出厂 NVS 中)。本示例将值存放在 RAM 中,每次启动都会重新应用,只适合演示与原型阶段。
setDeviceName() 与 NodeLabel 的语义
setDeviceName()写入的是 Basic Information 集群的NodeLabel属性。在单端点节点上,控制器(如 Apple Home、Google Home、Alexa 等智能家居应用)通常会把这个名字用作设备的显示标题;而在复合节点(composed node)上,它表示父节点/节点本身的名称,子灯不会被自动重命名。如果需要为开关类端点设置标签,应当使用MatterEndPoint::setTagList()添加 Descriptor 标签,而不是依赖灯的名字。
从源码实现看,NodeLabel 的写入是在栈启动后通过 CHIP 数据模型完成的:applyIdentityAfterStart()中调用writeNodeLabel(),它构造 TLV 编码、以kInternal操作标志写入根端点(Endpoint 0)的 Basic Information 集群 NodeLabel 属性(见 MatterIdentity.cpp)。
配对码的生成机制:PIN 变更会触发 SPAKE2+ 重新计算
Matter.begin()之后,示例通过printIdentity()打印手动配对码(Manual pairing code)与 QR 码 URL:
Serial.printf(" Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); Serial.printf(" QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());这两个 getter 在Matter.begin()之前调用会打印警告并返回空字符串——因为配对码需要以真实的 discriminator/PIN 为输入、在协议栈启动后才能生成(见 MatterIdentity.cpp)。
底层流程(applyIdentityAfterStart()→applyCommissionable(),见 MatterIdentity.cpp):
- 将自定义的
OverrideCommissionableDataProvider绑定到 CHIP 的CommissionableDataProvider并发布; - 若设置了 PIN,则基于现有 SPAKE2+ salt 与迭代次数,用新 PIN 重新生成 verifier(
ensureVerifier(),见 MatterIdentity.h); - 由于
Server::Init在启动时已经快照了出厂 SPAKE2+ verifier,若 PIN 变更,库会关闭当前配网窗口并重新打开一个基础配网窗口(在无 fabric 且 fail-safe 未触发的前提下),使新 PIN 立即生效; - 最后通过
refreshOnboardingCodes()重新生成 QR 码与手动配对码(见 MatterIdentity.cpp),Rendezvous 标志会根据CONFIG_ENABLE_CHIPOBLE自动加入kBLE。
这也是为什么 README 反复强调:修改 PIN 或 discriminator 后必须使用begin()之后打印的新配对码,并擦除 Flash 再重新配网——控制器在配网时会缓存设备名字等身份信息。
配网完成后的三层状态判定:Commissioned / Connected / Online
配对完成之后,示例驱动 LED 的依据是本地 Matter 状态(updateAccessory()),loop()保持按键响应,并每 2.5 秒采样一次Matter.isOnline(),仅在 CASE 会话上下线时打印日志(见 MatterDeviceIdentity.ino)。三个标志的语义差异非常重要:
Matter.isDeviceCommissioned()— 是否存在 fabric(即已加入某个 Matter 网络),实现为GetFabricTable().FabricCount() > 0(见 Matter.cpp);Matter.isDeviceConnected()— Wi-Fi 或 Thread 是否已连通,实现为isWiFiConnected() || isThreadConnected()(见 Matter.cpp);Matter.isOnline()— 是否有控制器与节点建立活跃的 CASE(操作)会话。它通过遍历安全会话管理器、查找活跃的 CASE 会话实现(见 MatterIdentity.cpp),且会保持为true直到会话被空闲逐出(idle-evict)——用户关闭 App 并不会立即让它变回 false。
因此,不要把 LED 的开/关逻辑 gate 在isOnline()上。示例在每次上电后都会从 Preferences(NVS)恢复上一次的开关状态(matterPref.getBool(onOffPrefKey, true)),即使 hub 离线,灯也能在上电后恢复上次状态。如果需要周期性地打印这三个标志,可以参照 MatterStatus 示例,它每 5 秒报告一次 commissioned / connected / radio 状态,每 2.5 秒采样一次isOnline()。
首次配网的典型状态迁移序列为:配对码 →Commissioned=YES→Connected=YES→Online=YES。
硬件连接与按键交互
- LED:优先使用
LED_BUILTIN,未定义时回退到 pin 2(若两者都不可用,编译期会触发#warning "Do not forget to set the LED pin"); - 按键:使用
BOOT_PIN,短按切换灯的开关状态,长按 5 秒调用Matter.decommission()取消配网(实质是esp_matter::factory_reset(),见 Matter.cpp),设备随后重新进入可配网状态。
构建与烧录步骤
- 在 Arduino IDE 中打开
MatterDeviceIdentity.ino(示例源码); - 在Tools > Board中选择你的 ESP32 目标芯片;
- 在Tools > Partition Scheme中选择Huge APP (3 MB No OTA / 1 MB SPIFFS)(对应
PartitionScheme=huge_app,参见 ci.yml); - 在Tools菜单中启用Erase All Flash Before Sketch Upload;
- 上传固件,串口监视器波特率设为115200。
特别注意:在修改身份信息或配对码之后,务必擦除 Flash 再上传。因为控制器在配网时会缓存节点名称等身份信息,残留的旧 fabric/缓存会导致配网或显示异常;无法配网时也可以通过Matter.decommission()或擦除 Flash(Arduino IDE 的 Erase All Flash,或esptool.py --port <PORT> erase_flash)恢复出厂状态。
总结
MatterDeviceIdentity 示例展示了 Arduino ESP32 Matter 库中"先设身份、再启动协议栈"的标准范式:所有身份与配网凭据的 setter 都必须发生在Matter.begin()之前;begin()之后库会基于新的 discriminator/PIN 重新生成 SPAKE2+ verifier 与配对码,因此必须使用运行时打印的新配对码完成配网;配网后通过isDeviceCommissioned()/isDeviceConnected()/isOnline()三层标志区分"已入网 / 网络已通 / 控制器在线",并以本地持久化状态驱动设备行为,从而保证断电重启后即使 hub 离线也能正常工作。这套模式同样适用于该库中的其他 Matter 端点示例(如 MatterStatus),可作为量产设备身份管理的基础参考。
相关资源
- 示例 README:MatterDeviceIdentity/README.md
- 示例源码:MatterDeviceIdentity.ino
- 身份实现:MatterIdentity.cpp / MatterIdentity.h
- 单例与状态查询:Matter.cpp / Matter.h
- 状态监控示例:MatterStatus/README.md
- 标签相关示例:MatterSmartButtonsTagList(Descriptor 标签,而非灯名)
- 许可:Apache License 2.0
【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考