Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(matter): Adds a new Matter Endpoint: Generic Switch (smart button) #10662

Merged
merged 3 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ set(ARDUINO_LIBRARY_OpenThread_SRCS
libraries/OpenThread/src/OThreadCLI_Util.cpp)

set(ARDUINO_LIBRARY_Matter_SRCS
libraries/Matter/src/MatterEndpoints/MatterGenericSwitch.cpp
libraries/Matter/src/MatterEndpoints/MatterOnOffLight.cpp
libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp
libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp
Expand Down
115 changes: 115 additions & 0 deletions libraries/Matter/examples/MatterSmartButon/MatterSmartButon.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// 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
// Generic Switch Endpoint - works as a smart button with a single click
MatterGenericSwitch SmartButton;

// 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

void setup() {
// Initialize the USER BUTTON (Boot button) GPIO that will act as a toggle switch
pinMode(buttonPin, INPUT_PULLUP);

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 the Matter EndPoint
SmartButton.begin();

// 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.");
}
}
// 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 Matter Fabric

void loop() {
// Check Matter Accessory 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 Generic Switch 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.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
}

// A builtin button is used to trigger a command to the Matter Controller
// 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 smart button 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
// builtin button is released - send a click event to the Matter Controller
Serial.println("User button released. Sending Click to the Matter Controller!");
// Matter Controller will receive an event and, if programmed, it will trigger an action
SmartButton.click();

// Factory reset is triggered if the button is pressed longer than 10 seconds
if (time_diff > decommissioningTimeout) {
Serial.println("Decommissioning the Generic Switch Matter Accessory. It shall be commissioned again.");
Matter.decommission();
}
}
}
7 changes: 7 additions & 0 deletions libraries/Matter/examples/MatterSmartButon/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"
]
}
3 changes: 2 additions & 1 deletion libraries/Matter/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

Matter KEYWORD1
ArduinoMatter KEYWORD1
MatterGenericSwitch KEYWORD1
MatterOnOffLight KEYWORD1
MatterDimmableLight KEYWORD1
MatterColorTemperatureLight KEYWORD1
Expand Down Expand Up @@ -45,7 +46,7 @@ onChangeOnOff KEYWORD2
onChangeBrightness KEYWORD2
onChangeColorTemperature KEYWORD2
onChangeColorHSV KEYWORD2

click KEYWORD2

#######################################
# Constants (LITERAL1)
Expand Down
2 changes: 2 additions & 0 deletions libraries/Matter/src/Matter.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <Arduino.h>
#include <esp_matter.h>
#include <ColorFormat.h>
#include <MatterEndpoints/MatterGenericSwitch.h>
#include <MatterEndpoints/MatterOnOffLight.h>
#include <MatterEndpoints/MatterDimmableLight.h>
#include <MatterEndpoints/MatterColorTemperatureLight.h>
Expand Down Expand Up @@ -48,6 +49,7 @@ class ArduinoMatter {
static void decommission();

// list of Matter EndPoints Friend Classes
friend class MatterGenericSwitch;
friend class MatterOnOffLight;
friend class MatterDimmableLight;
friend class MatterColorTemperatureLight;
Expand Down
100 changes: 100 additions & 0 deletions libraries/Matter/src/MatterEndpoints/MatterGenericSwitch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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

#include <Matter.h>
#include <app/server/Server.h>
#include <MatterEndpoints/MatterGenericSwitch.h>

using namespace esp_matter;
using namespace esp_matter::endpoint;
using namespace esp_matter::cluster;
using namespace chip::app::Clusters;

MatterGenericSwitch::MatterGenericSwitch() {}

MatterGenericSwitch::~MatterGenericSwitch() {
end();
}

bool MatterGenericSwitch::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) {
if (!started) {
log_e("Matter Generic Switch device has not begun.");
return false;
}

log_d("Generic Switch Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32);
return true;
}

bool MatterGenericSwitch::begin() {
ArduinoMatter::_init();
generic_switch::config_t switch_config;

// endpoint handles can be used to add/modify clusters.
endpoint_t *endpoint = generic_switch::create(node::get(), &switch_config, ENDPOINT_FLAG_NONE, (void *)this);
if (endpoint == nullptr) {
log_e("Failed to create Generic swtich endpoint");
return false;
}
// Add group cluster to the switch endpoint
cluster::groups::config_t groups_config;
cluster::groups::create(endpoint, &groups_config, CLUSTER_FLAG_SERVER | CLUSTER_FLAG_CLIENT);

cluster_t *aCluster = cluster::get(endpoint, Descriptor::Id);
esp_matter::cluster::descriptor::feature::taglist::add(aCluster);

cluster::fixed_label::config_t fl_config;
cluster::fixed_label::create(endpoint, &fl_config, CLUSTER_FLAG_SERVER);

cluster::user_label::config_t ul_config;
cluster::user_label::create(endpoint, &ul_config, CLUSTER_FLAG_SERVER);

aCluster = cluster::get(endpoint, Switch::Id);
switch_cluster::feature::momentary_switch::add(aCluster);
switch_cluster::event::create_initial_press(aCluster);

switch_cluster::feature::momentary_switch::add(aCluster);

switch_cluster::attribute::create_current_position(aCluster, 0);
switch_cluster::attribute::create_number_of_positions(aCluster, 2);

setEndPointId(endpoint::get_id(endpoint));
log_i("Generic Switch created with endpoint_id %d", getEndPointId());
started = true;
return true;
}

void MatterGenericSwitch::end() {
started = false;
}

void MatterGenericSwitch::click() {
if (!started) {
log_e("Matter Generic Switch device has not begun.");
return;
}

int switch_endpoint_id = getEndPointId();
uint8_t newPosition = 1;
// Press moves Position from 0 (off) to 1 (on)
chip::DeviceLayer::SystemLayer().ScheduleLambda([switch_endpoint_id, newPosition]() {
// InitialPress event takes newPosition as event data
switch_cluster::event::send_initial_press(switch_endpoint_id, newPosition);
});
}

#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */
39 changes: 39 additions & 0 deletions libraries/Matter/src/MatterEndpoints/MatterGenericSwitch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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.

#pragma once
#include <sdkconfig.h>
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL

#include <Matter.h>
#include <MatterEndPoint.h>

// Matter Generic Switch Endpoint that works as a single click smart button
class MatterGenericSwitch : public MatterEndPoint {
public:
MatterGenericSwitch();
~MatterGenericSwitch();
virtual bool begin();
void end(); // this will just stop processing Matter events

// send a simple click event to the Matter Controller
void click();

// this function is called by Matter internal event processor. It could be overwritten by the application, if necessary.
bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val);

protected:
bool started = false;
};
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */
Loading