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 2 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(ArduinoMatter::matterEvent_t eventType, const chip::DeviceLayer::ChipDeviceEvent *eventInfo) {
// Print the event type to Serial
Serial.print("===> Got a Matter Event: ");
switch (eventType) {
case ArduinoMatter::MATTER_WIFI_CONNECTIVITY_CHANGE:
Serial.println("WiFi Connectivity Change");
break;
case ArduinoMatter::MATTER_THREAD_CONNECTIVITY_CHANGE:
Serial.println("Thread Connectivity Change");
break;
case ArduinoMatter::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 ArduinoMatter::MATTER_SERVICE_CONNECTIVITY_CHANGE:
Serial.println("Service Connectivity Change");
break;
case ArduinoMatter::MATTER_SERVICE_PROVISIONING_CHANGE:
Serial.println("Service Provisioning Change");
break;
case ArduinoMatter::MATTER_TIME_SYNC_CHANGE:
Serial.println("Time Sync Change");
break;
case ArduinoMatter::MATTER_CHIPOBLE_CONNECTION_ESTABLISHED:
Serial.println("CHIPoBLE Connection Established");
break;
case ArduinoMatter::MATTER_CHIPOBLE_CONNECTION_CLOSED:
Serial.println("CHIPoBLE Connection Closed");
break;
case ArduinoMatter::MATTER_CLOSE_ALL_BLE_CONNECTIONS:
Serial.println("Close All BLE Connections");
break;
case ArduinoMatter::MATTER_WIFI_DEVICE_AVAILABLE:
Serial.println("WiFi Device Available");
break;
case ArduinoMatter::MATTER_OPERATIONAL_NETWORK_STARTED:
Serial.println("Operational Network Started");
break;
case ArduinoMatter::MATTER_THREAD_STATE_CHANGE:
Serial.println("Thread State Change");
break;
case ArduinoMatter::MATTER_THREAD_INTERFACE_STATE_CHANGE:
Serial.println("Thread Interface State Change");
break;
case ArduinoMatter::MATTER_CHIPOBLE_ADVERTISING_CHANGE:
Serial.println("CHIPoBLE Advertising Change");
break;
case ArduinoMatter::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 ArduinoMatter::MATTER_COMMISSIONING_COMPLETE:
Serial.println("Commissioning Complete");
break;
case ArduinoMatter::MATTER_FAIL_SAFE_TIMER_EXPIRED:
Serial.println("Fail Safe Timer Expired");
break;
case ArduinoMatter::MATTER_OPERATIONAL_NETWORK_ENABLED:
Serial.println("Operational Network Enabled");
break;
case ArduinoMatter::MATTER_DNSSD_INITIALIZED:
Serial.println("DNS-SD Initialized");
break;
case ArduinoMatter::MATTER_DNSSD_RESTART_NEEDED:
Serial.println("DNS-SD Restart Needed");
break;
case ArduinoMatter::MATTER_BINDINGS_CHANGED_VIA_CLUSTER:
Serial.println("Bindings Changed Via Cluster");
break;
case ArduinoMatter::MATTER_OTA_STATE_CHANGED:
Serial.println("OTA State Changed");
break;
case ArduinoMatter::MATTER_SERVER_READY:
Serial.println("Server Ready");
break;
case ArduinoMatter::MATTER_BLE_DEINITIALIZED:
Serial.println("BLE Deinitialized");
break;
case ArduinoMatter::MATTER_COMMISSIONING_SESSION_STARTED:
Serial.println("Commissioning Session Started");
break;
case ArduinoMatter::MATTER_COMMISSIONING_SESSION_STOPPED:
Serial.println("Commissioning Session Stopped");
break;
case ArduinoMatter::MATTER_COMMISSIONING_WINDOW_OPEN:
Serial.println("Commissioning Window Opened");
break;
case ArduinoMatter::MATTER_COMMISSIONING_WINDOW_CLOSED:
Serial.println("Commissioning Window Closed");
break;
case ArduinoMatter::MATTER_FABRIC_WILL_BE_REMOVED:
Serial.println("Fabric Will Be Removed");
break;
case ArduinoMatter::MATTER_FABRIC_REMOVED:
Serial.println("Fabric Removed");
break;
case ArduinoMatter::MATTER_FABRIC_COMMITTED:
Serial.println("Fabric Committed");
break;
case ArduinoMatter::MATTER_FABRIC_UPDATED:
Serial.println("Fabric Updated");
break;
case ArduinoMatter::MATTER_ESP32_SPECIFIC_EVENT:
Serial.println("Sending ESP32 Platform Specific Events");
break;
case ArduinoMatter::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. Commsssioning 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"
]
}
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<ArduinoMatter::matterEvent_t>(event->Type), event);
}
}

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