开发实战指南)
ESP32 Arduino Matter 占用传感器端点MatterOccupancySensor开发实战指南【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32本文以 Arduino-ESP32 核心库中的MatterOccupancySensor类为对象系统讲解如何在 ESP32 系列芯片上基于 Matter 协议实现标准占用传感器Occupancy Sensor端点涵盖 API 用法、HoldTime 保持时间配置、传感器类型选择、真实 PIR 传感器接入以及 HomeKit / Alexa / Google Home 的联调方法。读完本文你将能独立编写并配网一个符合 Matter 标准的占用检测设备并掌握其底层实现原理。一、MatterOccupancySensor 概述MatterOccupancySensor是 Arduino-ESP32 Matter 库提供的一个占用传感器端点类见 MatterOccupancySensor.h其作用是在 Matter 网络中提供一个符合 Matter 占用感知标准的端点用于检测并上报有人占用occupied与无人占用unoccupied两种状态典型应用包括 PIR 人体红外传感器、门磁接触式检测等。该类的核心特性占用状态上报支持 occupied / unoccupied 双状态实时上报多种传感器类型PIR被动红外、超声波Ultrasonic、PIR超声波组合、物理接触Physical ContactHoldTime 属性配置传感器在检测到占用后保持occupied状态的时长HoldTimeLimits提供最小值、最大值、默认值三段式校验与控制器参考元数据HoldTime 变更回调Matter 控制器修改 HoldTime 时实时通知应用简单布尔状态支持bool运算符直接读写只读传感器无任何控制功能仅上报状态状态自动更新应用侧改状态后自动同步到 Matter 属性生态集成可接入 Apple HomeKit、Amazon Alexa、Google HomeMatter 标准合规实现基于 Matter Occupancy Sensing Cluster。典型应用场景包括人体移动检测PIR、占用检测、安防系统、智能照明自动化有人开灯、无人关灯、能耗管理无人时自动关闭灯光/空调。从源码结构看MatterOccupancySensor继承自MatterEndPoint基类并在内部封装了 esp-matter 的occupancy_sensor::create()端点创建逻辑与 Occupancy Sensing Cluster 的属性和特性feature flags配置。二、API 参考2.1 构造函数MatterOccupancySensor();创建一个新的 Matter 占用传感器端点对象。该构造函数本身不创建 Matter 端点真正的端点创建发生在begin()中。对象通常在全局作用域声明例如MatterOccupancySensor OccupancySensor;2.2 初始化beginbool begin(bool _occupancyState false, OccupancySensorType_t _occupancySensorType OCCUPANCY_SENSOR_TYPE_PIR);初始化占用传感器端点参数说明_occupancyState初始占用状态true 有人占用false 无人占用默认false_occupancySensorType传感器类型默认OCCUPANCY_SENSOR_TYPE_PIR。返回true表示初始化成功false表示失败。从 MatterOccupancySensor.cpp 的实现可以看到begin()内部会调用ArduinoMatter::_init()完成 Matter 运行时初始化通过occupancy_sensor::create()创建端点并把当前对象指针作为私有数据传入根据传感器类型设置对应的 feature flagspassive_infrared、ultrasonic、physical_contact或other注册自定义的OccupancySensingAttrAccessWrapper属性访问接口用于支持 HoldTime / HoldTimeLimits 属性这两个属性属于 Matter 1.4 新增由 CHIP server 内部管理不会随occupancy_sensor::create()自动添加通过create_hold_time()/create_hold_time_limits()为 Occupancy Sensing 集群补充创建这两个属性。endvoid end();停止处理 Matter 占用传感器事件。实现中将started标志置为false此后setOccupancy()、setHoldTime()等操作会因未启动而返回false。析构函数会自动调用end()。2.3 传感器类型枚举OccupancySensorType_t定义于 MatterOccupancySensor.h枚举值含义对应 feature flagOCCUPANCY_SENSOR_TYPE_PIR被动红外PIR传感器passive_infraredOCCUPANCY_SENSOR_TYPE_ULTRASONIC超声波传感器ultrasonicOCCUPANCY_SENSOR_TYPE_PIR_AND_ULTRASONICPIR 与超声波组合passive_infrared \| ultrasonicOCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT物理接触传感器physical_contact这些枚举值直接映射到 Matter 的OccupancySensorTypeEnum标准枚举。组合类型在内部还会通过occupancySensorTypeBitmap映射表设置occupancy_sensor_type_bitmap属性PIR0x01、Ultrasonic0x02、PhysicalContact0x04 的位图组合。2.4 占用状态控制bool setOccupancy(bool _occupancyState); bool getOccupancy();setOccupancy()设置占用状态true occupiedfalse unoccupied成功返回true。从实现看若状态无变化会直接返回true跳过处理有变化时通过updateAttributeVal()更新 Occupancy 属性并同步内部成员这样 Matter 控制器才能收到状态变更通知getOccupancy()返回当前占用状态true表示有人false表示无人。2.5 HoldTime 控制bool setHoldTime(uint16_t _holdTime_seconds); uint16_t getHoldTime();setHoldTime()设置 HoldTime 值单位秒。HoldTime 决定传感器在最后一次检测后维持occupied状态的时长。重要该函数必须在Matter.begin()之后调用因为它依赖 Matter 事件循环实现中通过chip::DeviceLayer::SystemLayer().ScheduleLambda()将属性更新调度到 Matter 事件循环上下文执行以避免栈锁错误。若holdTimeMax_seconds大于 0即已设置限制新值必须落在 min/max 范围内否则返回falsegetHoldTime()返回当前 HoldTime 值秒。bool setHoldTimeLimits(uint16_t _holdTimeMin_seconds, uint16_t _holdTimeMax_seconds, uint16_t _holdTimeDefault_seconds);设置 HoldTime 限制最小值、最大值、默认值为控制器提供有效范围校验与参考指导。参数说明_holdTimeMin_secondsHoldTime 最小值秒_holdTimeMax_secondsHoldTime 最大值秒_holdTimeDefault_secondsHoldTime 默认/推荐值秒作为控制器的参考元数据。注意事项必须在Matter.begin()之后调用依赖 Matter 事件循环holdTimeDefault_seconds仅是提供给 Matter 控制器的信息性元数据推荐默认值不会自动设置 HoldTime 属性本身——要真正设置值必须调用setHoldTime()若当前 HoldTime 值超出新设置的范围会自动被调整到最近的边界最小值或最大值。这一逻辑在源码中体现为先比较holdTime_seconds与新的 min/max超出时构造adjustedHoldTime并通过SetHoldTimeLimitsAndHoldTimeInEventLoop()一次性在事件循环中同时更新限制与 HoldTime实现中还会做参数自检min max或default不在[min, max]区间内都会返回false。2.6 onHoldTimeChange 回调using HoldTimeChangeCB std::functionbool(uint16_t holdTime_seconds); void onHoldTimeChange(HoldTimeChangeCB onHoldTimeChangeCB);注册一个回调函数当 Matter 控制器修改 HoldTime 值时被调用。回调接收新的 HoldTime 值返回true表示接受变更返回false表示拒绝。示例OccupancySensor.onHoldTimeChange([](uint16_t holdTime_seconds) - bool { Serial.printf(HoldTime changed to %u seconds\n, holdTime_seconds); return true; // 接受变更 });从 MatterOccupancySensor.cpp 实现看HoldTime 的写入拦截是通过自定义OccupancySensingAttrAccessWrapper继承chip::app::AttributeAccessInterface完成的它先按 HoldTimeLimits 做标准校验再调用用户回调回调返回false时返回ConstraintError拒绝写入通过后才调用OccupancySensing::SetHoldTime()并同步内部成员变量。这样既保留了官方 server 的校验逻辑又扩展了用户回调能力。2.7 运算符重载operator bool(); void operator(bool _occupancyState);operator bool()直接返回当前占用状态可用于条件判断if (mySensor) { Serial.println(Room is occupied); } else { Serial.println(Room is unoccupied); }operator设置占用状态等价于setOccupancy()mySensor true; // 设为 occupied mySensor false; // 设为 unoccupied三、基础示例MatterOccupancySensor完整示例见 MatterOccupancySensor.ino演示了如何创建一个 Matter 占用传感器设备并模拟每 2 分钟切换一次占用状态。3.1 端点声明与全局配置#include Arduino.h #include Matter.h #if !CONFIG_ENABLE_CHIPOBLE // 若设备可通过 BLE 配网则无需 WiFi可节省 flash 空间 #include WiFi.h #endif // Matter 占用传感器端点 MatterOccupancySensor OccupancySensor; #if !CONFIG_ENABLE_CHIPOBLE const char *ssid your-ssid; // 修改为你的 WiFi SSID const char *password your-password; // 修改为你的 WiFi 密码 #endif // 使用板载 BOOT 按键做解除配网decommission const uint8_t buttonPin BOOT_PIN; const uint32_t decommissioningTimeout 5000; // 长按 5 秒解除配网CONFIG_ENABLE_CHIPOBLE启用时设备通过 BLE 配网CHIPoBLE不需要手工连接 WiFi未启用时如 ESP32、ESP32-S2 芯片不支持 BLE 配网则必须显式提供 WiFi 凭据。3.2 setup() 初始化流程void setup() { pinMode(buttonPin, INPUT_PULLUP); Serial.begin(115200); #if !CONFIG_ENABLE_CHIPOBLE WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(); #endif // 设置初始占用状态为 false传感器类型为 PIR默认 OccupancySensor.begin(); // Matter 初始化必须是最后一步在所有 EndPoint 初始化完成后调用 Matter.begin(); if (!Matter.isDeviceCommissioned()) { Serial.println(Matter Node is not commissioned yet.); Serial.println(Initiate the device discovery in your Matter environment.); Serial.println(Commission it to your Matter hub with the manual pairing code or QR code); Serial.printf(Manual pairing code: %s\r\n, Matter.getManualPairingCode().c_str()); Serial.printf(QR code URL: %s\r\n, Matter.getOnboardingQRCodeUrl().c_str()); // 等待 Matter 占用传感器完成配网 uint32_t timeCount 0; while (!Matter.isDeviceCommissioned()) { delay(100); if ((timeCount % 50) 0) { // 50*100ms 5 秒 Serial.println(Matter Node not commissioned yet. Waiting for commissioning.); } } Serial.println(Matter Node is commissioned and connected to the network. Ready for use.); } }关键要点OccupancySensor.begin()必须在Matter.begin()之前完成端点注册Matter.begin()是最后一步用于启动整个 Matter 协议栈配网信息手动配对码 二维码 URL在配网前通过串口打印配网期间阻塞等待Matter.isDeviceCommissioned()用于判断设备是否已被 Matter 网络如 HomePod、Nest Hub、Echo接受。3.3 模拟传感器与主循环bool simulatedHWOccupancySensor() { // 模拟占用传感器每 2 分钟切换一次状态 static bool occupancyState false; static uint32_t lastTime millis(); const uint32_t occupancyTimeout 120000; // 2 分钟 if (millis() - lastTime occupancyTimeout) { occupancyState !occupancyState; lastTime millis(); } return occupancyState; } void loop() { // 按钮消抖与解除配网逻辑长按 5 秒 if (digitalRead(buttonPin) LOW !button_state) { button_time_stamp millis(); button_state true; } if (button_state digitalRead(buttonPin) HIGH) { button_state false; } uint32_t time_diff millis() - button_time_stamp; if (button_state time_diff decommissioningTimeout) { Serial.println(Decommissioning Occupancy Sensor Matter Accessory. It shall be commissioned again.); Matter.decommission(); button_time_stamp millis(); } // 读取模拟传感器并同步到 Matter 属性 OccupancySensor.setOccupancy(simulatedHWOccupancySensor()); delay(50); }loop()中通过setOccupancy()将传感器读数持续同步到 Matter 属性Matter 协议栈会自动向控制器推送状态变更。按钮逻辑实现长按 5 秒解除配网的出厂重置功能。3.4 编译与烧录要点在 Arduino IDE 中打开 MatterOccupancySensor.ino选择目标 ESP32 开发板Tools Board分区方案选择Huge APP (3MB No OTA/1MB SPIFFS)开启Erase All Flash Before Sketch Upload串口监视器波特率115200。串口预期输出示例配网信息Manual pairing code: 34970112332 QR code URL: MT:6FCJ142C00KA0648G00... Matter Node not commissioned yet. Waiting for commissioning. ... Matter Node is commissioned and connected to the network. Ready for use.配网完成后占用传感器每 2 分钟自动切换 occupied/unoccupiedMatter 控制器手机 App / 智能家居中枢可实时收到状态更新。四、HoldTime 进阶示例MatterOccupancyWithHoldTime完整示例见 MatterOccupancyWithHoldTime.ino它在前一个示例基础上增加了 HoldTime 完整功能演示重点覆盖在Matter.begin()之后配置 HoldTimeLimits设置并持久化 HoldTime 值通过 Preferences/NVS 跨重启保持使用onHoldTimeChange()回调接收控制器的实时修改在传感器模拟逻辑中实现 HoldTime 过期自动切换为空闲。4.1 HoldTime 常量与 Preferences 持久化const uint16_t HOLD_TIME_MIN 0; // 最小 HoldTime秒 const uint16_t HOLD_TIME_MAX 3600; // 最大 HoldTime秒1 小时 const uint16_t HOLD_TIME_DEFAULT 30; // 默认 HoldTime秒 MatterOccupancySensor OccupancySensor; Preferences matterPref; const char *holdTimePrefKey HoldTime;setup() 中先恢复上次保存的 HoldTimematterPref.begin(MatterPrefs, false); uint16_t storedHoldTime matterPref.getUShort(holdTimePrefKey, HOLD_TIME_DEFAULT); // 校验存储值是否在合法范围内 if (storedHoldTime HOLD_TIME_MIN || storedHoldTime HOLD_TIME_MAX) { uint16_t invalidValue storedHoldTime; storedHoldTime HOLD_TIME_DEFAULT; Serial.printf(Invalid stored HoldTime (%u), using default: %u seconds\n, invalidValue, HOLD_TIME_DEFAULT); } else if (storedHoldTime ! HOLD_TIME_DEFAULT) { Serial.printf(Restored HoldTime from Preferences: %u seconds\n, storedHoldTime); }4.2 注册 HoldTime 变更回调并持久化OccupancySensor.onHoldTimeChange([](uint16_t holdTime_seconds) - bool { Serial.printf(HoldTime changed to %u seconds by Matter Controller\n, holdTime_seconds); // 将新 HoldTime 写入 Preferences实现跨重启持久化 matterPref.putUShort(holdTimePrefKey, holdTime_seconds); // 回调返回 false 可拒绝变更这里始终接受并同步模拟器 return true; });4.3 初始化顺序先 begin后设 Limits 与 HoldTime// 设置初始占用状态为 false传感器类型为 PIR默认 OccupancySensor.begin(); // Matter 初始化必须是最后一步 Matter.begin(); // 在 Matter.begin() 之后设置 HoldTimeLimits可选但推荐用于校验 if (!OccupancySensor.setHoldTimeLimits(HOLD_TIME_MIN, HOLD_TIME_MAX, HOLD_TIME_DEFAULT)) { Serial.println(Warning: Failed to set HoldTimeLimits); } else { Serial.printf(HoldTimeLimits set: Min%u, Max%u, Default%u seconds\n, HOLD_TIME_MIN, HOLD_TIME_MAX, HOLD_TIME_DEFAULT); } // 设置初始 HoldTime优先使用存储值否则用默认值 if (!OccupancySensor.setHoldTime(storedHoldTime)) { Serial.printf(Warning: Failed to set HoldTime to %u seconds\n, storedHoldTime); } else { Serial.printf(HoldTime set to: %u seconds\n, storedHoldTime); } Serial.printf(Initial HoldTime: %u seconds\n, OccupancySensor.getHoldTime());顺序非常关键setHoldTimeLimits()与setHoldTime()都依赖Matter.begin()启动的 Matter 事件循环SystemLayer必须在Matter.begin()之后调用否则会因SystemLayer未初始化而失败。4.4 带 HoldTime 过期的传感器模拟bool simulatedHWOccupancySensor() { static bool occupancyState false; static uint32_t lastDetectionTime 0; static uint32_t lastDetectionEvent millis(); const uint32_t detectionInterval 120000; // 每 2 分钟模拟一次检测 // 获取当前 HoldTime可能已被 Matter 控制器修改转换为毫秒 uint32_t holdTime_ms OccupancySensor.getHoldTime() * 1000; // 先检查 HoldTime 是否过期确保即使同一轮迭代有新检测也能正确过期 if (occupancyState (millis() - lastDetectionTime holdTime_ms)) { occupancyState false; lastDetectionEvent millis(); Serial.println(HoldTime expired. Switching to unoccupied state.); } // 再模拟周期性检测放在过期检查之后使新检测能立即重新触发占用 if (millis() - lastDetectionEvent detectionInterval) { lastDetectionEvent millis(); if (!occupancyState) { // 从无人切换到有人启动保持计时 occupancyState true; lastDetectionTime millis(); Serial.printf(Occupancy detected! Holding state for %u seconds (HoldTime)\n, OccupancySensor.getHoldTime()); } else { // 已处于有人状态新检测重置保持计时模拟持续有人 lastDetectionTime millis(); Serial.printf(Occupancy still detected. Resetting hold timer to %u seconds (HoldTime)\n, OccupancySensor.getHoldTime()); } } return occupancyState; }模拟器对 HoldTime 与检测间隔关系的处理逻辑holdTime detectionInterval状态在 HoldTime 后切回无人等待下一次检测holdTime detectionInterval检测持续到来时计时器不断重置表现为持续占用holdTime detectionInterval检测持续到来时计时器重置持续占用检测停止后从最后一次检测起 HoldTime 到期切换为空闲。主循环与基础示例一致OccupancySensor.setOccupancy(simulatedHWOccupancySensor());持续同步状态。五、接入真实 PIR 传感器两个示例的 READMEMatterOccupancySensor/README.md、MatterOccupancyWithHoldTime/README.md都给出了接入真实 PIR 传感器的完整方案。5.1 硬件接线以 HC-SR501、AM312 等常见 PIR 模块为例典型三引脚VCC、GND、OUTPIR VCC→ ESP32 3.3V 或 5V以传感器规格为准PIR GND→ ESP32 GNDPIR OUT→ ESP32 任意 GPIO如 GPIO 4。5.2 代码改造定义引脚并初始化const uint8_t pirPin 4; // 修改为你的 PIR 引脚 // setup() 中 pinMode(pirPin, INPUT);将模拟函数替换为真实读数含 100ms 消抖避免误触发bool simulatedHWOccupancySensor() { // 带消抖的 PIR 读数HIGH 检测到移动有人LOW 无移动无人 static bool lastState false; static uint32_t lastChangeTime 0; const uint32_t debounceTime 100; // 100ms 消抖 bool currentState digitalRead(pirPin) HIGH; if (currentState ! lastState) { if (millis() - lastChangeTime debounceTime) { lastState currentState; lastChangeTime millis(); Serial.printf(Occupancy state changed: %s\r\n, currentState ? OCCUPIED : UNOCCUPIED); } } return lastState; }配合 HoldTime 示例使用时真实 PIR 的最后一次检测后保持 HoldTime 时长再切回无人的行为逻辑完全一致——simulatedHWOccupancySensor()仅提供状态输入HoldTime 保持/过期逻辑无需改动。PIR 使用小贴士来自示例 README 的排障建议部分 PIR 模块需要 5V 供电上电后预留 30–60 秒让传感器稳定可通过灵敏度与延时电位器调节检测范围对误触发问题优先在软件中加入消抖。六、智能家居生态集成与配网设备烧录并启动后使用 Matter 兼容中枢如 Apple HomePod、Google Nest Hub、Amazon Echo即可配网。Apple Home打开 Home App → → Add Accessory → 扫描串口输出的二维码或选择 I Dont Have a Code or Cannot Scan 手动输入配对码。配网完成后设备以占用传感器Occupancy Sensor形态出现在 Home App 中可基于占用状态创建自动化如有人时开灯。Amazon AlexaAlexa App → More → Add Device → Matter → 扫码或手动输码完成设置随后可查看占用读数并创建 Routine。Google HomeGoogle Home App → → Set up device → New device → Matter device → 扫码或输码之后可查看占用读数并创建自动化。配网失败时可长按 BOOT 键 5 秒解除配网Matter.decommission()或在 Arduino IDE 中开启 Erase All Flash Before Sketch Upload 擦除整片 flash 后重试。支持的目标芯片SoCWi-FiThreadBLE 配网状态ESP32✅❌❌完整支持ESP32-S2✅❌❌完整支持ESP32-S3✅❌✅完整支持ESP32-C3✅❌✅完整支持ESP32-C5❌✅✅支持仅 ThreadESP32-C6✅❌✅完整支持ESP32-H2❌✅✅支持仅 Thread配网注意事项ESP32 与 ESP32-S2 不支持 BLE 配网必须在代码中显式提供 WiFi 凭据ESP32-C6 虽支持 Thread但 Arduino Matter 库预编译版本仅启用 Wi-Fi若要 Thread 专用模式需将 Arduino 作为 ESP-IDF 组件构建并禁用 Matter Wi-Fi station 特性ESP32-C5 预编译版本仅启用 Thread若要 Wi-Fi 模式需以 ESP-IDF 组件方式构建并仅保留 Wi-Fi station 网络。七、底层实现原理HoldTime 是如何工作的从 MatterOccupancySensor.cpp 可以看出 HoldTime 机制的实现细节属性动态创建HoldTime 与 HoldTimeLimits 是 Matter 1.4 引入、由 CHIP server 内部管理MANAGED_INTERNALLY的属性occupancy_sensor::create()不会自动创建它们。begin()中通过 esp-matter 的create_hold_time()/create_hold_time_limits()在 Occupancy Sensing 集群上补充创建且 HoldTimeLimits 仅在 HoldTime 创建成功后才创建。自定义属性访问接口类内定义了OccupancySensingAttrAccessWrapper继承chip::app::AttributeAccessInterface并注册到AttributeAccessInterfaceRegistry。读取操作委托给标准OccupancySensing::Instance写入操作则先解码新值对照GetHoldTimeLimitsForEndpoint()获取的 HoldTimeLimits 做 ConstraintError 校验再调用用户回调回调返回false同样返回ConstraintError最后调用OccupancySensing::SetHoldTime()并同步内部成员。事件循环调度setHoldTime()/setHoldTimeLimits()内部通过SystemLayer().ScheduleLambda()把属性更新调度到 Matter 事件循环线程执行这是因为MatterReportingAttributeChangeCallback()必须在 Matter 事件循环上下文中调用否则会出现栈锁错误——这也是文档强调必须在Matter.begin()之后调用的根本原因。内部状态一致性setHoldTimeLimits()在调度成功后才会提交成员变量holdTimeMin/Max/Default_seconds失败时保持不变若当前 HoldTime 超出新范围会计算adjustedHoldTime并一次性调度设限制 调 HoldTime确保属性与内部状态始终一致。八、常见问题排查设备配网时不可见确认 Wi-Fi 或 Thread 连接配置正确参考上文芯片支持表与注意事项占用读数不更新确认模拟函数被正确调用真实传感器场景检查接线与库初始化状态不变化模拟传感器每 2 分钟120000ms切换一次耐心等待或缩短occupancyTimeout便于测试HoldTime 不生效确认setHoldTimeLimits()与setHoldTime()均在Matter.begin()之后调用并在串口检查报错信息HoldTime 重启后丢失确认 Preferences 已初始化且回调正确执行putUShort()观察串口 HoldTime changed 日志PIR 检测不到移动检查 VCC/GND/OUT 接线、供电电压3.3V/5V、预留 30–60 秒稳定时间、调节灵敏度电位器、确保检测区域无遮挡并可先直接在串口打印 GPIO 值验证配网失败长按按键解除配网后重试或开启 Erase All Flash Before Sketch Upload 擦除 flash无串口输出确认波特率 115200 与 USB 连接。九、进一步阅读Matter 总览文档matter.rstMatter 端点基类说明matter_ep.rst其他传感器端点文档温湿度ep_temperature_sensor.rst、光照ep_light_sensor.rst、接触ep_contact_sensor.rst等均可参考同一套 API 风格类声明MatterOccupancySensor.h类实现MatterOccupancySensor.cpp【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考