Skip to content

Commit e17900b

Browse files
committed
feat(matter): adds matter occupancy sensor endpoint
1 parent 59d51de commit e17900b

File tree

7 files changed

+322
-0
lines changed

7 files changed

+322
-0
lines changed

Diff for: 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,129 @@
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+
// The button will also be used to manually toggle the Occupancy Sensor state
52+
pinMode(buttonPin, INPUT_PULLUP);
53+
54+
Serial.begin(115200);
55+
56+
// Manually connect to WiFi
57+
WiFi.begin(ssid, password);
58+
// Wait for connection
59+
while (WiFi.status() != WL_CONNECTED) {
60+
delay(500);
61+
Serial.print(".");
62+
}
63+
Serial.println();
64+
65+
// set initial occupancy sensor state as false and connected to a PIR sensor type (default)
66+
OccupancySensor.begin();
67+
68+
// Matter beginning - Last step, after all EndPoints are initialized
69+
Matter.begin();
70+
71+
// Check Matter Accessory Commissioning state, which may change during execution of loop()
72+
if (!Matter.isDeviceCommissioned()) {
73+
Serial.println("");
74+
Serial.println("Matter Node is not commissioned yet.");
75+
Serial.println("Initiate the device discovery in your Matter environment.");
76+
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
77+
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
78+
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
79+
// waits for Matter Occupancy Sensor Commissioning.
80+
uint32_t timeCount = 0;
81+
while (!Matter.isDeviceCommissioned()) {
82+
delay(100);
83+
if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
84+
Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
85+
}
86+
}
87+
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
88+
}
89+
}
90+
91+
bool simulatedHWOccupancySensor() {
92+
// Simulated Occupancy Sensor
93+
static bool occupancyState = false;
94+
static uint32_t lastTime = millis();
95+
const uint32_t occupancyTimeout = 120000; // 2 minutes to toggle the state
96+
97+
// Simulate a Occupancy Sensor state change every 2 minutes
98+
if (millis() - lastTime > occupancyTimeout) {
99+
occupancyState = !occupancyState;
100+
lastTime = millis();
101+
}
102+
return occupancyState;
103+
}
104+
105+
void loop() {
106+
// Check if the button has been pressed
107+
if (digitalRead(buttonPin) == LOW && !button_state) {
108+
// deals with button debouncing
109+
button_time_stamp = millis(); // record the time while the button is pressed.
110+
button_state = true; // pressed.
111+
}
112+
113+
if (button_state && digitalRead(buttonPin) == HIGH) {
114+
button_state = false; // released
115+
}
116+
117+
// Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node
118+
uint32_t time_diff = millis() - button_time_stamp;
119+
if (button_state && time_diff > decommissioningTimeout) {
120+
Serial.println("Decommissioning the Generic Switch Matter Accessory. It shall be commissioned again.");
121+
Matter.decommission();
122+
button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so
123+
}
124+
125+
// Check Simulated Occupancy Sensor and set Matter Attribute
126+
OccupancySensor.setOccupancy(simulatedHWOccupancySensor());
127+
128+
delay(50);
129+
}
+7
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+
}

Diff for: 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)

Diff for: 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,107 @@
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+
const uint8_t MatterOccupancySensor::occupancySensorTypeBitmap[4] = {
27+
MatterOccupancySensor::occupancySensorTypePir,
28+
MatterOccupancySensor::occupancySensorTypePir | MatterOccupancySensor::occupancySensorTypeUltrasonic,
29+
MatterOccupancySensor::occupancySensorTypeUltrasonic,
30+
MatterOccupancySensor::occupancySensorTypePhysicalContact
31+
};
32+
33+
bool MatterOccupancySensor::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) {
34+
bool ret = true;
35+
if (!started) {
36+
log_e("Matter Occupancy Sensor device has not begun.");
37+
return false;
38+
}
39+
40+
log_d("Occupancy Sensor Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32);
41+
return ret;
42+
}
43+
44+
MatterOccupancySensor::MatterOccupancySensor() {}
45+
46+
MatterOccupancySensor::~MatterOccupancySensor() {
47+
end();
48+
}
49+
50+
bool MatterOccupancySensor::begin(bool _occupancyState, OccupancySensorType_t _occupancySensorType) {
51+
ArduinoMatter::_init();
52+
53+
occupancy_sensor::config_t occupancy_sensor_config;
54+
occupancy_sensor_config.occupancy_sensing.occupancy = _occupancyState;
55+
occupancy_sensor_config.occupancy_sensing.occupancy_sensor_type = _occupancySensorType;
56+
occupancy_sensor_config.occupancy_sensing.occupancy_sensor_type_bitmap = occupancySensorTypeBitmap[_occupancySensorType];
57+
58+
// endpoint handles can be used to add/modify clusters.
59+
endpoint_t *endpoint = occupancy_sensor::create(node::get(), &occupancy_sensor_config, ENDPOINT_FLAG_NONE, (void *)this);
60+
if (endpoint == nullptr) {
61+
log_e("Failed to create Occupancy Sensor endpoint");
62+
return false;
63+
}
64+
occupancyState = _occupancyState;
65+
setEndPointId(endpoint::get_id(endpoint));
66+
log_i("Occupancy Sensor created with endpoint_id %d", getEndPointId());
67+
started = true;
68+
return true;
69+
}
70+
71+
void MatterOccupancySensor::end() {
72+
started = false;
73+
}
74+
75+
bool MatterOccupancySensor::setOccupancy(bool _occupancyState) {
76+
if (!started) {
77+
log_e("Matter Occupancy Sensor device has not begun.");
78+
return false;
79+
}
80+
81+
// avoid processing the a "no-change"
82+
if (occupancyState == _occupancyState) {
83+
return true;
84+
}
85+
86+
esp_matter_attr_val_t occupancyVal = esp_matter_invalid(NULL);
87+
88+
if (!getAttributeVal(OccupancySensing::Id, OccupancySensing::Attributes::Occupancy::Id, &occupancyVal)) {
89+
log_e("Failed to get Occupancy Sensor Attribute.");
90+
return false;
91+
}
92+
if (occupancyVal.val.u8 != _occupancyState) {
93+
occupancyVal.val.u8 = _occupancyState;
94+
bool ret;
95+
ret = updateAttributeVal(OccupancySensing::Id, OccupancySensing::Attributes::Occupancy::Id, &occupancyVal);
96+
if (!ret) {
97+
log_e("Failed to update Occupancy Sensor Attribute.");
98+
return false;
99+
}
100+
occupancyState = _occupancyState;
101+
}
102+
log_v("Occupancy Sensor set to %s", _occupancyState ? "Occupied" : "Vacant");
103+
104+
return true;
105+
}
106+
107+
#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)