Skip to content

Commit 8c44945

Browse files
committed
feat(matter): new matter endpoint dimmable light
1 parent 65078c9 commit 8c44945

File tree

5 files changed

+376
-3
lines changed

5 files changed

+376
-3
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// Matter Manager
2+
#include <Matter.h>
3+
#include <WiFi.h>
4+
#include <Preferences.h>
5+
6+
// List of Matter Endpoints for this Node
7+
// Dimmable Light Endpoint
8+
#include <MatterEndpoints/MatterDimmableLight.h>
9+
MatterDimmableLight DimmableLight;
10+
11+
// it will keep last OnOff & Brightness state stored, using Preferences
12+
Preferences lastStatePref;
13+
14+
// set your board RGB LED pin here
15+
#ifdef RGB_BUILTIN
16+
const uint8_t ledPin = RGB_BUILTIN;
17+
#else
18+
const uint8_t ledPin = 2; // Set your pin here if your board has not defined LED_BUILTIN
19+
#warning "Do not forget to set the RGB LED pin"
20+
#endif
21+
22+
// set your board USER BUTTON pin here
23+
const uint8_t buttonPin = 0; // Set your pin here. Using BOOT Button. C6/C3 use GPIO9.
24+
25+
// WiFi is manually set and started
26+
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
27+
const char *password = "your-password"; // Change this to your WiFi password
28+
29+
// Set the RGB LED Light based on the current state of the Dimmable Light
30+
void setRGBLight(bool state, uint8_t brightness) {
31+
Serial.printf("Setting Light to State: %s and Brightness: %d\r\n", DimmableLight ? "ON" : "OFF", brightness);
32+
if (state) {
33+
rgbLedWrite(ledPin, brightness, brightness, brightness);
34+
} else {
35+
digitalWrite(ledPin, LOW);
36+
}
37+
}
38+
39+
// Matter Protocol Endpoint On-Off Change Callback
40+
bool setLightOnOff(bool state) {
41+
Serial.printf("User Callback :: New Light State = %s\r\n", state ? "ON" : "OFF");
42+
setRGBLight(state, DimmableLight.getBrightness());
43+
// store last OnOff state for when the Light is restarted / power goes off
44+
lastStatePref.putBool("lastOnOffState", state);
45+
// This callback must return the success state to Matter core
46+
return true;
47+
}
48+
49+
// Matter Protocol Endpoint Brightness Change Callback
50+
bool setLightBrightness(uint8_t brightness) {
51+
Serial.printf("User Callback :: New Light Brigthness = %.2f%% [Val = %d]\r\n", ((float) brightness * 100) / MatterDimmableLight::MAX_BRIGHTNESS, brightness);
52+
setRGBLight(DimmableLight.getOnOff(), brightness);
53+
// store last Brightness for when the Light is restarted / power goes off
54+
lastStatePref.putUChar("lastBrightness", brightness);
55+
// This callback must return the success state to Matter core
56+
return true;
57+
}
58+
59+
void setup() {
60+
// Initialize the USER BUTTON (Boot button) GPIO that will act as a toggle switch
61+
pinMode(buttonPin, INPUT_PULLUP);
62+
// Initialize the LED (light) GPIO and Matter End Point
63+
pinMode(ledPin, OUTPUT);
64+
65+
Serial.begin(115200);
66+
while (!Serial) {
67+
delay(100);
68+
}
69+
70+
// We start by connecting to a WiFi network
71+
Serial.print("Connecting to ");
72+
Serial.println(ssid);
73+
// enable IPv6
74+
WiFi.enableIPv6(true);
75+
// Manually connect to WiFi
76+
WiFi.begin(ssid, password);
77+
// Wait for connection
78+
while (WiFi.status() != WL_CONNECTED) {
79+
delay(500);
80+
Serial.print(".");
81+
}
82+
Serial.println("\r\nWiFi connected");
83+
Serial.println("IP address: ");
84+
Serial.println(WiFi.localIP());
85+
delay(500);
86+
87+
// Initialize Matter EndPoint
88+
lastStatePref.begin("matterLight", false);
89+
bool lastOnOffState = lastStatePref.getBool("lastOnOffState", true);
90+
uint8_t lastBrightness = lastStatePref.getUChar("lastBrightness", 15); // default brightness = 12%
91+
DimmableLight.begin(lastOnOffState, lastBrightness);
92+
DimmableLight.onChangeOnOff(setLightOnOff);
93+
DimmableLight.onChangeBrightness(setLightBrightness);
94+
95+
// Matter beginning - Last step, after all EndPoints are initialized
96+
Matter.begin();
97+
// This may be a restart of a already commissioned Matter accessory
98+
if (Matter.isDeviceCommissioned()) {
99+
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
100+
Serial.printf("Initial state: %s | brightness: %d\r\n", DimmableLight ? "ON" : "OFF", DimmableLight.getBrightness());
101+
setLightOnOff(DimmableLight.getOnOff()); // configure the Light based on initial state
102+
setLightBrightness(DimmableLight.getBrightness()); // configure the Light based on initial brightness
103+
}
104+
}
105+
// Button control
106+
uint32_t button_time_stamp = 0; // debouncing control
107+
bool button_state = false; // false = released | true = pressed
108+
const uint32_t debouceTime = 250; // button debouncing time (ms)
109+
const uint32_t decommissioningTimeout = 10000; // keep the button pressed for 10s to decommission the light
110+
111+
void loop() {
112+
// Check Matter Light 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 Light 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.printf("Initial state: %s | brightness: %d\r\n", DimmableLight ? "ON" : "OFF", DimmableLight.getBrightness());
129+
setLightOnOff(DimmableLight.getOnOff()); // configure the Light based on initial state
130+
setLightBrightness(DimmableLight.getBrightness()); // configure the Light based on initial brightness
131+
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
132+
}
133+
134+
// A button is also used to control the light
135+
// Check if the button has been pressed
136+
if (digitalRead(buttonPin) == LOW && !button_state) {
137+
// deals with button debouncing
138+
button_time_stamp = millis(); // record the time while the button is pressed.
139+
button_state = true; // pressed.
140+
}
141+
142+
// Onboard User Button is used as a Light toggle switch or to decommission it
143+
uint32_t time_diff = millis() - button_time_stamp;
144+
if (button_state && time_diff > debouceTime && digitalRead(buttonPin) == HIGH) {
145+
button_state = false; // released
146+
// Toggle button is released - toggle the light
147+
Serial.println("User button released. Toggling Light!");
148+
DimmableLight.toggle(); // Matter Controller also can see the change
149+
150+
// Factory reset is triggered if the button is pressed longer than 10 seconds
151+
if (time_diff > decommissioningTimeout) {
152+
Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again.");
153+
DimmableLight = false; // turn the light off
154+
Matter.decommission();
155+
}
156+
}
157+
}
Lines changed: 7 additions & 0 deletions
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+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#include <sdkconfig.h>
2+
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
3+
4+
#include <Matter.h>
5+
#include <app/server/Server.h>
6+
#include <MatterEndpoints/MatterDimmableLight.h>
7+
8+
using namespace esp_matter;
9+
using namespace esp_matter::endpoint;
10+
using namespace chip::app::Clusters;
11+
12+
bool MatterDimmableLight::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) {
13+
bool ret = true;
14+
if (!started) {
15+
log_e("Matter DimmableLight device has not begun.");
16+
return false;
17+
}
18+
19+
log_d("Dimmable Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32);
20+
21+
if (endpoint_id == getEndPointId()) {
22+
switch(cluster_id) {
23+
case OnOff::Id:
24+
if (attribute_id == OnOff::Attributes::OnOff::Id) {
25+
if (_onChangeOnOffCB != NULL) {
26+
ret = _onChangeOnOffCB(val->val.b);
27+
log_d("DimmableLight On/Off State changed to %d", val->val.b);
28+
if (ret == true) {
29+
onOffState = val->val.b;
30+
}
31+
}
32+
}
33+
break;
34+
case LevelControl::Id:
35+
if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) {
36+
if (_onChangeBrightnessCB != NULL) {
37+
ret = _onChangeBrightnessCB(val->val.u8);
38+
log_d("DimmableLight Brightness changed to %d", val->val.u8);
39+
if (ret == true) {
40+
brightnessLevel = val->val.u8;
41+
}
42+
}
43+
}
44+
break;
45+
}
46+
}
47+
return ret;
48+
}
49+
50+
MatterDimmableLight::MatterDimmableLight() {}
51+
52+
MatterDimmableLight::~MatterDimmableLight() {
53+
end();
54+
}
55+
56+
bool MatterDimmableLight::begin(bool initialState, uint8_t brightness) {
57+
ArduinoMatter::_init();
58+
dimmable_light::config_t light_config;
59+
60+
light_config.on_off.on_off = initialState;
61+
light_config.on_off.lighting.start_up_on_off = nullptr;
62+
onOffState = initialState;
63+
64+
light_config.level_control.current_level = brightness;
65+
light_config.level_control.lighting.start_up_current_level = nullptr;
66+
brightnessLevel = brightness;
67+
68+
// endpoint handles can be used to add/modify clusters.
69+
endpoint_t *endpoint = dimmable_light::create(node::get(), &light_config, ENDPOINT_FLAG_NONE, (void *)this);
70+
if (endpoint == nullptr) {
71+
log_e("Failed to create dimmable light endpoint");
72+
return false;
73+
}
74+
75+
setEndPointId(endpoint::get_id(endpoint));
76+
log_i("Dimmable Light created with endpoint_id %d", getEndPointId());
77+
started = true;
78+
return true;
79+
}
80+
81+
void MatterDimmableLight::end() {
82+
started = false;
83+
}
84+
85+
bool MatterDimmableLight::setOnOff(bool newState) {
86+
if (!started) {
87+
log_e("Matter Dimmable Light device has not begun.");
88+
return false;
89+
}
90+
91+
// avoid processing the a "no-change"
92+
if (onOffState == newState) {
93+
return true;
94+
}
95+
96+
onOffState = newState;
97+
98+
endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
99+
cluster_t *cluster = cluster::get(endpoint, OnOff::Id);
100+
attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id);
101+
102+
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
103+
attribute::get_val(attribute, &val);
104+
105+
if (val.val.b != onOffState) {
106+
val.val.b = onOffState;
107+
attribute::update(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, &val);
108+
}
109+
return true;
110+
}
111+
112+
bool MatterDimmableLight::getOnOff() {
113+
return onOffState;
114+
}
115+
116+
bool MatterDimmableLight::toggle() {
117+
return setOnOff(!onOffState);
118+
}
119+
120+
bool MatterDimmableLight::setBrightness(uint8_t newBrightness) {
121+
if (!started) {
122+
log_w("Matter Dimmable Light device has not begun.");
123+
return false;
124+
}
125+
126+
// avoid processing the a "no-change"
127+
if (brightnessLevel == newBrightness) {
128+
return true;
129+
}
130+
131+
brightnessLevel = newBrightness;
132+
133+
endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
134+
cluster_t *cluster = cluster::get(endpoint, LevelControl::Id);
135+
attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id);
136+
137+
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
138+
attribute::get_val(attribute, &val);
139+
140+
if (val.val.u8 != brightnessLevel) {
141+
val.val.u8 = brightnessLevel;
142+
attribute::update(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &val);
143+
}
144+
return true;
145+
}
146+
147+
uint8_t MatterDimmableLight::getBrightness() {
148+
return brightnessLevel;
149+
}
150+
151+
MatterDimmableLight::operator bool() {
152+
return getOnOff();
153+
}
154+
155+
void MatterDimmableLight::operator=(bool newState) {
156+
setOnOff(newState);
157+
}
158+
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#pragma once
2+
#include <sdkconfig.h>
3+
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
4+
5+
#include <Matter.h>
6+
#include <MatterEndPoint.h>
7+
8+
class MatterDimmableLight : public MatterEndPoint {
9+
public:
10+
static const uint8_t MAX_BRIGHTNESS = 255;
11+
12+
MatterDimmableLight();
13+
~MatterDimmableLight();
14+
// default initial state is off and brightness is 0
15+
virtual bool begin(bool initialState = false, uint8_t brightness = 0);
16+
// this will just stop processing Light Matter events
17+
void end();
18+
19+
bool setOnOff(bool newState); // returns true if successful
20+
bool getOnOff(); // returns current light state
21+
bool toggle(); // returns true if successful
22+
23+
bool setBrightness(uint8_t newBrightness); // returns true if successful
24+
uint8_t getBrightness(); // returns current brightness
25+
26+
operator bool(); // returns current on/off light state
27+
void operator=(bool state); // turns light on or off
28+
// this function is called by Matter internal event processor. It could be overwritten by the application, if necessary.
29+
bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val);
30+
// User Callback for whenever the Light On/Off state is changed by the Matter Controller
31+
using EndPointOnOffCB = std::function<bool(bool)>;
32+
void onChangeOnOff(EndPointOnOffCB onChangeCB) {
33+
_onChangeOnOffCB = onChangeCB;
34+
}
35+
// User Callback for whenever the Light brightness value [0..255] is changed by the Matter Controller
36+
using EndPointBrightnessCB = std::function<bool(uint8_t)>;
37+
void onChangeBrightness(EndPointBrightnessCB onChangeCB) {
38+
_onChangeBrightnessCB = onChangeCB;
39+
}
40+
41+
protected:
42+
bool started = false;
43+
bool onOffState = false; // default initial state is off, but it can be changed by begin(bool)
44+
uint8_t brightnessLevel = 0; // default initial brightness is 0, but it can be changed by begin(bool, uint8_t)
45+
EndPointOnOffCB _onChangeOnOffCB = NULL;
46+
EndPointBrightnessCB _onChangeBrightnessCB = NULL;
47+
};
48+
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */

0 commit comments

Comments
 (0)