Skip to content

Commit 6bf6df2

Browse files
authored
Merge pull request #10734 from SuGlider/matter_identify_callback
feat(matter) adds Identification callback to all matter endpoints
2 parents 6a6edcb + 71e57ea commit 6bf6df2

17 files changed

+288
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
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 the smallest code that will create a Matter Device which can be
17+
* commissioned and controlled from a Matter Environment APP.
18+
* It controls a GPIO that could be attached to a LED for visualization.
19+
* Additionally the ESP32 will send debug messages indicating the Matter activity.
20+
* Turning DEBUG Level ON may be useful to following Matter Accessory and Controller messages.
21+
*
22+
* This example is a simple Matter On/Off Light that can be controlled by a Matter Controller.
23+
* It demonstrates how to use On Identify callback when the Identify Cluster is called.
24+
* The Matter user APP can be used to request the device to identify itself by blinking the LED.
25+
*/
26+
27+
// Matter Manager
28+
#include <Matter.h>
29+
#include <WiFi.h>
30+
31+
// List of Matter Endpoints for this Node
32+
// Single On/Off Light Endpoint - at least one per node
33+
MatterOnOffLight OnOffLight;
34+
35+
// WiFi is manually set and started
36+
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
37+
const char *password = "your-password"; // Change this to your WiFi password
38+
39+
// Light GPIO that can be controlled by Matter APP
40+
#ifdef LED_BUILTIN
41+
const uint8_t ledPin = LED_BUILTIN;
42+
#else
43+
const uint8_t ledPin = 2; // Set your pin here if your board has not defined LED_BUILTIN
44+
#endif
45+
46+
// set your board USER BUTTON pin here - decommissioning button
47+
const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button.
48+
49+
// Button control - decommision the Matter Node
50+
uint32_t button_time_stamp = 0; // debouncing control
51+
bool button_state = false; // false = released | true = pressed
52+
const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission
53+
54+
// Identify Flag and blink time - Blink the LED
55+
const uint8_t identifyLedPin = ledPin; // uses the same LED as the Light - change if needed
56+
volatile bool identifyFlag = false; // Flag to start the Blink when in Identify state
57+
bool identifyBlink = false; // Blink state when in Identify state
58+
59+
// Matter Protocol Endpoint (On/OFF Light) Callback
60+
bool onOffLightCallback(bool state) {
61+
digitalWrite(ledPin, state ? HIGH : LOW);
62+
// This callback must return the success state to Matter core
63+
return true;
64+
}
65+
66+
// Identification shall be done by Blink in Red or just the GPIO when no LED_BUILTIN is not defined
67+
bool onIdentifyLightCallback(bool identifyIsActive) {
68+
Serial.printf("Identify Cluster is %s\r\n", identifyIsActive ? "Active" : "Inactive");
69+
if (identifyIsActive) {
70+
// Start Blinking the light in loop()
71+
identifyFlag = true;
72+
identifyBlink = !OnOffLight; // Start with the inverted light state
73+
} else {
74+
// Stop Blinking and restore the light to the its last state
75+
identifyFlag = false;
76+
// force returning to the original state by toggling the light twice
77+
OnOffLight.toggle();
78+
OnOffLight.toggle();
79+
}
80+
return true;
81+
}
82+
83+
void setup() {
84+
// Initialize the USER BUTTON (Boot button) that will be used to decommission the Matter Node
85+
pinMode(buttonPin, INPUT_PULLUP);
86+
// Initialize the LED GPIO
87+
pinMode(ledPin, OUTPUT);
88+
89+
Serial.begin(115200);
90+
91+
// Manually connect to WiFi
92+
WiFi.begin(ssid, password);
93+
// Wait for connection
94+
while (WiFi.status() != WL_CONNECTED) {
95+
delay(500);
96+
Serial.print(".");
97+
}
98+
Serial.println();
99+
100+
// Initialize at least one Matter EndPoint
101+
OnOffLight.begin();
102+
103+
// On Identify Callback - Blink the LED
104+
OnOffLight.onIdentify(onIdentifyLightCallback);
105+
106+
// Associate a callback to the Matter Controller
107+
OnOffLight.onChange(onOffLightCallback);
108+
109+
// Matter beginning - Last step, after all EndPoints are initialized
110+
Matter.begin();
111+
112+
// Check Matter Accessory Commissioning state, which may change during execution of loop()
113+
if (!Matter.isDeviceCommissioned()) {
114+
Serial.println("");
115+
Serial.println("Matter Node is not commissioned yet.");
116+
Serial.println("Initiate the device discovery in your Matter environment.");
117+
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
118+
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
119+
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
120+
// waits for Matter Occupancy Sensor Commissioning.
121+
uint32_t timeCount = 0;
122+
while (!Matter.isDeviceCommissioned()) {
123+
delay(100);
124+
if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
125+
Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
126+
}
127+
}
128+
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
129+
}
130+
}
131+
132+
void loop() {
133+
// check if the Light is in identify state and blink it every 500ms (delay loop time)
134+
if (identifyFlag) {
135+
#ifdef LED_BUILTIN
136+
uint8_t brightness = 32 * identifyBlink;
137+
rgbLedWrite(identifyLedPin, brightness, 0, 0);
138+
#else
139+
digitalWrite(identifyLedPin, identifyBlink ? HIGH : LOW);
140+
#endif
141+
identifyBlink = !identifyBlink;
142+
}
143+
144+
// Check if the button has been pressed
145+
if (digitalRead(buttonPin) == LOW && !button_state) {
146+
// deals with button debouncing
147+
button_time_stamp = millis(); // record the time while the button is pressed.
148+
button_state = true; // pressed.
149+
}
150+
151+
if (digitalRead(buttonPin) == HIGH && button_state) {
152+
button_state = false; // released
153+
}
154+
155+
// Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node
156+
uint32_t time_diff = millis() - button_time_stamp;
157+
if (button_state && time_diff > decommissioningTimeout) {
158+
Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again.");
159+
Matter.decommission();
160+
button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so
161+
}
162+
163+
delay(500); // works as a debounce for the button and also for the LED blink
164+
}
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/src/Matter.cpp

+21-7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using namespace esp_matter;
2222
using namespace esp_matter::attribute;
2323
using namespace esp_matter::endpoint;
24+
using namespace esp_matter::identification;
2425
using namespace chip::app::Clusters;
2526

2627
constexpr auto k_timeout_seconds = 300;
@@ -29,11 +30,6 @@ static bool _matter_has_started = false;
2930
static node::config_t node_config;
3031
static node_t *deviceNode = NULL;
3132

32-
typedef void *app_driver_handle_t;
33-
esp_err_t matter_light_attribute_update(
34-
app_driver_handle_t driver_handle, uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val
35-
);
36-
3733
// This callback is called for every attribute update. The callback implementation shall
3834
// handle the desired attributes and return an appropriate error code. If the attribute
3935
// is not of your interest, please do not return an error code and strictly return ESP_OK.
@@ -67,8 +63,26 @@ static esp_err_t app_attribute_update_cb(
6763
// This callback is invoked when clients interact with the Identify Cluster.
6864
// In the callback implementation, an endpoint can identify itself. (e.g., by flashing an LED or light).
6965
static esp_err_t app_identification_cb(identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id, uint8_t effect_variant, void *priv_data) {
70-
log_i("Identification callback: type: %u, effect: %u, variant: %u", type, effect_id, effect_variant);
71-
return ESP_OK;
66+
log_d("Identification callback to endpoint %d: type: %u, effect: %u, variant: %u", endpoint_id, type, effect_id, effect_variant);
67+
esp_err_t err = ESP_OK;
68+
MatterEndPoint *ep = (MatterEndPoint *)priv_data; // endpoint pointer to base class
69+
// Identify the endpoint sending a counter to the application
70+
bool identifyIsActive = false;
71+
72+
if (type == identification::callback_type_t::START) {
73+
log_v("Identification callback: START");
74+
identifyIsActive = true;
75+
} else if (type == identification::callback_type_t::EFFECT) {
76+
log_v("Identification callback: EFFECT");
77+
} else if (type == identification::callback_type_t::STOP) {
78+
identifyIsActive = false;
79+
log_v("Identification callback: STOP");
80+
}
81+
if (ep != NULL) {
82+
err = ep->endpointIdentifyCB(endpoint_id, identifyIsActive) ? ESP_OK : ESP_FAIL;
83+
}
84+
85+
return err;
7286
}
7387

7488
// This callback is invoked for all Matter events. The application can handle the events as required.

libraries/Matter/src/MatterEndPoint.h

+14
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,21 @@ class MatterEndPoint {
102102
// this function is called by Matter internal event processor. It could be overwritten by the application, if necessary.
103103
virtual bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) = 0;
104104

105+
// This callback is invoked when clients interact with the Identify Cluster of an specific endpoint.
106+
bool endpointIdentifyCB(uint16_t endpoint_id, bool identifyIsEnabled) {
107+
if (_onEndPointIdentifyCB) {
108+
return _onEndPointIdentifyCB(identifyIsEnabled);
109+
}
110+
return true;
111+
}
112+
// User callaback for the Identify Cluster functionality
113+
using EndPointIdentifyCB = std::function<bool(bool)>;
114+
void onIdentify(EndPointIdentifyCB onEndPointIdentifyCB) {
115+
_onEndPointIdentifyCB = onEndPointIdentifyCB;
116+
}
117+
105118
protected:
106119
uint16_t endpoint_id = 0;
120+
EndPointIdentifyCB _onEndPointIdentifyCB = NULL;
107121
};
108122
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */

libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,13 @@ MatterColorLight::~MatterColorLight() {
162162

163163
bool MatterColorLight::begin(bool initialState, espHsvColor_t _colorHSV) {
164164
ArduinoMatter::_init();
165-
rgb_color_light::config_t light_config;
166165

166+
if (getEndPointId() != 0) {
167+
log_e("Matter RGB Color Light with Endpoint Id %d device has already been created.", getEndPointId());
168+
return false;
169+
}
170+
171+
rgb_color_light::config_t light_config;
167172
light_config.on_off.on_off = initialState;
168173
light_config.on_off.lighting.start_up_on_off = nullptr;
169174
onOffState = initialState;

libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp

+16-11
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@ using namespace chip::app::Clusters;
2626
bool MatterColorTemperatureLight::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) {
2727
bool ret = true;
2828
if (!started) {
29-
log_e("Matter CW_WW Light device has not begun.");
29+
log_e("Matter Temperature Light device has not begun.");
3030
return false;
3131
}
3232

33-
log_d("CW_WW Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32);
33+
log_d("Temperature Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32);
3434

3535
if (endpoint_id == getEndPointId()) {
3636
switch (cluster_id) {
3737
case OnOff::Id:
3838
if (attribute_id == OnOff::Attributes::OnOff::Id) {
39-
log_d("CW_WW Light On/Off State changed to %d", val->val.b);
39+
log_d("Temperature Light On/Off State changed to %d", val->val.b);
4040
if (_onChangeOnOffCB != NULL) {
4141
ret &= _onChangeOnOffCB(val->val.b);
4242
}
@@ -50,7 +50,7 @@ bool MatterColorTemperatureLight::attributeChangeCB(uint16_t endpoint_id, uint32
5050
break;
5151
case LevelControl::Id:
5252
if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) {
53-
log_d("CW_WW Light Brightness changed to %d", val->val.u8);
53+
log_d("Temperature Light Brightness changed to %d", val->val.u8);
5454
if (_onChangeBrightnessCB != NULL) {
5555
ret &= _onChangeBrightnessCB(val->val.u8);
5656
}
@@ -64,7 +64,7 @@ bool MatterColorTemperatureLight::attributeChangeCB(uint16_t endpoint_id, uint32
6464
break;
6565
case ColorControl::Id:
6666
if (attribute_id == ColorControl::Attributes::ColorTemperatureMireds::Id) {
67-
log_d("CW_WW Light Temperature changed to %d", val->val.u16);
67+
log_d("Temperature Light Temperature changed to %d", val->val.u16);
6868
if (_onChangeTemperatureCB != NULL) {
6969
ret &= _onChangeTemperatureCB(val->val.u16);
7070
}
@@ -89,8 +89,13 @@ MatterColorTemperatureLight::~MatterColorTemperatureLight() {
8989

9090
bool MatterColorTemperatureLight::begin(bool initialState, uint8_t brightness, uint16_t ColorTemperature) {
9191
ArduinoMatter::_init();
92-
color_temperature_light::config_t light_config;
9392

93+
if (getEndPointId() != 0) {
94+
log_e("Matter Temperature Light with Endpoint Id %d device has already been created.", getEndPointId());
95+
return false;
96+
}
97+
98+
color_temperature_light::config_t light_config;
9499
light_config.on_off.on_off = initialState;
95100
light_config.on_off.lighting.start_up_on_off = nullptr;
96101
onOffState = initialState;
@@ -108,12 +113,12 @@ bool MatterColorTemperatureLight::begin(bool initialState, uint8_t brightness, u
108113
// endpoint handles can be used to add/modify clusters.
109114
endpoint_t *endpoint = color_temperature_light::create(node::get(), &light_config, ENDPOINT_FLAG_NONE, (void *)this);
110115
if (endpoint == nullptr) {
111-
log_e("Failed to create CW_WW light endpoint");
116+
log_e("Failed to create Temperature Light endpoint");
112117
return false;
113118
}
114119

115120
setEndPointId(endpoint::get_id(endpoint));
116-
log_i("CW_WW Light created with endpoint_id %d", getEndPointId());
121+
log_i("Temperature Light created with endpoint_id %d", getEndPointId());
117122

118123
/* Mark deferred persistence for some attributes that might be changed rapidly */
119124
cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id);
@@ -134,7 +139,7 @@ void MatterColorTemperatureLight::end() {
134139

135140
bool MatterColorTemperatureLight::setOnOff(bool newState) {
136141
if (!started) {
137-
log_e("Matter CW_WW Light device has not begun.");
142+
log_e("Matter Temperature Light device has not begun.");
138143
return false;
139144
}
140145

@@ -175,7 +180,7 @@ bool MatterColorTemperatureLight::toggle() {
175180

176181
bool MatterColorTemperatureLight::setBrightness(uint8_t newBrightness) {
177182
if (!started) {
178-
log_w("Matter CW_WW Light device has not begun.");
183+
log_w("Matter Temperature Light device has not begun.");
179184
return false;
180185
}
181186

@@ -206,7 +211,7 @@ uint8_t MatterColorTemperatureLight::getBrightness() {
206211

207212
bool MatterColorTemperatureLight::setColorTemperature(uint16_t newTemperature) {
208213
if (!started) {
209-
log_w("Matter CW_WW Light device has not begun.");
214+
log_w("Matter Temperature Light device has not begun.");
210215
return false;
211216
}
212217

libraries/Matter/src/MatterEndpoints/MatterContactSensor.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ MatterContactSensor::~MatterContactSensor() {
4343
bool MatterContactSensor::begin(bool _contactState) {
4444
ArduinoMatter::_init();
4545

46+
if (getEndPointId() != 0) {
47+
log_e("Matter Contact Sensor with Endpoint Id %d device has already been created.", getEndPointId());
48+
return false;
49+
}
50+
4651
contact_sensor::config_t contact_sensor_config;
4752
contact_sensor_config.boolean_state.state_value = _contactState;
4853

libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,12 @@ MatterDimmableLight::~MatterDimmableLight() {
7575

7676
bool MatterDimmableLight::begin(bool initialState, uint8_t brightness) {
7777
ArduinoMatter::_init();
78-
dimmable_light::config_t light_config;
78+
if (getEndPointId() != 0) {
79+
log_e("Matter Dimmable Light with Endpoint Id %d device has already been created.", getEndPointId());
80+
return false;
81+
}
7982

83+
dimmable_light::config_t light_config;
8084
light_config.on_off.on_off = initialState;
8185
light_config.on_off.lighting.start_up_on_off = nullptr;
8286
onOffState = initialState;

libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,13 @@ MatterEnhancedColorLight::~MatterEnhancedColorLight() {
178178

179179
bool MatterEnhancedColorLight::begin(bool initialState, espHsvColor_t _colorHSV, uint8_t brightness, uint16_t ColorTemperature) {
180180
ArduinoMatter::_init();
181-
enhanced_color_light::config_t light_config;
182181

182+
if (getEndPointId() != 0) {
183+
log_e("Matter Enhanced ColorLight with Endpoint Id %d device has already been created.", getEndPointId());
184+
return false;
185+
}
186+
187+
enhanced_color_light::config_t light_config;
183188
light_config.on_off.on_off = initialState;
184189
light_config.on_off.lighting.start_up_on_off = nullptr;
185190
onOffState = initialState;

0 commit comments

Comments
 (0)