Skip to content

feat(matter): Adds Matter Events callback plus example #11465

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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
246 changes: 246 additions & 0 deletions libraries/Matter/examples/MatterEvents/MatterEvents.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// Copyright 2025 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>

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

// List of Matter Endpoints for this Node
// On/Off Light Endpoint
MatterOnOffLight OnOffLight;

// This function is called when a Matter event occurs
void onMatterEvent(matterEvent_t eventType, const chip::DeviceLayer::ChipDeviceEvent *eventInfo) {
// Print the event type to Serial
Serial.print("===> Got a Matter Event: ");
switch (eventType) {
case MATTER_WIFI_CONNECTIVITY_CHANGE:
Serial.println("WiFi Connectivity Change");
break;
case MATTER_THREAD_CONNECTIVITY_CHANGE:
Serial.println("Thread Connectivity Change");
break;
case MATTER_INTERNET_CONNECTIVITY_CHANGE: {
bool newIPAddress = false;
Serial.print("Internet Connectivity Change :: ");
if (eventInfo->InternetConnectivityChange.IPv4 != chip::DeviceLayer::ConnectivityChange::kConnectivity_NoChange) {
Serial.print("IPv4 Connectivity: ");
switch (eventInfo->InternetConnectivityChange.IPv4) {
case chip::DeviceLayer::ConnectivityChange::kConnectivity_Established: {
newIPAddress = true;
break;
}
case chip::DeviceLayer::ConnectivityChange::kConnectivity_Lost:
Serial.println("Lost");
break;
default:
Serial.println("Unknown");
break;
}
}
if (eventInfo->InternetConnectivityChange.IPv6 != chip::DeviceLayer::ConnectivityChange::kConnectivity_NoChange) {
Serial.print("IPv6 Connectivity: ");
switch (eventInfo->InternetConnectivityChange.IPv6) {
case chip::DeviceLayer::ConnectivityChange::kConnectivity_Established: {
newIPAddress = true;
break;
}
case chip::DeviceLayer::ConnectivityChange::kConnectivity_Lost:
Serial.println("Lost");
break;
default:
Serial.println("Unknown");
break;
}
}
// Print the IP address if it was established
if (newIPAddress) {
Serial.print("Established - IP Address: ");
char ipAddressStr[chip::Transport::PeerAddress::kMaxToStringSize];
eventInfo->InternetConnectivityChange.ipAddress.ToString(ipAddressStr);
Serial.println(ipAddressStr);
}
break;
}
case MATTER_SERVICE_CONNECTIVITY_CHANGE:
Serial.println("Service Connectivity Change");
break;
case MATTER_SERVICE_PROVISIONING_CHANGE:
Serial.println("Service Provisioning Change");
break;
case MATTER_TIME_SYNC_CHANGE:
Serial.println("Time Sync Change");
break;
case MATTER_CHIPOBLE_CONNECTION_ESTABLISHED:
Serial.println("CHIPoBLE Connection Established");
break;
case MATTER_CHIPOBLE_CONNECTION_CLOSED:
Serial.println("CHIPoBLE Connection Closed");
break;
case MATTER_CLOSE_ALL_BLE_CONNECTIONS:
Serial.println("Close All BLE Connections");
break;
case MATTER_WIFI_DEVICE_AVAILABLE:
Serial.println("WiFi Device Available");
break;
case MATTER_OPERATIONAL_NETWORK_STARTED:
Serial.println("Operational Network Started");
break;
case MATTER_THREAD_STATE_CHANGE:
Serial.println("Thread State Change");
break;
case MATTER_THREAD_INTERFACE_STATE_CHANGE:
Serial.println("Thread Interface State Change");
break;
case MATTER_CHIPOBLE_ADVERTISING_CHANGE:
Serial.println("CHIPoBLE Advertising Change");
break;
case MATTER_INTERFACE_IP_ADDRESS_CHANGED:
switch (eventInfo->InterfaceIpAddressChanged.Type) {
case chip::DeviceLayer::InterfaceIpChangeType::kIpV4_Assigned:
Serial.println("IPv4 Address Assigned");
break;
case chip::DeviceLayer::InterfaceIpChangeType::kIpV4_Lost:
Serial.println("IPv4 Address Lost");
break;
case chip::DeviceLayer::InterfaceIpChangeType::kIpV6_Assigned:
Serial.println("IPv6 Address Assigned");
break;
case chip::DeviceLayer::InterfaceIpChangeType::kIpV6_Lost:
Serial.println("IPv6 Address Lost");
break;
}
break;
case MATTER_COMMISSIONING_COMPLETE:
Serial.println("Commissioning Complete");
break;
case MATTER_FAIL_SAFE_TIMER_EXPIRED:
Serial.println("Fail Safe Timer Expired");
break;
case MATTER_OPERATIONAL_NETWORK_ENABLED:
Serial.println("Operational Network Enabled");
break;
case MATTER_DNSSD_INITIALIZED:
Serial.println("DNS-SD Initialized");
break;
case MATTER_DNSSD_RESTART_NEEDED:
Serial.println("DNS-SD Restart Needed");
break;
case MATTER_BINDINGS_CHANGED_VIA_CLUSTER:
Serial.println("Bindings Changed Via Cluster");
break;
case MATTER_OTA_STATE_CHANGED:
Serial.println("OTA State Changed");
break;
case MATTER_SERVER_READY:
Serial.println("Server Ready");
break;
case MATTER_BLE_DEINITIALIZED:
Serial.println("BLE Deinitialized");
break;
case MATTER_COMMISSIONING_SESSION_STARTED:
Serial.println("Commissioning Session Started");
break;
case MATTER_COMMISSIONING_SESSION_STOPPED:
Serial.println("Commissioning Session Stopped");
break;
case MATTER_COMMISSIONING_WINDOW_OPEN:
Serial.println("Commissioning Window Opened");
break;
case MATTER_COMMISSIONING_WINDOW_CLOSED:
Serial.println("Commissioning Window Closed");
break;
case MATTER_FABRIC_WILL_BE_REMOVED:
Serial.println("Fabric Will Be Removed");
break;
case MATTER_FABRIC_REMOVED:
Serial.println("Fabric Removed");
break;
case MATTER_FABRIC_COMMITTED:
Serial.println("Fabric Committed");
break;
case MATTER_FABRIC_UPDATED:
Serial.println("Fabric Updated");
break;
case MATTER_ESP32_SPECIFIC_EVENT:
Serial.println("Sending ESP32 Platform Specific Events");
break;
case MATTER_ESP32_PUBLIC_SPECIFIC_EVENT:
Serial.println("Next Event Has Populated EventInfo");
break;
default:
// If the event type is not recognized, print "Unknown" and the event ID
Serial.println("Unknown, EventID = 0x" + String(eventType, HEX));
break;
}
}

void setup() {
Serial.begin(115200);
while (!Serial) {
delay(10); // Wait for Serial to initialize
}

// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
// Manually connect to WiFi
WiFi.enableIPv6(true); // Enable IPv6 if needed
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 at least one Matter EndPoint
OnOffLight.begin();

// Set the Matter Event Callback
Matter.onEvent(onMatterEvent);
// Matter beginning - Last step, after all EndPoints are initialized
Matter.begin();
Serial.println("Starting Matter Commission Test...");

}

void loop() {
// Check Matter Commissioning state
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.
while (!Matter.isDeviceCommissioned()) {
delay(5000);
Serial.println("Matter Fabric not commissioned yet. Waiting for commissioning.");
}
}
Serial.println("Matter Node is commissioned and connected to Wi-Fi.");
Serial.println("====> Decommissioning in 60 seconds. <====");
delay(60000);
Matter.decommission();
Serial.println("Matter Node is decommissioned. Commissioning widget shall start over.");
}
7 changes: 7 additions & 0 deletions libraries/Matter/examples/MatterEvents/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"
]
}
39 changes: 37 additions & 2 deletions libraries/Matter/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ EndPointSpeedCB KEYWORD1
EndPointOnOffCB KEYWORD1
EndPointBrightnessCB KEYWORD1
EndPointRGBColorCB KEYWORD1
matterEvent_t KEYWORD1
matterEventCB KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
Expand Down Expand Up @@ -108,6 +110,7 @@ onChangeMode KEYWORD2
onChangeLocalTemperature KEYWORD2
onChangeCoolingSetpoint KEYWORD2
onChangeHeatingSetpoint KEYWORD2
onEvent KEYWORD2

#######################################
# Constants (LITERAL1)
Expand Down Expand Up @@ -144,5 +147,37 @@ THERMOSTAT_MODE_OFF LITERAL1
THERMOSTAT_MODE_AUTO LITERAL1
THERMOSTAT_MODE_COOL LITERAL1
THERMOSTAT_MODE_HEAT LITERAL1
THERMOSTAT_AUTO_MODE_DISABLED LITERAL1
THERMOSTAT_AUTO_MODE_ENABLED LITERAL1
MATTER_WIFI_CONNECTIVITY_CHANGE LITERAL1
MATTER_THREAD_CONNECTIVITY_CHANGE LITERAL1
MATTER_INTERNET_CONNECTIVITY_CHANGE LITERAL1
MATTER_SERVICE_CONNECTIVITY_CHANGE LITERAL1
MATTER_SERVICE_PROVISIONING_CHANGE LITERAL1
MATTER_TIME_SYNC_CHANGE LITERAL1
MATTER_CHIPOBLE_CONNECTION_ESTABLISHED LITERAL1
MATTER_CHIPOBLE_CONNECTION_CLOSED LITERAL1
MATTER_CLOSE_ALL_BLE_CONNECTIONS LITERAL1
MATTER_WIFI_DEVICE_AVAILABLE LITERAL1
MATTER_OPERATIONAL_NETWORK_STARTED LITERAL1
MATTER_THREAD_STATE_CHANGE LITERAL1
MATTER_THREAD_INTERFACE_STATE_CHANGE LITERAL1
MATTER_CHIPOBLE_ADVERTISING_CHANGE LITERAL1
MATTER_INTERFACE_IP_ADDRESS_CHANGED LITERAL1
MATTER_COMMISSIONING_COMPLETE LITERAL1
MATTER_FAIL_SAFE_TIMER_EXPIRED LITERAL1
MATTER_OPERATIONAL_NETWORK_ENABLED LITERAL1
MATTER_DNSSD_INITIALIZED LITERAL1
MATTER_DNSSD_RESTART_NEEDED LITERAL1
MATTER_BINDINGS_CHANGED_VIA_CLUSTER LITERAL1
MATTER_OTA_STATE_CHANGED LITERAL1
MATTER_SERVER_READY LITERAL1
MATTER_BLE_DEINITIALIZED LITERAL1
MATTER_ESP32_SPECIFIC_EVENT LITERAL1
MATTER_COMMISSIONING_SESSION_STARTED LITERAL1
MATTER_COMMISSIONING_SESSION_STOPPED LITERAL1
MATTER_COMMISSIONING_WINDOW_OPEN LITERAL1
MATTER_COMMISSIONING_WINDOW_CLOSED LITERAL1
MATTER_FABRIC_WILL_BE_REMOVED LITERAL1
MATTER_FABRIC_REMOVED LITERAL1
MATTER_FABRIC_COMMITTED LITERAL1
MATTER_FABRIC_UPDATED LITERAL1
MATTER_ESP32_PUBLIC_SPECIFIC_EVENT LITERAL1
37 changes: 21 additions & 16 deletions libraries/Matter/src/Matter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ constexpr auto k_timeout_seconds = 300;

static bool _matter_has_started = false;
static node::config_t node_config;
static node_t *deviceNode = NULL;
static node_t *deviceNode = nullptr;
ArduinoMatter::matterEventCB ArduinoMatter::_matterEventCB = nullptr;

// This callback is called for every attribute update. The callback implementation shall
// handle the desired attributes and return an appropriate error code. If the attribute
Expand All @@ -42,7 +43,7 @@ static esp_err_t app_attribute_update_cb(
switch (type) {
case PRE_UPDATE: // Callback before updating the value in the database
log_v("Attribute update callback: PRE_UPDATE");
if (ep != NULL) {
if (ep != nullptr) {
err = ep->attributeChangeCB(endpoint_id, cluster_id, attribute_id, val) ? ESP_OK : ESP_FAIL;
}
break;
Expand Down Expand Up @@ -78,7 +79,7 @@ static esp_err_t app_identification_cb(identification::callback_type_t type, uin
identifyIsActive = false;
log_v("Identification callback: STOP");
}
if (ep != NULL) {
if (ep != nullptr) {
err = ep->endpointIdentifyCB(endpoint_id, identifyIsActive) ? ESP_OK : ESP_FAIL;
}

Expand All @@ -89,21 +90,21 @@ static esp_err_t app_identification_cb(identification::callback_type_t type, uin
static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) {
switch (event->Type) {
case chip::DeviceLayer::DeviceEventType::kInterfaceIpAddressChanged:
log_i(
log_d(
"Interface %s Address changed", event->InterfaceIpAddressChanged.Type == chip::DeviceLayer::InterfaceIpChangeType::kIpV4_Assigned ? "IPv4" : "IPV6"
);
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningComplete: log_i("Commissioning complete"); break;
case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired: log_i("Commissioning failed, fail safe timer expired"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStarted: log_i("Commissioning session started"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped: log_i("Commissioning session stopped"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened: log_i("Commissioning window opened"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed: log_i("Commissioning window closed"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningComplete: log_d("Commissioning complete"); break;
case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired: log_d("Commissioning failed, fail safe timer expired"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStarted: log_d("Commissioning session started"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped: log_d("Commissioning session stopped"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened: log_d("Commissioning window opened"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed: log_d("Commissioning window closed"); break;
case chip::DeviceLayer::DeviceEventType::kFabricRemoved:
{
log_i("Fabric removed successfully");
log_d("Fabric removed successfully");
if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0) {
log_i("No fabric left, opening commissioning window");
log_d("No fabric left, opening commissioning window");
chip::CommissioningWindowManager &commissionMgr = chip::Server::GetInstance().GetCommissioningWindowManager();
constexpr auto kTimeoutSeconds = chip::System::Clock::Seconds16(k_timeout_seconds);
if (!commissionMgr.IsCommissioningWindowOpen()) {
Expand All @@ -116,12 +117,16 @@ static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) {
}
break;
}
case chip::DeviceLayer::DeviceEventType::kFabricWillBeRemoved: log_i("Fabric will be removed"); break;
case chip::DeviceLayer::DeviceEventType::kFabricUpdated: log_i("Fabric is updated"); break;
case chip::DeviceLayer::DeviceEventType::kFabricCommitted: log_i("Fabric is committed"); break;
case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized: log_i("BLE deinitialized and memory reclaimed"); break;
case chip::DeviceLayer::DeviceEventType::kFabricWillBeRemoved: log_d("Fabric will be removed"); break;
case chip::DeviceLayer::DeviceEventType::kFabricUpdated: log_d("Fabric is updated"); break;
case chip::DeviceLayer::DeviceEventType::kFabricCommitted: log_d("Fabric is committed"); break;
case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized: log_d("BLE deinitialized and memory reclaimed"); break;
default: break;
}
// Check if the user-defined callback is set
if (ArduinoMatter::_matterEventCB != nullptr) {
ArduinoMatter::_matterEventCB(static_cast<matterEvent_t>(event->Type), event);
}
}

void ArduinoMatter::_init() {
Expand Down
Loading
Loading