Merge branch 'master' into release/v3.3.x

This commit is contained in:
Me No Dev 2025-06-21 15:46:46 +03:00 committed by GitHub
commit bf55924b67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 484 additions and 67 deletions

View file

@ -32,10 +32,11 @@
#include "driver/gpio.h" #include "driver/gpio.h"
#include "hal/gpio_hal.h" #include "hal/gpio_hal.h"
#include "esp_rom_gpio.h" #include "esp_rom_gpio.h"
#include "esp_private/gpio.h"
#include "driver/rtc_io.h" #include "driver/rtc_io.h"
#include "driver/lp_io.h" #include "driver/lp_io.h"
#include "soc/uart_periph.h" #include "soc/uart_pins.h"
#include "esp_private/uart_share_hw_ctrl.h" #include "esp_private/uart_share_hw_ctrl.h"
static int s_uart_debug_nr = 0; // UART number for debug output static int s_uart_debug_nr = 0; // UART number for debug output
@ -1399,39 +1400,9 @@ unsigned long uartDetectBaudrate(uart_t *uart) {
} }
/* /*
These functions are for testing purpose only and can be used in Arduino Sketches * These functions are for testing purposes only and can be used in Arduino Sketches.
Those are used in the UART examples * They are utilized in the UART examples and CI.
*/ */
/*
This is intended to make an internal loopback connection using IOMUX
The function uart_internal_loopback() shall be used right after Arduino Serial.begin(...)
This code "replaces" the physical wiring for connecting TX <--> RX in a loopback
*/
// gets the right TX or RX SIGNAL, based on the UART number from gpio_sig_map.h
#ifdef CONFIG_IDF_TARGET_ESP32P4
#define UART_TX_SIGNAL(uartNumber) \
(uartNumber == UART_NUM_0 \
? UART0_TXD_PAD_OUT_IDX \
: (uartNumber == UART_NUM_1 \
? UART1_TXD_PAD_OUT_IDX \
: (uartNumber == UART_NUM_2 ? UART2_TXD_PAD_OUT_IDX : (uartNumber == UART_NUM_3 ? UART3_TXD_PAD_OUT_IDX : UART4_TXD_PAD_OUT_IDX))))
#define UART_RX_SIGNAL(uartNumber) \
(uartNumber == UART_NUM_0 \
? UART0_RXD_PAD_IN_IDX \
: (uartNumber == UART_NUM_1 \
? UART1_RXD_PAD_IN_IDX \
: (uartNumber == UART_NUM_2 ? UART2_RXD_PAD_IN_IDX : (uartNumber == UART_NUM_3 ? UART3_RXD_PAD_IN_IDX : UART4_RXD_PAD_IN_IDX))))
#else
#if SOC_UART_HP_NUM > 2
#define UART_TX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : (uartNumber == UART_NUM_1 ? U1TXD_OUT_IDX : U2TXD_OUT_IDX))
#define UART_RX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0RXD_IN_IDX : (uartNumber == UART_NUM_1 ? U1RXD_IN_IDX : U2RXD_IN_IDX))
#else
#define UART_TX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : U1TXD_OUT_IDX)
#define UART_RX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0RXD_IN_IDX : U1RXD_IN_IDX)
#endif
#endif // ifdef CONFIG_IDF_TARGET_ESP32P4
/* /*
This function internally binds defined UARTs TX signal with defined RX pin of any UART (same or different). This function internally binds defined UARTs TX signal with defined RX pin of any UART (same or different).
@ -1443,7 +1414,12 @@ void uart_internal_loopback(uint8_t uartNum, int8_t rxPin) {
log_e("UART%d is not supported for loopback or RX pin %d is invalid.", uartNum, rxPin); log_e("UART%d is not supported for loopback or RX pin %d is invalid.", uartNum, rxPin);
return; return;
} }
esp_rom_gpio_connect_out_signal(rxPin, UART_TX_SIGNAL(uartNum), false, false); // forces rxPin to use GPIO Matrix and setup the pin to receive UART TX Signal - IDF 5.4.1 Change with uart_release_pin()
gpio_func_sel((gpio_num_t)rxPin, PIN_FUNC_GPIO);
gpio_pullup_en((gpio_num_t)rxPin);
gpio_input_enable((gpio_num_t)rxPin);
esp_rom_gpio_connect_in_signal(rxPin, uart_periph_signal[uartNum].pins[SOC_UART_RX_PIN_IDX].signal, false);
esp_rom_gpio_connect_out_signal(rxPin, uart_periph_signal[uartNum].pins[SOC_UART_TX_PIN_IDX].signal, false, false);
} }
/* /*

View file

@ -111,16 +111,22 @@ void DNSServer::_handleUDP(AsyncUDPPacket &pkt) {
// will reply with IP only to "*" or if domain matches without www. subdomain // will reply with IP only to "*" or if domain matches without www. subdomain
if (dnsHeader.OPCode == DNS_OPCODE_QUERY && requestIncludesOnlyOneQuestion(dnsHeader) if (dnsHeader.OPCode == DNS_OPCODE_QUERY && requestIncludesOnlyOneQuestion(dnsHeader)
&& (_domainName.isEmpty() || getDomainNameWithoutWwwPrefix(static_cast<const unsigned char *>(dnsQuestion.QName), dnsQuestion.QNameLength) == _domainName)) { && (_domainName.isEmpty() || getDomainNameWithoutWwwPrefix(static_cast<const unsigned char *>(dnsQuestion.QName), dnsQuestion.QNameLength) == _domainName)) {
// Qtype = A (1) or ANY (255): send an A record otherwise an empty response
if (ntohs(dnsQuestion.QType) == 1 || ntohs(dnsQuestion.QType) == 255) {
replyWithIP(pkt, dnsHeader, dnsQuestion); replyWithIP(pkt, dnsHeader, dnsQuestion);
} else {
replyWithNoAnsw(pkt, dnsHeader, dnsQuestion);
}
return; return;
} }
// otherwise reply with custom code // otherwise reply with custom code
replyWithCustomCode(pkt, dnsHeader); replyWithCustomCode(pkt, dnsHeader);
} }
bool DNSServer::requestIncludesOnlyOneQuestion(DNSHeader &dnsHeader) { bool DNSServer::requestIncludesOnlyOneQuestion(DNSHeader &dnsHeader) {
return ntohs(dnsHeader.QDCount) == 1 && dnsHeader.ANCount == 0 && dnsHeader.NSCount == 0 && dnsHeader.ARCount == 0; dnsHeader.ARCount = 0; // We assume that if ARCount !=0 there is a EDNS OPT packet, just ignore
return ntohs(dnsHeader.QDCount) == 1 && dnsHeader.ANCount == 0 && dnsHeader.NSCount == 0;
} }
String DNSServer::getDomainNameWithoutWwwPrefix(const unsigned char *start, size_t len) { String DNSServer::getDomainNameWithoutWwwPrefix(const unsigned char *start, size_t len) {
@ -139,7 +145,6 @@ String DNSServer::getDomainNameWithoutWwwPrefix(const unsigned char *start, size
void DNSServer::replyWithIP(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion) { void DNSServer::replyWithIP(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion) {
AsyncUDPMessage rpl; AsyncUDPMessage rpl;
// Change the type of message to a response and set the number of answers equal to // Change the type of message to a response and set the number of answers equal to
// the number of questions in the header // the number of questions in the header
dnsHeader.QR = DNS_QR_RESPONSE; dnsHeader.QR = DNS_QR_RESPONSE;
@ -187,3 +192,76 @@ void DNSServer::replyWithCustomCode(AsyncUDPPacket &req, DNSHeader &dnsHeader) {
rpl.write(reinterpret_cast<const uint8_t *>(&dnsHeader), sizeof(DNSHeader)); rpl.write(reinterpret_cast<const uint8_t *>(&dnsHeader), sizeof(DNSHeader));
_udp.sendTo(rpl, req.remoteIP(), req.remotePort()); _udp.sendTo(rpl, req.remoteIP(), req.remotePort());
} }
void DNSServer::replyWithNoAnsw(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion) {
dnsHeader.QR = DNS_QR_RESPONSE;
dnsHeader.ANCount = 0;
dnsHeader.NSCount = htons(1);
AsyncUDPMessage rpl;
rpl.write(reinterpret_cast<const uint8_t *>(&dnsHeader), sizeof(DNSHeader));
// Write the question
rpl.write(dnsQuestion.QName, dnsQuestion.QNameLength);
rpl.write((uint8_t *)&dnsQuestion.QType, 2);
rpl.write((uint8_t *)&dnsQuestion.QClass, 2);
// An empty answer contains an authority section with a SOA,
// We take the name of the query as the root of the zone for which the SOA is generated
// and use a value of DNS_MINIMAL_TTL seconds in order to minimize negative caching
// Write the authority section:
// The SOA RR's ownername is set equal to the query name, and we use made up names for
// the MNAME and RNAME - it doesn't really matter from a protocol perspective - as for
// a no such QTYPE answer only the timing fields are used.
// a protocol perspective - it
// Use DNS name compression : instead of repeating the name in this RNAME occurrence,
// set the two MSB of the byte corresponding normally to the length to 1. The following
// 14 bits must be used to specify the offset of the domain name in the message
// (<255 here so the first byte has the 6 LSB at 0)
rpl.write((uint8_t)0xC0);
rpl.write((uint8_t)DNS_OFFSET_DOMAIN_NAME);
// DNS type A : host address, DNS class IN for INternet, returning an IPv4 address
uint16_t answerType = htons(DNS_TYPE_SOA), answerClass = htons(DNS_CLASS_IN);
uint32_t Serial = htonl(DNS_SOA_SERIAL); // Date type serial based on the date this piece of code was written
uint32_t Refresh = htonl(DNS_SOA_REFRESH); // These timers don't matter, we don't serve zone transfers
uint32_t Retry = htonl(DNS_SOA_RETRY);
uint32_t Expire = htonl(DNS_SOA_EXPIRE);
uint32_t MinTTL = htonl(DNS_MINIMAL_TTL); // See RFC2308 section 5
char MLabel[] = DNS_SOA_MNAME_LABEL;
char RLabel[] = DNS_SOA_RNAME_LABEL;
char PostFixLabel[] = DNS_SOA_POSTFIX_LABEL;
// 4 accounts for len fields and for both rname
// and lname and their postfix labels and there are 5 32 bit fields
uint16_t RdataLength = htons((uint16_t)(strlen(MLabel) + strlen(RLabel) + 2 * strlen(PostFixLabel) + 4 + 5 * sizeof(Serial)));
rpl.write((unsigned char *)&answerType, 2);
rpl.write((unsigned char *)&answerClass, 2);
rpl.write((unsigned char *)&MinTTL, 4); // DNS Time To Live
rpl.write((unsigned char *)&RdataLength, 2);
rpl.write((uint8_t)strlen(MLabel));
rpl.write((unsigned char *)&MLabel, strlen(MLabel));
rpl.write((unsigned char *)&PostFixLabel, strlen(PostFixLabel));
rpl.write((uint8_t)0);
// rpl.write((uint8_t)0xC0);
// rpl.write((uint8_t)DNS_OFFSET_DOMAIN_NAME);
rpl.write((uint8_t)strlen(RLabel));
rpl.write((unsigned char *)&RLabel, strlen(RLabel));
rpl.write((unsigned char *)&PostFixLabel, strlen(PostFixLabel));
rpl.write((uint8_t)0);
rpl.write((unsigned char *)&Serial, 4);
rpl.write((unsigned char *)&Refresh, 4);
rpl.write((unsigned char *)&Retry, 4);
rpl.write((unsigned char *)&Expire, 4);
rpl.write((unsigned char *)&MinTTL, 4);
_udp.sendTo(rpl, req.remoteIP(), req.remotePort());
}

View file

@ -9,6 +9,26 @@
#define DNS_OFFSET_DOMAIN_NAME DNS_HEADER_SIZE // Offset in bytes to reach the domain name labels in the DNS message #define DNS_OFFSET_DOMAIN_NAME DNS_HEADER_SIZE // Offset in bytes to reach the domain name labels in the DNS message
#define DNS_DEFAULT_PORT 53 #define DNS_DEFAULT_PORT 53
#define DNS_SOA_MNAME_LABEL "ns"
#define DNS_SOA_RNAME_LABEL "esp32"
// The POSTFIX_LABEL will be concatenated to the RName and MName Label label
// do not use a multilabel name here. "local" is a good choice as it is reserved for
// local use by IANA
// The postfix label is defined as an array of characters that follows the
// definition of RFC1035 3.1
// for instance, a postfix of example.com would be defined as:
// #define DNS_SOA_POSTFIX_LABEL {'\7', 'e', 'x', 'a', 'm', 'p', 'l', 'e', '\3', 'c', 'o', 'm', '\0'}
#define DNS_SOA_POSTFIX_LABEL \
{ '\5', 'l', 'o', 'c', 'a', 'l', '\0' }
// From the following values only the MINIMAL_TTL has relevance
// in the context of client-server protocol interactions.
// The other values are arbitrary chosen as they are only relevant for
// in a zone-transfer scenario.
#define DNS_SOA_SERIAL 2025052900 // Arbitrary serial (format: YYYYMMDDnn)
#define DNS_SOA_REFRESH 100000 // Arbitrary (seconds)
#define DNS_SOA_RETRY 10000 // Arbitrary (seconds)
#define DNS_SOA_EXPIRE 1000000 // Arbitrary (seconds)
#define DNS_MINIMAL_TTL 5 // Time to live for negative answers RFC2308
enum class DNSReplyCode : uint16_t { enum class DNSReplyCode : uint16_t {
NoError = 0, NoError = 0,
FormError = 1, FormError = 1,
@ -179,5 +199,7 @@ private:
inline bool requestIncludesOnlyOneQuestion(DNSHeader &dnsHeader); inline bool requestIncludesOnlyOneQuestion(DNSHeader &dnsHeader);
void replyWithIP(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion); void replyWithIP(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion);
inline void replyWithCustomCode(AsyncUDPPacket &req, DNSHeader &dnsHeader); inline void replyWithCustomCode(AsyncUDPPacket &req, DNSHeader &dnsHeader);
inline void replyWithNoAnsw(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion);
void _handleUDP(AsyncUDPPacket &pkt); void _handleUDP(AsyncUDPPacket &pkt);
}; };

View file

@ -0,0 +1,168 @@
// 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.");
}

View file

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=huge_app",
"requires": [
"CONFIG_SOC_WIFI_SUPPORTED=y",
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
]
}

View file

@ -36,6 +36,8 @@ EndPointSpeedCB KEYWORD1
EndPointOnOffCB KEYWORD1 EndPointOnOffCB KEYWORD1
EndPointBrightnessCB KEYWORD1 EndPointBrightnessCB KEYWORD1
EndPointRGBColorCB KEYWORD1 EndPointRGBColorCB KEYWORD1
matterEvent_t KEYWORD1
matterEventCB KEYWORD1
####################################### #######################################
# Methods and Functions (KEYWORD2) # Methods and Functions (KEYWORD2)
@ -108,6 +110,7 @@ onChangeMode KEYWORD2
onChangeLocalTemperature KEYWORD2 onChangeLocalTemperature KEYWORD2
onChangeCoolingSetpoint KEYWORD2 onChangeCoolingSetpoint KEYWORD2
onChangeHeatingSetpoint KEYWORD2 onChangeHeatingSetpoint KEYWORD2
onEvent KEYWORD2
####################################### #######################################
# Constants (LITERAL1) # Constants (LITERAL1)
@ -144,5 +147,37 @@ THERMOSTAT_MODE_OFF LITERAL1
THERMOSTAT_MODE_AUTO LITERAL1 THERMOSTAT_MODE_AUTO LITERAL1
THERMOSTAT_MODE_COOL LITERAL1 THERMOSTAT_MODE_COOL LITERAL1
THERMOSTAT_MODE_HEAT LITERAL1 THERMOSTAT_MODE_HEAT LITERAL1
THERMOSTAT_AUTO_MODE_DISABLED LITERAL1 MATTER_WIFI_CONNECTIVITY_CHANGE LITERAL1
THERMOSTAT_AUTO_MODE_ENABLED 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

View file

@ -28,7 +28,8 @@ constexpr auto k_timeout_seconds = 300;
static bool _matter_has_started = false; static bool _matter_has_started = false;
static node::config_t node_config; 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 // 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 // handle the desired attributes and return an appropriate error code. If the attribute
@ -42,7 +43,7 @@ static esp_err_t app_attribute_update_cb(
switch (type) { switch (type) {
case PRE_UPDATE: // Callback before updating the value in the database case PRE_UPDATE: // Callback before updating the value in the database
log_v("Attribute update callback: PRE_UPDATE"); 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; err = ep->attributeChangeCB(endpoint_id, cluster_id, attribute_id, val) ? ESP_OK : ESP_FAIL;
} }
break; break;
@ -78,7 +79,7 @@ static esp_err_t app_identification_cb(identification::callback_type_t type, uin
identifyIsActive = false; identifyIsActive = false;
log_v("Identification callback: STOP"); log_v("Identification callback: STOP");
} }
if (ep != NULL) { if (ep != nullptr) {
err = ep->endpointIdentifyCB(endpoint_id, identifyIsActive) ? ESP_OK : ESP_FAIL; err = ep->endpointIdentifyCB(endpoint_id, identifyIsActive) ? ESP_OK : ESP_FAIL;
} }
@ -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) { static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) {
switch (event->Type) { switch (event->Type) {
case chip::DeviceLayer::DeviceEventType::kInterfaceIpAddressChanged: case chip::DeviceLayer::DeviceEventType::kInterfaceIpAddressChanged:
log_i( log_d(
"Interface %s Address changed", event->InterfaceIpAddressChanged.Type == chip::DeviceLayer::InterfaceIpChangeType::kIpV4_Assigned ? "IPv4" : "IPV6" "Interface %s Address changed", event->InterfaceIpAddressChanged.Type == chip::DeviceLayer::InterfaceIpChangeType::kIpV4_Assigned ? "IPv4" : "IPV6"
); );
break; break;
case chip::DeviceLayer::DeviceEventType::kCommissioningComplete: log_i("Commissioning complete"); break; case chip::DeviceLayer::DeviceEventType::kCommissioningComplete: log_d("Commissioning complete"); break;
case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired: log_i("Commissioning failed, fail safe timer expired"); break; case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired: log_d("Commissioning failed, fail safe timer expired"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStarted: log_i("Commissioning session started"); break; case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStarted: log_d("Commissioning session started"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped: log_i("Commissioning session stopped"); break; case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped: log_d("Commissioning session stopped"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened: log_i("Commissioning window opened"); break; case chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened: log_d("Commissioning window opened"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed: log_i("Commissioning window closed"); break; case chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed: log_d("Commissioning window closed"); break;
case chip::DeviceLayer::DeviceEventType::kFabricRemoved: case chip::DeviceLayer::DeviceEventType::kFabricRemoved:
{ {
log_i("Fabric removed successfully"); log_d("Fabric removed successfully");
if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0) { 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(); chip::CommissioningWindowManager &commissionMgr = chip::Server::GetInstance().GetCommissioningWindowManager();
constexpr auto kTimeoutSeconds = chip::System::Clock::Seconds16(k_timeout_seconds); constexpr auto kTimeoutSeconds = chip::System::Clock::Seconds16(k_timeout_seconds);
if (!commissionMgr.IsCommissioningWindowOpen()) { if (!commissionMgr.IsCommissioningWindowOpen()) {
@ -116,12 +117,16 @@ static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) {
} }
break; break;
} }
case chip::DeviceLayer::DeviceEventType::kFabricWillBeRemoved: log_i("Fabric will be removed"); break; case chip::DeviceLayer::DeviceEventType::kFabricWillBeRemoved: log_d("Fabric will be removed"); break;
case chip::DeviceLayer::DeviceEventType::kFabricUpdated: log_i("Fabric is updated"); break; case chip::DeviceLayer::DeviceEventType::kFabricUpdated: log_d("Fabric is updated"); break;
case chip::DeviceLayer::DeviceEventType::kFabricCommitted: log_i("Fabric is committed"); break; case chip::DeviceLayer::DeviceEventType::kFabricCommitted: log_d("Fabric is committed"); break;
case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized: log_i("BLE deinitialized and memory reclaimed"); break; case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized: log_d("BLE deinitialized and memory reclaimed"); break;
default: 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() { void ArduinoMatter::_init() {

View file

@ -34,10 +34,136 @@
#include <MatterEndpoints/MatterOnOffPlugin.h> #include <MatterEndpoints/MatterOnOffPlugin.h>
#include <MatterEndpoints/MatterThermostat.h> #include <MatterEndpoints/MatterThermostat.h>
// Matter Event types used when there is a user callback for Matter Events
enum matterEvent_t {
// Starting from 0x8000, these events are public and can be used by applications.
// Defined in CHIPDeviceEvent.h
// WiFi Connectivity Change: Signals a change in connectivity of the device's WiFi station interface.
MATTER_WIFI_CONNECTIVITY_CHANGE = (uint16_t)chip::DeviceLayer::DeviceEventType::kWiFiConnectivityChange,
// Thread Connectivity Change: Signals a change in connectivity of the device's Thread interface.
MATTER_THREAD_CONNECTIVITY_CHANGE = (uint16_t)chip::DeviceLayer::DeviceEventType::kThreadConnectivityChange,
// Internet Connectivity Change: Signals a change in the device's ability to communicate via the Internet.
MATTER_INTERNET_CONNECTIVITY_CHANGE = (uint16_t)chip::DeviceLayer::DeviceEventType::kInternetConnectivityChange,
// Service Connectivity Change: Signals a change in the device's ability to communicate with a chip-enabled service.
MATTER_SERVICE_CONNECTIVITY_CHANGE = (uint16_t)chip::DeviceLayer::DeviceEventType::kServiceConnectivityChange,
// Service Provisioning Change: Signals a change to the device's service provisioning state.
MATTER_SERVICE_PROVISIONING_CHANGE = (uint16_t)chip::DeviceLayer::DeviceEventType::kServiceProvisioningChange,
// Time Sync Change: Signals a change to the device's real time clock synchronization state.
MATTER_TIME_SYNC_CHANGE = (uint16_t)chip::DeviceLayer::DeviceEventType::kTimeSyncChange,
// CHIPoBLE Connection Established: Signals that an external entity has established a new
// CHIPoBLE connection with the device.
MATTER_CHIPOBLE_CONNECTION_ESTABLISHED = (uint16_t)chip::DeviceLayer::DeviceEventType::kCHIPoBLEConnectionEstablished,
// CHIPoBLE Connection Closed: Signals that an external entity has closed existing CHIPoBLE
// connection with the device.
MATTER_CHIPOBLE_CONNECTION_CLOSED = (uint16_t)chip::DeviceLayer::DeviceEventType::kCHIPoBLEConnectionClosed,
// Request BLE connections to be closed. This is used in the supportsConcurrentConnection = False case.
MATTER_CLOSE_ALL_BLE_CONNECTIONS = (uint16_t)chip::DeviceLayer::DeviceEventType::kCloseAllBleConnections,
// WiFi Device Available: When supportsConcurrentConnection = False, the ConnectNetwork
// command cannot start until the BLE device is closed and the Operation Network device (e.g. WiFi) has been started.
MATTER_WIFI_DEVICE_AVAILABLE = (uint16_t)chip::DeviceLayer::DeviceEventType::kWiFiDeviceAvailable,
MATTER_OPERATIONAL_NETWORK_STARTED = (uint16_t)chip::DeviceLayer::DeviceEventType::kOperationalNetworkStarted,
// Thread State Change: Signals that a state change has occurred in the Thread stack.
MATTER_THREAD_STATE_CHANGE = (uint16_t)chip::DeviceLayer::DeviceEventType::kThreadStateChange,
// Thread Interface State Change: Signals that the state of the Thread network interface has changed.
MATTER_THREAD_INTERFACE_STATE_CHANGE = (uint16_t)chip::DeviceLayer::DeviceEventType::kThreadInterfaceStateChange,
// CHIPoBLE Advertising Change: Signals that the state of CHIPoBLE advertising has changed.
MATTER_CHIPOBLE_ADVERTISING_CHANGE = (uint16_t)chip::DeviceLayer::DeviceEventType::kCHIPoBLEAdvertisingChange,
// Interface IP Address Changed: IP address availability - either ipv4 or ipv6
// addresses assigned to the underlying wifi/ethernet interface.
MATTER_INTERFACE_IP_ADDRESS_CHANGED = (uint16_t)chip::DeviceLayer::DeviceEventType::kInterfaceIpAddressChanged,
// Commissioning Complete: Commissioning has completed by a call to the general
// commissioning cluster command.
MATTER_COMMISSIONING_COMPLETE = (uint16_t)chip::DeviceLayer::DeviceEventType::kCommissioningComplete,
// Fail Safe Timer Expired: Signals that the fail-safe timer expired before
// the CommissioningComplete command was successfully invoked.
MATTER_FAIL_SAFE_TIMER_EXPIRED = (uint16_t)chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired,
// Operational Network Enabled.
MATTER_OPERATIONAL_NETWORK_ENABLED = (uint16_t)chip::DeviceLayer::DeviceEventType::kOperationalNetworkEnabled,
// DNS-SD Initialized: Signals that DNS-SD has been initialized and is ready to operate.
MATTER_DNSSD_INITIALIZED = (uint16_t)chip::DeviceLayer::DeviceEventType::kDnssdInitialized,
// DNS-SD Restart Needed: Signals that DNS-SD backend was restarted and services must be published again.
MATTER_DNSSD_RESTART_NEEDED = (uint16_t)chip::DeviceLayer::DeviceEventType::kDnssdRestartNeeded,
// Bindings Changed Via Cluster: Signals that bindings were updated.
MATTER_BINDINGS_CHANGED_VIA_CLUSTER = (uint16_t)chip::DeviceLayer::DeviceEventType::kBindingsChangedViaCluster,
// OTA State Changed: Signals that the state of the OTA engine changed.
MATTER_OTA_STATE_CHANGED = (uint16_t)chip::DeviceLayer::DeviceEventType::kOtaStateChanged,
// Server Ready: Server initialization has completed. Signals that all server components have been initialized
// and the node is ready to establish connections with other nodes. This event can be used to trigger on-boot actions
// that require sending messages to other nodes.
MATTER_SERVER_READY = (uint16_t)chip::DeviceLayer::DeviceEventType::kServerReady,
// BLE Deinitialized: Signals that BLE stack is deinitialized and memory reclaimed
MATTER_BLE_DEINITIALIZED = (uint16_t)chip::DeviceLayer::DeviceEventType::kBLEDeinitialized,
// Starting ESP32 Platform Specific Events from 0x9000
MATTER_ESP32_SPECIFIC_EVENT, // value is previous + 1
// Commissioning Session Started: Signals that Commissioning session has started
MATTER_COMMISSIONING_SESSION_STARTED = (uint16_t)chip::DeviceLayer::DeviceEventType::kCommissioningSessionStarted,
// Commissioning Session Stopped: Signals that Commissioning session has stopped
MATTER_COMMISSIONING_SESSION_STOPPED = (uint16_t)chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped,
// Commissioning Window Opened: Signals that Commissioning window is now opened
MATTER_COMMISSIONING_WINDOW_OPEN = (uint16_t)chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened,
// Commissioning Window Closed: Signals that Commissioning window is now closed
MATTER_COMMISSIONING_WINDOW_CLOSED = (uint16_t)chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed,
// Fabric Will Be Removed: Signals that a fabric is about to be deleted. This allows actions to be taken that need the
// fabric to still be around before we delete it
MATTER_FABRIC_WILL_BE_REMOVED = (uint16_t)chip::DeviceLayer::DeviceEventType::kFabricWillBeRemoved,
// Fabric Has Been Removed: Signals that a fabric is effectively deleted
MATTER_FABRIC_REMOVED = (uint16_t)chip::DeviceLayer::DeviceEventType::kFabricRemoved,
// Fabric Has Been Committed: Signals that a fabric in Fabric Table is persisted to storage, by CommitPendingFabricData
MATTER_FABRIC_COMMITTED = (uint16_t)chip::DeviceLayer::DeviceEventType::kFabricCommitted,
// Fabric Has Been Updated: Signals that operational credentials are changed, which may not be persistent.
// Can be used to affect what is needed for UpdateNOC prior to commit
MATTER_FABRIC_UPDATED = (uint16_t)chip::DeviceLayer::DeviceEventType::kFabricUpdated,
// ESP32 Matter Events: These are custom ESP32 Matter events as defined in CHIPDevicePlatformEvent.h.
MATTER_ESP32_PUBLIC_SPECIFIC_EVENT = (uint16_t)chip::DeviceLayer::DeviceEventType::kRange_PublicPlatformSpecific, // ESPSystemEvent
};
using namespace esp_matter; using namespace esp_matter;
class ArduinoMatter { class ArduinoMatter {
public: public:
// Matter Event Callback type
using matterEventCB = std::function<void(matterEvent_t, const chip::DeviceLayer::ChipDeviceEvent *)>;
// Matter Event Callback
static matterEventCB _matterEventCB;
// set the Matter Event Callback
static void onEvent(matterEventCB cb) {
_matterEventCB = cb;
}
static inline String getManualPairingCode() { static inline String getManualPairingCode() {
// return the pairing code for manual pairing // return the pairing code for manual pairing
return String("34970112332"); return String("34970112332");

View file

@ -41,22 +41,22 @@ public:
esp_matter::attribute_t *getAttribute(uint32_t cluster_id, uint32_t attribute_id) { esp_matter::attribute_t *getAttribute(uint32_t cluster_id, uint32_t attribute_id) {
if (endpoint_id == 0) { if (endpoint_id == 0) {
log_e("Endpoint ID is not set"); log_e("Endpoint ID is not set");
return NULL; return nullptr;
} }
endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
if (endpoint == NULL) { if (endpoint == nullptr) {
log_e("Endpoint [%d] not found", endpoint_id); log_e("Endpoint [%d] not found", endpoint_id);
return NULL; return nullptr;
} }
cluster_t *cluster = cluster::get(endpoint, cluster_id); cluster_t *cluster = cluster::get(endpoint, cluster_id);
if (cluster == NULL) { if (cluster == nullptr) {
log_e("Cluster [%d] not found", cluster_id); log_e("Cluster [%d] not found", cluster_id);
return NULL; return nullptr;
} }
esp_matter::attribute_t *attribute = attribute::get(cluster, attribute_id); esp_matter::attribute_t *attribute = attribute::get(cluster, attribute_id);
if (attribute == NULL) { if (attribute == nullptr) {
log_e("Attribute [%d] not found", attribute_id); log_e("Attribute [%d] not found", attribute_id);
return NULL; return nullptr;
} }
return attribute; return attribute;
} }
@ -64,7 +64,7 @@ public:
// get the value of an attribute from its cluster id and attribute it // get the value of an attribute from its cluster id and attribute it
bool getAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) { bool getAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) {
esp_matter::attribute_t *attribute = getAttribute(cluster_id, attribute_id); esp_matter::attribute_t *attribute = getAttribute(cluster_id, attribute_id);
if (attribute == NULL) { if (attribute == nullptr) {
return false; return false;
} }
if (attribute::get_val(attribute, attrVal) == ESP_OK) { if (attribute::get_val(attribute, attrVal) == ESP_OK) {
@ -78,7 +78,7 @@ public:
// set the value of an attribute from its cluster id and attribute it // set the value of an attribute from its cluster id and attribute it
bool setAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) { bool setAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) {
esp_matter::attribute_t *attribute = getAttribute(cluster_id, attribute_id); esp_matter::attribute_t *attribute = getAttribute(cluster_id, attribute_id);
if (attribute == NULL) { if (attribute == nullptr) {
return false; return false;
} }
if (attribute::set_val(attribute, attrVal) == ESP_OK) { if (attribute::set_val(attribute, attrVal) == ESP_OK) {
@ -117,6 +117,6 @@ public:
protected: protected:
uint16_t endpoint_id = 0; uint16_t endpoint_id = 0;
EndPointIdentifyCB _onEndPointIdentifyCB = NULL; EndPointIdentifyCB _onEndPointIdentifyCB = nullptr;
}; };
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ #endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */