Skip to content

Commit e060b02

Browse files
authored
feat(matter): adds new Matter Occupancy Sensor endpoint (#10717)
* feat(matter): adds matter occupancy sensor endpoint
1 parent a4f96e9 commit e060b02

File tree

7 files changed

+323
-0
lines changed

7 files changed

+323
-0
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ set(ARDUINO_LIBRARY_Matter_SRCS
179179
libraries/Matter/src/MatterEndpoints/MatterHumiditySensor.cpp
180180
libraries/Matter/src/MatterEndpoints/MatterContactSensor.cpp
181181
libraries/Matter/src/MatterEndpoints/MatterPressureSensor.cpp
182+
libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp
182183
libraries/Matter/src/Matter.cpp)
183184

184185
set(ARDUINO_LIBRARY_PPP_SRCS
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/*
16+
* This example is an example code that will create a Matter Device which can be
17+
* commissioned and controlled from a Matter Environment APP.
18+
* Additionally the ESP32 will send debug messages indicating the Matter activity.
19+
* Turning DEBUG Level ON may be useful to following Matter Accessory and Controller messages.
20+
*
21+
* The example will create a Matter Occupancy Sensor Device.
22+
* The Occupancy Sensor will be simulated to change its state every 2 minutes.
23+
*
24+
* The onboard button can be kept pressed for 5 seconds to decommission the Matter Node.
25+
* The example will also show the manual commissioning code and QR code to be used in the Matter environment.
26+
*
27+
*/
28+
29+
// Matter Manager
30+
#include <Matter.h>
31+
#include <WiFi.h>
32+
33+
// List of Matter Endpoints for this Node
34+
// Matter Occupancy Sensor Endpoint
35+
MatterOccupancySensor OccupancySensor;
36+
37+
// set your board USER BUTTON pin here - decommissioning only
38+
const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button.
39+
40+
// WiFi is manually set and started
41+
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
42+
const char *password = "your-password"; // Change this to your WiFi password
43+
44+
// Button control
45+
uint32_t button_time_stamp = 0; // debouncing control
46+
bool button_state = false; // false = released | true = pressed
47+
const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission
48+
49+
void setup() {
50+
// Initialize the USER BUTTON (Boot button) that will be used to decommission the Matter Node
51+
pinMode(buttonPin, INPUT_PULLUP);
52+
53+
Serial.begin(115200);
54+
55+
// Manually connect to WiFi
56+
WiFi.begin(ssid, password);
57+
// Wait for connection
58+
while (WiFi.status() != WL_CONNECTED) {
59+
delay(500);
60+
Serial.print(".");
61+
}
62+
Serial.println();
63+
64+
// set initial occupancy sensor state as false and connected to a PIR sensor type (default)
65+
OccupancySensor.begin();
66+
67+
// Matter beginning - Last step, after all EndPoints are initialized
68+
Matter.begin();
69+
70+
// Check Matter Accessory Commissioning state, which may change during execution of loop()
71+
if (!Matter.isDeviceCommissioned()) {
72+
Serial.println("");
73+
Serial.println("Matter Node is not commissioned yet.");
74+
Serial.println("Initiate the device discovery in your Matter environment.");
75+
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
76+
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
77+
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
78+
// waits for Matter Occupancy Sensor Commissioning.
79+
uint32_t timeCount = 0;
80+
while (!Matter.isDeviceCommissioned()) {
81+
delay(100);
82+
if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
83+
Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
84+
}
85+
}
86+
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
87+
}
88+
}
89+
90+
bool simulatedHWOccupancySensor() {
91+
// Simulated Occupancy Sensor
92+
static bool occupancyState = false;
93+
static uint32_t lastTime = millis();
94+
const uint32_t occupancyTimeout = 120000; // 2 minutes to toggle the state
95+
96+
// Simulate a Occupancy Sensor state change every 2 minutes
97+
if (millis() - lastTime > occupancyTimeout) {
98+
occupancyState = !occupancyState;
99+
lastTime = millis();
100+
}
101+
return occupancyState;
102+
}
103+
104+
void loop() {
105+
// Check if the button has been pressed
106+
if (digitalRead(buttonPin) == LOW && !button_state) {
107+
// deals with button debouncing
108+
button_time_stamp = millis(); // record the time while the button is pressed.
109+
button_state = true; // pressed.
110+
}
111+
112+
if (button_state && digitalRead(buttonPin) == HIGH) {
113+
button_state = false; // released
114+
}
115+
116+
// Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node
117+
uint32_t time_diff = millis() - button_time_stamp;
118+
if (button_state && time_diff > decommissioningTimeout) {
119+
Serial.println("Decommissioning the Generic Switch Matter Accessory. It shall be commissioned again.");
120+
Matter.decommission();
121+
button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so
122+
}
123+
124+
// Check Simulated Occupancy Sensor and set Matter Attribute
125+
OccupancySensor.setOccupancy(simulatedHWOccupancySensor());
126+
127+
delay(50);
128+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"fqbn_append": "PartitionScheme=huge_app",
3+
"requires": [
4+
"CONFIG_SOC_WIFI_SUPPORTED=y",
5+
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
6+
]
7+
}

libraries/Matter/keywords.txt

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ MatterTemperatureSensor KEYWORD1
2222
MatterHumiditySensor KEYWORD1
2323
MatterContactSensor KEYWORD1
2424
MatterPressureSensor KEYWORD1
25+
MatterOccupancySensor KEYWORD1
2526

2627
#######################################
2728
# Methods and Functions (KEYWORD2)
@@ -74,6 +75,8 @@ setContact KEYWORD2
7475
getContact KEYWORD2
7576
setPressure KEYWORD2
7677
getPressure KEYWORD2
78+
setOccupancy KEYWORD2
79+
getOccupancy KEYWORD2
7780

7881
#######################################
7982
# Constants (LITERAL1)

libraries/Matter/src/Matter.h

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <MatterEndpoints/MatterHumiditySensor.h>
3131
#include <MatterEndpoints/MatterContactSensor.h>
3232
#include <MatterEndpoints/MatterPressureSensor.h>
33+
#include <MatterEndpoints/MatterOccupancySensor.h>
3334

3435
using namespace esp_matter;
3536

@@ -66,6 +67,7 @@ class ArduinoMatter {
6667
friend class MatterHumiditySensor;
6768
friend class MatterContactSensor;
6869
friend class MatterPressureSensor;
70+
friend class MatterOccupancySensor;
6971

7072
protected:
7173
static void _init();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <sdkconfig.h>
16+
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
17+
18+
#include <Matter.h>
19+
#include <app/server/Server.h>
20+
#include <MatterEndpoints/MatterOccupancySensor.h>
21+
22+
using namespace esp_matter;
23+
using namespace esp_matter::endpoint;
24+
using namespace chip::app::Clusters;
25+
26+
// clang-format off
27+
const uint8_t MatterOccupancySensor::occupancySensorTypeBitmap[4] = {
28+
MatterOccupancySensor::occupancySensorTypePir,
29+
MatterOccupancySensor::occupancySensorTypePir | MatterOccupancySensor::occupancySensorTypeUltrasonic,
30+
MatterOccupancySensor::occupancySensorTypeUltrasonic,
31+
MatterOccupancySensor::occupancySensorTypePhysicalContact
32+
};
33+
// clang-format on
34+
35+
bool MatterOccupancySensor::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) {
36+
bool ret = true;
37+
if (!started) {
38+
log_e("Matter Occupancy Sensor device has not begun.");
39+
return false;
40+
}
41+
42+
log_d("Occupancy Sensor Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32);
43+
return ret;
44+
}
45+
46+
MatterOccupancySensor::MatterOccupancySensor() {}
47+
48+
MatterOccupancySensor::~MatterOccupancySensor() {
49+
end();
50+
}
51+
52+
bool MatterOccupancySensor::begin(bool _occupancyState, OccupancySensorType_t _occupancySensorType) {
53+
ArduinoMatter::_init();
54+
55+
occupancy_sensor::config_t occupancy_sensor_config;
56+
occupancy_sensor_config.occupancy_sensing.occupancy = _occupancyState;
57+
occupancy_sensor_config.occupancy_sensing.occupancy_sensor_type = _occupancySensorType;
58+
occupancy_sensor_config.occupancy_sensing.occupancy_sensor_type_bitmap = occupancySensorTypeBitmap[_occupancySensorType];
59+
60+
// endpoint handles can be used to add/modify clusters.
61+
endpoint_t *endpoint = occupancy_sensor::create(node::get(), &occupancy_sensor_config, ENDPOINT_FLAG_NONE, (void *)this);
62+
if (endpoint == nullptr) {
63+
log_e("Failed to create Occupancy Sensor endpoint");
64+
return false;
65+
}
66+
occupancyState = _occupancyState;
67+
setEndPointId(endpoint::get_id(endpoint));
68+
log_i("Occupancy Sensor created with endpoint_id %d", getEndPointId());
69+
started = true;
70+
return true;
71+
}
72+
73+
void MatterOccupancySensor::end() {
74+
started = false;
75+
}
76+
77+
bool MatterOccupancySensor::setOccupancy(bool _occupancyState) {
78+
if (!started) {
79+
log_e("Matter Occupancy Sensor device has not begun.");
80+
return false;
81+
}
82+
83+
// avoid processing the a "no-change"
84+
if (occupancyState == _occupancyState) {
85+
return true;
86+
}
87+
88+
esp_matter_attr_val_t occupancyVal = esp_matter_invalid(NULL);
89+
90+
if (!getAttributeVal(OccupancySensing::Id, OccupancySensing::Attributes::Occupancy::Id, &occupancyVal)) {
91+
log_e("Failed to get Occupancy Sensor Attribute.");
92+
return false;
93+
}
94+
if (occupancyVal.val.u8 != _occupancyState) {
95+
occupancyVal.val.u8 = _occupancyState;
96+
bool ret;
97+
ret = updateAttributeVal(OccupancySensing::Id, OccupancySensing::Attributes::Occupancy::Id, &occupancyVal);
98+
if (!ret) {
99+
log_e("Failed to update Occupancy Sensor Attribute.");
100+
return false;
101+
}
102+
occupancyState = _occupancyState;
103+
}
104+
log_v("Occupancy Sensor set to %s", _occupancyState ? "Occupied" : "Vacant");
105+
106+
return true;
107+
}
108+
109+
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
#include <sdkconfig.h>
17+
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
18+
19+
#include <Matter.h>
20+
#include <MatterEndPoint.h>
21+
#include <app-common/zap-generated/cluster-objects.h>
22+
23+
using namespace chip::app::Clusters::OccupancySensing;
24+
25+
class MatterOccupancySensor : public MatterEndPoint {
26+
public:
27+
// Different Occupancy Sensor Types
28+
enum OccupancySensorType_t {
29+
OCCUPANCY_SENSOR_TYPE_PIR = (uint8_t)OccupancySensorTypeEnum::kPir,
30+
OCCUPANCY_SENSOR_TYPE_ULTRASONIC = (uint8_t)OccupancySensorTypeEnum::kUltrasonic,
31+
OCCUPANCY_SENSOR_TYPE_PIR_AND_ULTRASONIC = (uint8_t)OccupancySensorTypeEnum::kPIRAndUltrasonic,
32+
OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT = (uint8_t)OccupancySensorTypeEnum::kPhysicalContact
33+
};
34+
35+
MatterOccupancySensor();
36+
~MatterOccupancySensor();
37+
// begin Matter Occupancy Sensor endpoint with initial occupancy state and default PIR sensor type
38+
bool begin(bool _occupancyState = false, OccupancySensorType_t _occupancySensorType = OCCUPANCY_SENSOR_TYPE_PIR);
39+
// this will just stop processing Occupancy Sensor Matter events
40+
void end();
41+
42+
// set the occupancy state
43+
bool setOccupancy(bool _occupancyState);
44+
// returns the occupancy state
45+
bool getOccupancy() {
46+
return occupancyState;
47+
}
48+
49+
// bool conversion operator
50+
void operator=(bool _occupancyState) {
51+
setOccupancy(_occupancyState);
52+
}
53+
// bool conversion operator
54+
operator bool() {
55+
return getOccupancy();
56+
}
57+
58+
// this function is called by Matter internal event processor. It could be overwritten by the application, if necessary.
59+
bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val);
60+
61+
protected:
62+
// bitmap for Occupancy Sensor Types
63+
static const uint8_t occupancySensorTypePir = 0x01;
64+
static const uint8_t occupancySensorTypeUltrasonic = 0x02;
65+
static const uint8_t occupancySensorTypePhysicalContact = 0x04;
66+
67+
// bitmap for Occupancy Sensor Type Bitmap mapped array
68+
static const uint8_t occupancySensorTypeBitmap[4];
69+
70+
bool started = false;
71+
bool occupancyState = false;
72+
};
73+
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */

0 commit comments

Comments
 (0)