Skip to content

Commit

Permalink
feat(Matter): New Matter Endpoint - Dimmable Light (#10543)
Browse files Browse the repository at this point in the history
* feat(matter): source code folder organization

* feat(matter): new matter endpoint dimmable light

* feat(matter): dimmable friend class and adjusts log levels

* feat(matter): adds new folder and source code to cmakelists.txt

* fix(matter): identation in cmakelists.txt

* feat(matter): adds license disclaimer to all source code

* feat(matter): example code simplification

* feat(matter): make it work with rgb and analog led

* feat(matter): single user callback based on lambda function

* feat(matter): commentaries and code improvement. update() and callbacks added.

* feat(matter): adde new keywords to arduino settings

* feat(matter): example code adjustment to use improved api

* fix(matter): it shuold work even when no callback is set

* feat(matter): matter.h includes all possible endpoint header files

* feat(matter): adjusts all examples to only include matter.h

* fix(matter): double declaration of ret - var scope issue

* ci(pre-commit): Apply automatic fixes

---------

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
  • Loading branch information
SuGlider and pre-commit-ci-lite[bot] authored Nov 5, 2024
1 parent a80b03e commit 69825a9
Show file tree
Hide file tree
Showing 14 changed files with 609 additions and 35 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ set(ARDUINO_LIBRARY_OpenThread_SRCS
libraries/OpenThread/src/OThreadCLI_Util.cpp)

set(ARDUINO_LIBRARY_Matter_SRCS
libraries/Matter/src/MatterOnOffLight.cpp
libraries/Matter/src/MatterEndpoints/MatterOnOffLight.cpp
libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp
libraries/Matter/src/Matter.cpp)

set(ARDUINO_LIBRARY_PPP_SRCS
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Matter Manager
#include <Matter.h>
#include <WiFi.h>

// List of Matter Endpoints for this Node
// On/Off Light Endpoint
#include <MatterOnOffLight.h>
MatterOnOffLight OnOffLight;

// WiFi is manually set and started
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Matter Manager
#include <Matter.h>
#include <WiFi.h>

// List of Matter Endpoints for this Node
// There will be 3 On/Off Light Endpoints in the same Node
#include <MatterOnOffLight.h>
MatterOnOffLight OnOffLight1;
MatterOnOffLight OnOffLight2;
MatterOnOffLight OnOffLight3;
Expand Down Expand Up @@ -56,9 +69,9 @@ void setup() {
OnOffLight1.begin();
OnOffLight2.begin();
OnOffLight3.begin();
OnOffLight1.onChangeOnOff(setLightOnOff1);
OnOffLight2.onChangeOnOff(setLightOnOff2);
OnOffLight3.onChangeOnOff(setLightOnOff3);
OnOffLight1.onChange(setLightOnOff1);
OnOffLight2.onChange(setLightOnOff2);
OnOffLight3.onChange(setLightOnOff3);

// Matter beginning - Last step, after all EndPoints are initialized
Matter.begin();
Expand Down
170 changes: 170 additions & 0 deletions libraries/Matter/examples/MatterDimmableLight/MatterDimmableLight.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Matter Manager
#include <Matter.h>
#include <WiFi.h>
#include <Preferences.h>

// List of Matter Endpoints for this Node
// Dimmable Light Endpoint
MatterDimmableLight DimmableLight;

// it will keep last OnOff & Brightness state stored, using Preferences
Preferences lastStatePref;

// set your board RGB LED pin here
#ifdef RGB_BUILTIN
const uint8_t ledPin = RGB_BUILTIN;
#else
const uint8_t ledPin = 2; // Set your pin here if your board has not defined LED_BUILTIN
#warning "Do not forget to set the RGB LED pin"
#endif

// set your board USER BUTTON pin here
const uint8_t buttonPin = 0; // Set your pin here. Using BOOT Button. C6/C3 use GPIO9.

// WiFi is manually set and started
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
const char *password = "your-password"; // Change this to your WiFi password

// Set the RGB LED Light based on the current state of the Dimmable Light
bool setLightState(bool state, uint8_t brightness) {
if (state) {
#ifdef RGB_BUILTIN
rgbLedWrite(ledPin, brightness, brightness, brightness);
#else
analogWrite(ledPin, brightness);
#endif
} else {
digitalWrite(ledPin, LOW);
}
// store last Brightness and OnOff state for when the Light is restarted / power goes off
lastStatePref.putUChar("lastBrightness", brightness);
lastStatePref.putBool("lastOnOffState", state);
// This callback must return the success state to Matter core
return true;
}

void setup() {
// Initialize the USER BUTTON (Boot button) GPIO that will act as a toggle switch
pinMode(buttonPin, INPUT_PULLUP);
// Initialize the LED (light) GPIO and Matter End Point
pinMode(ledPin, OUTPUT);

Serial.begin(115200);
while (!Serial) {
delay(100);
}

// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
// enable IPv6
WiFi.enableIPv6(true);
// Manually connect to WiFi
WiFi.begin(ssid, password);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\r\nWiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
delay(500);

// Initialize Matter EndPoint
lastStatePref.begin("matterLight", false);
// default OnOff state is ON if not stored before
bool lastOnOffState = lastStatePref.getBool("lastOnOffState", true);
// default brightness ~= 6% (15/255)
uint8_t lastBrightness = lastStatePref.getUChar("lastBrightness", 15);
DimmableLight.begin(lastOnOffState, lastBrightness);
// set the callback function to handle the Light state change
DimmableLight.onChange(setLightState);

// lambda functions are used to set the attribute change callbacks
DimmableLight.onChangeOnOff([](bool state) {
Serial.printf("Light OnOff changed to %s\r\n", state ? "ON" : "OFF");
return true;
});
DimmableLight.onChangeBrightness([](uint8_t level) {
Serial.printf("Light Brightness changed to %d\r\n", level);
return true;
});

// Matter beginning - Last step, after all EndPoints are initialized
Matter.begin();
// This may be a restart of a already commissioned Matter accessory
if (Matter.isDeviceCommissioned()) {
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
Serial.printf("Initial state: %s | brightness: %d\r\n", DimmableLight ? "ON" : "OFF", DimmableLight.getBrightness());
// configure the Light based on initial on-off state and brightness
DimmableLight.updateAccessory();
}
}
// Button control
uint32_t button_time_stamp = 0; // debouncing control
bool button_state = false; // false = released | true = pressed
const uint32_t debouceTime = 250; // button debouncing time (ms)
const uint32_t decommissioningTimeout = 10000; // keep the button pressed for 10s to decommission the light

void loop() {
// Check Matter Light Commissioning state, which may change during execution of loop()
if (!Matter.isDeviceCommissioned()) {
Serial.println("");
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());
// waits for Matter Light Commissioning.
uint32_t timeCount = 0;
while (!Matter.isDeviceCommissioned()) {
delay(100);
if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
}
}
Serial.printf("Initial state: %s | brightness: %d\r\n", DimmableLight ? "ON" : "OFF", DimmableLight.getBrightness());
// configure the Light based on initial on-off state and brightness
DimmableLight.updateAccessory();
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
}

// A button is also used to control the light
// Check if the button has been pressed
if (digitalRead(buttonPin) == LOW && !button_state) {
// deals with button debouncing
button_time_stamp = millis(); // record the time while the button is pressed.
button_state = true; // pressed.
}

// Onboard User Button is used as a Light toggle switch or to decommission it
uint32_t time_diff = millis() - button_time_stamp;
if (button_state && time_diff > debouceTime && digitalRead(buttonPin) == HIGH) {
button_state = false; // released
// Toggle button is released - toggle the light
Serial.println("User button released. Toggling Light!");
DimmableLight.toggle(); // Matter Controller also can see the change

// Factory reset is triggered if the button is pressed longer than 10 seconds
if (time_diff > decommissioningTimeout) {
Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again.");
DimmableLight = false; // turn the light off
Matter.decommission();
}
}
}
7 changes: 7 additions & 0 deletions libraries/Matter/examples/MatterDimmableLight/ci.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=huge_app",
"requires": [
"CONFIG_SOC_WIFI_SUPPORTED=y",
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
]
}
21 changes: 17 additions & 4 deletions libraries/Matter/examples/MatterOnOffLight/MatterOnOffLight.ino
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Matter Manager
#include <Matter.h>
#include <WiFi.h>
#include <Preferences.h>

// List of Matter Endpoints for this Node
// On/Off Light Endpoint
#include <MatterOnOffLight.h>
MatterOnOffLight OnOffLight;

// it will keep last OnOff state stored, using Preferences
Expand Down Expand Up @@ -72,15 +85,15 @@ void setup() {
lastStatePref.begin("matterLight", false);
bool lastOnOffState = lastStatePref.getBool("lastOnOffState", true);
OnOffLight.begin(lastOnOffState);
OnOffLight.onChangeOnOff(setLightOnOff);
OnOffLight.onChange(setLightOnOff);

// Matter beginning - Last step, after all EndPoints are initialized
Matter.begin();
// This may be a restart of a already commissioned Matter accessory
if (Matter.isDeviceCommissioned()) {
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
Serial.printf("Initial state: %s\r\n", OnOffLight.getOnOff() ? "ON" : "OFF");
setLightOnOff(OnOffLight.getOnOff()); // configure the Light based on initial state
OnOffLight.updateAccessory(); // configure the Light based on initial state
}
}
// Button control
Expand All @@ -107,7 +120,7 @@ void loop() {
}
}
Serial.printf("Initial state: %s\r\n", OnOffLight.getOnOff() ? "ON" : "OFF");
setLightOnOff(OnOffLight.getOnOff()); // configure the Light based on initial state
OnOffLight.updateAccessory(); // configure the Light based on initial state
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
}

Expand Down
8 changes: 7 additions & 1 deletion libraries/Matter/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
#######################################

Matter KEYWORD1
ArduinoMatter KEYWORD1
MatterOnOffLight KEYWORD1
MatterDimmableLight KEYWORD1
MatterEndPoint KEYWORD1

#######################################
Expand All @@ -16,7 +18,6 @@ MatterEndPoint KEYWORD1

begin KEYWORD2
end KEYWORD2
start KEYWORD2
getManualPairingCode KEYWORD2
getOnboardingQRCodeUrl KEYWORD2
isDeviceCommissioned KEYWORD2
Expand All @@ -27,8 +28,13 @@ decommission KEYWORD2
attributeChangeCB KEYWORD2
setOnOff KEYWORD2
getOnOff KEYWORD2
setBrightness KEYWORD2
getBrightness KEYWORD2
toggle KEYWORD2
updateAccessory KEYWORD2
onChange KEYWORD2
onChangeOnOff KEYWORD2
onChangeBrightness KEYWORD2

#######################################
# Constants (LITERAL1)
Expand Down
27 changes: 21 additions & 6 deletions libraries/Matter/src/Matter.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <sdkconfig.h>
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL

Expand Down Expand Up @@ -27,25 +41,26 @@ esp_err_t matter_light_attribute_update(
static esp_err_t app_attribute_update_cb(
attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data
) {
log_d("Attribute update callback: type: %u, endpoint: %u, cluster: %u, attribute: %u, val: %u", type, endpoint_id, cluster_id, attribute_id, val->val.u32);
esp_err_t err = ESP_OK;
MatterEndPoint *ep = (MatterEndPoint *)priv_data; // endpoint pointer to base class
switch (type) {
case PRE_UPDATE: // Callback before updating the value in the database
log_i("Attribute update callback: PRE_UPDATE");
log_v("Attribute update callback: PRE_UPDATE");
if (ep != NULL) {
err = ep->attributeChangeCB(endpoint_id, cluster_id, attribute_id, val) ? ESP_OK : ESP_FAIL;
}
break;
case POST_UPDATE: // Callback after updating the value in the database
log_i("Attribute update callback: POST_UPDATE");
log_v("Attribute update callback: POST_UPDATE");
break;
case READ: // Callback for reading the attribute value. This is used when the `ATTRIBUTE_FLAG_OVERRIDE` is set.
log_i("Attribute update callback: READ");
log_v("Attribute update callback: READ");
break;
case WRITE: // Callback for writing the attribute value. This is used when the `ATTRIBUTE_FLAG_OVERRIDE` is set.
log_i("Attribute update callback: WRITE");
log_v("Attribute update callback: WRITE");
break;
default: log_i("Attribute update callback: Unknown type %d", type);
default: log_v("Attribute update callback: Unknown type %d", type);
}
return err;
}
Expand Down Expand Up @@ -114,7 +129,7 @@ void ArduinoMatter::_init() {

void ArduinoMatter::begin() {
if (!_matter_has_started) {
log_w("No Matter endpoint has been created. Please create an endpoint first.");
log_e("No Matter endpoint has been created. Please create an endpoint first.");
return;
}

Expand Down
Loading

0 comments on commit 69825a9

Please sign in to comment.