From 6f56df2a09a17213823e2edda3cdff3db32cadaf Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Wed, 4 Jun 2025 10:19:44 -0300 Subject: [PATCH 01/42] feat(codeowners): Initial CODEOWNERS setup (#11397) * feat(codeowners): Initial CODEOWNERS setup * fix(comment): Improve comment * fix(codeowners): Add teams * fix(codeowners): Apply suggestions * fix(codeowners): Add missing libraries --- .github/CODEOWNERS | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000000..75a2b46d619 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,79 @@ +# CODEOWNERS for ESP32 Arduino Core + +# This file is used to specify the code owners for the ESP32 Arduino Core. +# Read more about CODEOWNERS: +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners +# Note that order matters. The last matching pattern will be used. + +# The default owners are the active developers of the ESP32 Arduino Core. +# Refrain from using @espressif/arduino-esp32 to avoid spamming non-developers with review requests. +* @espressif/arduino-devs + +# CI +/.github/ @lucasssvaz @me-no-dev @P-R-O-C-H-Y +/tests/ @lucasssvaz @P-R-O-C-H-Y + +# Tools +/tools/ @me-no-dev +/tools/pre-commit/ @lucasssvaz +/tools/add_lib.sh @P-R-O-C-H-Y + +# Pre-commit +/.* @lucasssvaz # Files in root directory that start with a dot. + +# Git Files +/.gitignore @espressif/arduino-devs +/.gitmodules @espressif/arduino-devs + +# Documentation +/docs/ @pedrominatel +/.github/ISSUE_TEMPLATE/ @pedrominatel +/.github/PULL_REQUEST_TEMPLATE.md @pedrominatel +/.readthedocs.yaml @pedrominatel +/*.md @pedrominatel + +# Boards +/variants/ @P-R-O-C-H-Y +/boards.txt @P-R-O-C-H-Y + +# Arduino as Component +/idf_component_examples/ @SuGlider +/idf_component.yml @SuGlider @me-no-dev +/CMakeLists.txt @SuGlider @me-no-dev +/Kconfig.projbuild @SuGlider @me-no-dev + +# Build System +/package.json @me-no-dev +/platform.txt @me-no-dev +/programmers.txt @me-no-dev +/package/ @me-no-dev + +# Libraries +/libraries/ArduinoOTA/ @me-no-dev +/libraries/AsyncUDP/ @me-no-dev +/libraries/BLE/ @lucasssvaz @SuGlider +/libraries/ESP_I2S/ @me-no-dev +/libraries/ESP_NOW/ @P-R-O-C-H-Y @lucasssvaz +/libraries/ESP_SR/ @me-no-dev +/libraries/ESPmDNS/ @me-no-dev +/libraries/Ethernet/ @me-no-dev +/libraries/Matter/ @SuGlider +/libraries/NetBIOS/ @me-no-dev +/libraries/Network/ @me-no-dev +/libraries/OpenThread/ @SuGlider +/libraries/PPP/ @me-no-dev +/libraries/SPI/ @me-no-dev +/libraries/Update/ @me-no-dev +/libraries/USB/ @SuGlider @me-no-dev +/libraries/WiFi/ @me-no-dev +/libraries/WiFiProv/ @me-no-dev +/libraries/Wire/ @me-no-dev +/libraries/Zigbee/ @P-R-O-C-H-Y + +# CI JSON +# Keep this after other libraries and tests to avoid being overridden. +**/ci.json @lucasssvaz + +# The CODEOWNERS file should be owned by the developers of the ESP32 Arduino Core. +# Leave this entry as the last one to avoid being overridden. +/.github/CODEOWNERS @espressif/arduino-devs From b5c5655cf004858ed56f3488f006ebad3690c303 Mon Sep 17 00:00:00 2001 From: Kevin Sidwar Date: Wed, 4 Jun 2025 08:39:19 -0600 Subject: [PATCH 02/42] Support HTTP 204 (#11408) HTTP 204 is a successful return code which indicates No Content. While it's appropriate to return a 304 if the server has content for a device but it hasn't change, it is more accurate for a server to return a 204 if it simply doesn't have any firmware files for a particular device. Co-authored-by: Me No Dev --- libraries/HTTPUpdate/src/HTTPUpdate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/HTTPUpdate/src/HTTPUpdate.cpp b/libraries/HTTPUpdate/src/HTTPUpdate.cpp index 1eda0a4f7e5..b463ffe2e83 100644 --- a/libraries/HTTPUpdate/src/HTTPUpdate.cpp +++ b/libraries/HTTPUpdate/src/HTTPUpdate.cpp @@ -356,6 +356,7 @@ HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient &http, const String ¤ log_e("Content-Length was 0 or wasn't set by Server?!\n"); } break; + case HTTP_CODE_NO_CONTENT: case HTTP_CODE_NOT_MODIFIED: ///< Not Modified (No updates) ret = HTTP_UPDATE_NO_UPDATES; From a2880a4c17038b4ff3dfa083e7af7c7bbdba914a Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Wed, 4 Jun 2025 17:39:34 +0300 Subject: [PATCH 03/42] feat(ap): Add support for DHCP Captive Portal (opt 114) (#11412) * feat(ap): Add support for DHCP Captive Portal (opt 114) * feat(ap): No need to guard the function * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../examples/CaptivePortal/CaptivePortal.ino | 5 ++- libraries/WiFi/src/AP.cpp | 39 +++++++++++++++++++ libraries/WiFi/src/WiFiAP.h | 1 + 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino b/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino index d956dc14ad3..7759aa09a1c 100644 --- a/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino +++ b/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino @@ -34,8 +34,9 @@ void handleNotFound() { void setup() { Serial.begin(115200); - WiFi.mode(WIFI_AP); - WiFi.softAP("ESP32-DNSServer"); + WiFi.AP.begin(); + WiFi.AP.create("ESP32-DNSServer"); + WiFi.AP.enableDhcpCaptivePortal(); // by default DNSServer is started serving any "*" domain name. It will reply // AccessPoint's IP to all DNS request (this is required for Captive Portal detection) diff --git a/libraries/WiFi/src/AP.cpp b/libraries/WiFi/src/AP.cpp index 0e7839764ea..a649c3898cb 100644 --- a/libraries/WiFi/src/AP.cpp +++ b/libraries/WiFi/src/AP.cpp @@ -305,6 +305,45 @@ bool APClass::enableNAPT(bool enable) { return true; } +bool APClass::enableDhcpCaptivePortal() { + esp_err_t err = ESP_OK; + static char captiveportal_uri[32] = { + 0, + }; + + if (!started()) { + log_e("AP must be first started to enable DHCP Captive Portal"); + return false; + } + + // Create Captive Portal URL: http://192.168.0.4 + strcpy(captiveportal_uri, "http://"); + strcat(captiveportal_uri, String(localIP()).c_str()); + + // Stop DHCPS + err = esp_netif_dhcps_stop(_esp_netif); + if (err && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) { + log_e("DHCPS Stop Failed! 0x%04x: %s", err, esp_err_to_name(err)); + return false; + } + + // Enable DHCP Captive Portal + err = esp_netif_dhcps_option(_esp_netif, ESP_NETIF_OP_SET, ESP_NETIF_CAPTIVEPORTAL_URI, captiveportal_uri, strlen(captiveportal_uri)); + if (err) { + log_e("Could not set enable DHCP Captive Portal! 0x%x: %s", err, esp_err_to_name(err)); + return false; + } + + // Start DHCPS + err = esp_netif_dhcps_start(_esp_netif); + if (err) { + log_e("DHCPS Start Failed! 0x%04x: %s", err, esp_err_to_name(err)); + return false; + } + + return true; +} + String APClass::SSID(void) const { if (!started()) { return String(); diff --git a/libraries/WiFi/src/WiFiAP.h b/libraries/WiFi/src/WiFiAP.h index 540ec87f44f..2b7ce469801 100644 --- a/libraries/WiFi/src/WiFiAP.h +++ b/libraries/WiFi/src/WiFiAP.h @@ -53,6 +53,7 @@ class APClass : public NetworkInterface { bool bandwidth(wifi_bandwidth_t bandwidth); bool enableNAPT(bool enable = true); + bool enableDhcpCaptivePortal(); String SSID(void) const; uint8_t stationCount(); From 460b89201b0be121c4345549551bcf102c26f165 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 4 Jun 2025 16:39:54 +0200 Subject: [PATCH 04/42] make BT core code execution conditional from include esp_bt.h (#11413) * make code execution conditional from include esp_bt.h.h * only one if * Update esp32-hal-bt.c --- cores/esp32/esp32-hal-bt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-bt.c b/cores/esp32/esp32-hal-bt.c index 1e5f73e324c..5d512d448a3 100644 --- a/cores/esp32/esp32-hal-bt.c +++ b/cores/esp32/esp32-hal-bt.c @@ -15,7 +15,7 @@ #include "esp32-hal-bt.h" #if SOC_BT_SUPPORTED -#ifdef CONFIG_BT_BLUEDROID_ENABLED +#if defined(CONFIG_BT_BLUEDROID_ENABLED) && __has_include("esp_bt.h") #if CONFIG_IDF_TARGET_ESP32 bool btInUse() { From 375f2c002de0d5ace08bac59a4e42134be1af22e Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 4 Jun 2025 16:40:12 +0200 Subject: [PATCH 05/42] C2: Disable network provisioning (#11423) --- idf_component.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/idf_component.yml b/idf_component.yml index 967c4ecf0f6..7f090cb26a0 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -68,6 +68,8 @@ dependencies: # RainMaker Start (Fixed versions, because Matter supports only Insights 1.0.1) espressif/network_provisioning: version: "1.0.2" + rules: + - if: "target != esp32c2" espressif/esp_rainmaker: version: "1.5.2" rules: From cae66e65a30d5a3d7c15b85ffa4130fa74e01b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Wed, 4 Jun 2025 16:40:28 +0200 Subject: [PATCH 06/42] feat(zigbee): Add endpoint identification in read handlers + command structures fix (#11425) * feat(zigbee): Add endpoint identification in read handlers * fix(zigbee): initialize Zigbee command structures with zeros * fix(zigbee): Spelling correction * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../Zigbee_Thermostat/Zigbee_Thermostat.ino | 56 +++- libraries/Zigbee/keywords.txt | 3 +- libraries/Zigbee/src/ZigbeeEP.cpp | 20 +- libraries/Zigbee/src/ZigbeeEP.h | 4 +- libraries/Zigbee/src/ZigbeeHandlers.cpp | 8 +- .../Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp | 48 +-- libraries/Zigbee/src/ep/ZigbeeSwitch.cpp | 32 +- libraries/Zigbee/src/ep/ZigbeeThermostat.cpp | 279 +++++++++++++++++- libraries/Zigbee/src/ep/ZigbeeThermostat.h | 29 +- 9 files changed, 390 insertions(+), 89 deletions(-) diff --git a/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino b/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino index 7cdf45ef711..6f5934f791d 100644 --- a/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino +++ b/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino @@ -34,7 +34,8 @@ #include "Zigbee.h" /* Zigbee thermostat configuration */ -#define THERMOSTAT_ENDPOINT_NUMBER 5 +#define THERMOSTAT_ENDPOINT_NUMBER 1 +#define USE_RECEIVE_TEMP_WITH_SOURCE 1 uint8_t button = BOOT_PIN; ZigbeeThermostat zbThermostat = ZigbeeThermostat(THERMOSTAT_ENDPOINT_NUMBER); @@ -48,13 +49,28 @@ float sensor_tolerance; struct tm timeinfo = {}; // Time structure for Time cluster /****************** Temperature sensor handling *******************/ -void recieveSensorTemp(float temperature) { +#if USE_RECEIVE_TEMP_WITH_SOURCE == 0 +void receiveSensorTemp(float temperature) { Serial.printf("Temperature sensor value: %.2f°C\n", temperature); sensor_temp = temperature; } +#else +void receiveSensorTempWithSource(float temperature, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) { + if (src_address.addr_type == ESP_ZB_ZCL_ADDR_TYPE_SHORT) { + Serial.printf("Temperature sensor value: %.2f°C from endpoint %d, address 0x%04x\n", temperature, src_endpoint, src_address.u.short_addr); + } else { + Serial.printf( + "Temperature sensor value: %.2f°C from endpoint %d, address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", temperature, src_endpoint, + src_address.u.ieee_addr[7], src_address.u.ieee_addr[6], src_address.u.ieee_addr[5], src_address.u.ieee_addr[4], src_address.u.ieee_addr[3], + src_address.u.ieee_addr[2], src_address.u.ieee_addr[1], src_address.u.ieee_addr[0] + ); + } + sensor_temp = temperature; +} +#endif -void recieveSensorConfig(float min_temp, float max_temp, float tolerance) { - Serial.printf("Temperature sensor settings: min %.2f°C, max %.2f°C, tolerance %.2f°C\n", min_temp, max_temp, tolerance); +void receiveSensorConfig(float min_temp, float max_temp, float tolerance) { + Serial.printf("Temperature sensor config: min %.2f°C, max %.2f°C, tolerance %.2f°C\n", min_temp, max_temp, tolerance); sensor_min_temp = min_temp; sensor_max_temp = max_temp; sensor_tolerance = tolerance; @@ -66,9 +82,15 @@ void setup() { // Init button switch pinMode(button, INPUT_PULLUP); - // Set callback functions for temperature and configuration receive - zbThermostat.onTempRecieve(recieveSensorTemp); - zbThermostat.onConfigRecieve(recieveSensorConfig); +// Set callback function for receiving temperature from sensor - Use only one option +#if USE_RECEIVE_TEMP_WITH_SOURCE == 0 + zbThermostat.onTempReceive(receiveSensorTemp); // If you bound only one sensor or you don't need to know the source of the temperature +#else + zbThermostat.onTempReceiveWithSource(receiveSensorTempWithSource); +#endif + + // Set callback function for receiving sensor configuration + zbThermostat.onConfigReceive(receiveSensorConfig); //Optional: set Zigbee device name and model zbThermostat.setManufacturerAndModel("Espressif", "ZigbeeThermostat"); @@ -107,19 +129,30 @@ void setup() { Serial.println(); - // Get temperature sensor configuration - zbThermostat.getSensorSettings(); + // Get temperature sensor configuration for all bound sensors by endpoint number and address + std::list boundSensors = zbThermostat.getBoundDevices(); + for (const auto &device : boundSensors) { + Serial.println("--------------------------------"); + if (device->short_addr == 0x0000 || device->short_addr == 0xFFFF) { //End devices never have 0x0000 short address or 0xFFFF group address + Serial.printf( + "Device on endpoint %d, IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", device->endpoint, device->ieee_addr[7], device->ieee_addr[6], + device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0] + ); + zbThermostat.getSensorSettings(device->endpoint, device->ieee_addr); + } else { + Serial.printf("Device on endpoint %d, short address: 0x%x\r\n", device->endpoint, device->short_addr); + zbThermostat.getSensorSettings(device->endpoint, device->short_addr); + } + } } void loop() { // Handle button switch in loop() if (digitalRead(button) == LOW) { // Push button pressed - // Key debounce handling while (digitalRead(button) == LOW) { delay(50); } - // Set reporting interval for temperature sensor zbThermostat.setTemperatureReporting(0, 10, 2); } @@ -130,5 +163,6 @@ void loop() { last_print = millis(); int temp_percent = (int)((sensor_temp - sensor_min_temp) / (sensor_max_temp - sensor_min_temp) * 100); Serial.printf("Loop temperature info: %.2f°C (%d %%)\n", sensor_temp, temp_percent); + zbThermostat.printBoundDevices(Serial); } } diff --git a/libraries/Zigbee/keywords.txt b/libraries/Zigbee/keywords.txt index 4d4cd7d0606..5ce1e8f6f51 100644 --- a/libraries/Zigbee/keywords.txt +++ b/libraries/Zigbee/keywords.txt @@ -107,6 +107,7 @@ setLightColor KEYWORD2 # ZigbeeThermostat onTempRecieve KEYWORD2 onConfigRecieve KEYWORD2 +onTempReceiveWithSource KEYWORD2 getTemperature KEYWORD2 getSensorSettings KEYWORD2 setTemperatureReporting KEYWORD2 @@ -191,4 +192,4 @@ ZIGBEE_DEFAULT_COORDINATOR_CONFIG LITERAL1 ZIGBEE_DEFAULT_RADIO_CONFIG LITERAL1 ZIGBEE_DEFAULT_UART_RCP_RADIO_CONFIG LITERAL1 ZIGBEE_DEFAULT_HOST_CONFIG LITERAL1 -ZB_ARRAY_LENTH LITERAL1 +ZB_ARRAY_LENGHT LITERAL1 diff --git a/libraries/Zigbee/src/ZigbeeEP.cpp b/libraries/Zigbee/src/ZigbeeEP.cpp index 1d3df126ce8..efddbdd0368 100644 --- a/libraries/Zigbee/src/ZigbeeEP.cpp +++ b/libraries/Zigbee/src/ZigbeeEP.cpp @@ -145,7 +145,7 @@ bool ZigbeeEP::setBatteryVoltage(uint8_t voltage) { bool ZigbeeEP::reportBatteryPercentage() { /* Send report attributes command */ - esp_zb_zcl_report_attr_cmd_t report_attr_cmd; + esp_zb_zcl_report_attr_cmd_t report_attr_cmd = {0}; report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID; report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; @@ -166,7 +166,7 @@ bool ZigbeeEP::reportBatteryPercentage() { char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr) { /* Read peer Manufacture Name & Model Identifier */ - esp_zb_zcl_read_attr_cmd_t read_req; + esp_zb_zcl_read_attr_cmd_t read_req = {0}; if (short_addr != 0) { read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; @@ -183,7 +183,7 @@ char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_i uint16_t attributes[] = { ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, }; - read_req.attr_number = ZB_ARRAY_LENTH(attributes); + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); read_req.attr_field = attributes; if (_read_manufacturer != NULL) { @@ -204,7 +204,7 @@ char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_i char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr) { /* Read peer Manufacture Name & Model Identifier */ - esp_zb_zcl_read_attr_cmd_t read_req; + esp_zb_zcl_read_attr_cmd_t read_req = {0}; if (short_addr != 0) { read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; @@ -221,7 +221,7 @@ char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_add uint16_t attributes[] = { ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, }; - read_req.attr_number = ZB_ARRAY_LENTH(attributes); + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); read_req.attr_field = attributes; if (_read_model != NULL) { @@ -375,7 +375,7 @@ bool ZigbeeEP::setTimezone(int32_t gmt_offset) { tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) { /* Read peer time */ - esp_zb_zcl_read_attr_cmd_t read_req; + esp_zb_zcl_read_attr_cmd_t read_req = {0}; if (short_addr >= 0) { read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; @@ -386,7 +386,7 @@ tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ie } uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ID}; - read_req.attr_number = ZB_ARRAY_LENTH(attributes); + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); read_req.attr_field = attributes; read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME; @@ -427,7 +427,7 @@ tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ie int32_t ZigbeeEP::getTimezone(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) { /* Read peer timezone */ - esp_zb_zcl_read_attr_cmd_t read_req; + esp_zb_zcl_read_attr_cmd_t read_req = {0}; if (short_addr >= 0) { read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; @@ -438,7 +438,7 @@ int32_t ZigbeeEP::getTimezone(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_ } uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID}; - read_req.attr_number = ZB_ARRAY_LENTH(attributes); + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); read_req.attr_field = attributes; read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME; @@ -543,7 +543,7 @@ static void findOTAServer(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t } void ZigbeeEP::requestOTAUpdate() { - esp_zb_zdo_match_desc_req_param_t req; + esp_zb_zdo_match_desc_req_param_t req = {0}; uint16_t cluster_list[] = {ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE}; /* Match the OTA server of coordinator */ diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index fe22f31faaf..a3217cbd066 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -12,7 +12,7 @@ #define ZB_CMD_TIMEOUT 10000 // 10 seconds #define OTA_UPGRADE_QUERY_INTERVAL (1 * 60) // 1 hour = 60 minutes -#define ZB_ARRAY_LENTH(arr) (sizeof(arr) / sizeof(arr[0])) +#define ZB_ARRAY_LENGHT(arr) (sizeof(arr) / sizeof(arr[0])) #define RGB_TO_XYZ(r, g, b, X, Y, Z) \ { \ @@ -131,7 +131,7 @@ class ZigbeeEP { // list of all handlers function calls, to be override by EPs implementation virtual void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) {}; - virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) {}; + virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {}; virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message); virtual void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) {}; diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index eeeb1e8013a..5d54e459058 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -108,7 +108,9 @@ static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_mes // List through all Zigbee EPs and call the callback function, with the message for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { if (message->dst_endpoint == (*it)->getEndpoint()) { - (*it)->zbAttributeRead(message->cluster, &message->attribute); //method zbAttributeRead must be implemented in specific EP class + (*it)->zbAttributeRead( + message->cluster, &message->attribute, message->src_endpoint, message->src_address + ); //method zbAttributeRead must be implemented in specific EP class } } return ESP_OK; @@ -142,7 +144,9 @@ static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_re } else if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_TIME) { (*it)->zbReadTimeCluster(&variable->attribute); //method zbReadTimeCluster implemented in the common EP class } else { - (*it)->zbAttributeRead(message->info.cluster, &variable->attribute); //method zbAttributeRead must be implemented in specific EP class + (*it)->zbAttributeRead( + message->info.cluster, &variable->attribute, message->info.src_endpoint, message->info.src_address + ); //method zbAttributeRead must be implemented in specific EP class } } variable = variable->next; diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp index 935d638324f..f795ed1a3c8 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp @@ -53,7 +53,7 @@ void ZigbeeColorDimmerSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t ad ZigbeeColorDimmerSwitch *instance = static_cast(user_ctx); if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) { log_d("Found light endpoint"); - esp_zb_zdo_bind_req_param_t bind_req; + esp_zb_zdo_bind_req_param_t bind_req = {0}; zb_device_params_t *light = (zb_device_params_t *)malloc(sizeof(zb_device_params_t)); light->endpoint = endpoint; light->short_addr = addr; @@ -97,7 +97,7 @@ void ZigbeeColorDimmerSwitch::findEndpoint(esp_zb_zdo_match_desc_req_param_t *cm // Methods to control the light void ZigbeeColorDimmerSwitch::lightToggle() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_TOGGLE_ID; @@ -112,7 +112,7 @@ void ZigbeeColorDimmerSwitch::lightToggle() { void ZigbeeColorDimmerSwitch::lightToggle(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -128,7 +128,7 @@ void ZigbeeColorDimmerSwitch::lightToggle(uint16_t group_addr) { void ZigbeeColorDimmerSwitch::lightToggle(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -145,7 +145,7 @@ void ZigbeeColorDimmerSwitch::lightToggle(uint8_t endpoint, uint16_t short_addr) void ZigbeeColorDimmerSwitch::lightToggle(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -165,7 +165,7 @@ void ZigbeeColorDimmerSwitch::lightToggle(uint8_t endpoint, esp_zb_ieee_addr_t i void ZigbeeColorDimmerSwitch::lightOn() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_ON_ID; @@ -180,7 +180,7 @@ void ZigbeeColorDimmerSwitch::lightOn() { void ZigbeeColorDimmerSwitch::lightOn(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -196,7 +196,7 @@ void ZigbeeColorDimmerSwitch::lightOn(uint16_t group_addr) { void ZigbeeColorDimmerSwitch::lightOn(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -213,7 +213,7 @@ void ZigbeeColorDimmerSwitch::lightOn(uint8_t endpoint, uint16_t short_addr) { void ZigbeeColorDimmerSwitch::lightOn(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -233,7 +233,7 @@ void ZigbeeColorDimmerSwitch::lightOn(uint8_t endpoint, esp_zb_ieee_addr_t ieee_ void ZigbeeColorDimmerSwitch::lightOff() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_OFF_ID; @@ -248,7 +248,7 @@ void ZigbeeColorDimmerSwitch::lightOff() { void ZigbeeColorDimmerSwitch::lightOff(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -264,7 +264,7 @@ void ZigbeeColorDimmerSwitch::lightOff(uint16_t group_addr) { void ZigbeeColorDimmerSwitch::lightOff(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -281,7 +281,7 @@ void ZigbeeColorDimmerSwitch::lightOff(uint8_t endpoint, uint16_t short_addr) { void ZigbeeColorDimmerSwitch::lightOff(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -301,7 +301,7 @@ void ZigbeeColorDimmerSwitch::lightOff(uint8_t endpoint, esp_zb_ieee_addr_t ieee void ZigbeeColorDimmerSwitch::lightOffWithEffect(uint8_t effect_id, uint8_t effect_variant) { if (_is_bound) { - esp_zb_zcl_on_off_off_with_effect_cmd_t cmd_req; + esp_zb_zcl_on_off_off_with_effect_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.effect_id = effect_id; @@ -317,7 +317,7 @@ void ZigbeeColorDimmerSwitch::lightOffWithEffect(uint8_t effect_id, uint8_t effe void ZigbeeColorDimmerSwitch::lightOnWithSceneRecall() { if (_is_bound) { - esp_zb_zcl_on_off_on_with_recall_global_scene_cmd_t cmd_req; + esp_zb_zcl_on_off_on_with_recall_global_scene_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; log_v("Sending 'light on with scene recall' command"); @@ -331,7 +331,7 @@ void ZigbeeColorDimmerSwitch::lightOnWithSceneRecall() { void ZigbeeColorDimmerSwitch::lightOnWithTimedOff(uint8_t on_off_control, uint16_t time_on, uint16_t time_off) { if (_is_bound) { - esp_zb_zcl_on_off_on_with_timed_off_cmd_t cmd_req; + esp_zb_zcl_on_off_on_with_timed_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_control = on_off_control; //TODO: Test how it works, then maybe change API @@ -348,7 +348,7 @@ void ZigbeeColorDimmerSwitch::lightOnWithTimedOff(uint8_t on_off_control, uint16 void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level) { if (_is_bound) { - esp_zb_zcl_move_to_level_cmd_t cmd_req; + esp_zb_zcl_move_to_level_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.level = level; @@ -364,7 +364,7 @@ void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level) { void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_move_to_level_cmd_t cmd_req; + esp_zb_zcl_move_to_level_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -381,7 +381,7 @@ void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint16_t group_addr) void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_move_to_level_cmd_t cmd_req; + esp_zb_zcl_move_to_level_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -399,7 +399,7 @@ void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint8_t endpoint, uin void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_move_to_level_cmd_t cmd_req; + esp_zb_zcl_move_to_level_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -422,7 +422,7 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t if (_is_bound) { espXyColor_t xy_color = espRgbToXYColor(red, green, blue); - esp_zb_zcl_color_move_to_color_cmd_t cmd_req; + esp_zb_zcl_color_move_to_color_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.color_x = xy_color.x; @@ -441,7 +441,7 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t if (_is_bound) { espXyColor_t xy_color = espRgbToXYColor(red, green, blue); - esp_zb_zcl_color_move_to_color_cmd_t cmd_req; + esp_zb_zcl_color_move_to_color_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -461,7 +461,7 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t if (_is_bound) { espXyColor_t xy_color = espRgbToXYColor(red, green, blue); - esp_zb_zcl_color_move_to_color_cmd_t cmd_req; + esp_zb_zcl_color_move_to_color_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -482,7 +482,7 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t if (_is_bound) { espXyColor_t xy_color = espRgbToXYColor(red, green, blue); - esp_zb_zcl_color_move_to_color_cmd_t cmd_req; + esp_zb_zcl_color_move_to_color_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; diff --git a/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp index 38fd7d370fb..68e7a7430cc 100644 --- a/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp @@ -52,7 +52,7 @@ void ZigbeeSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t ZigbeeSwitch *instance = static_cast(user_ctx); if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) { log_d("Found light endpoint"); - esp_zb_zdo_bind_req_param_t bind_req; + esp_zb_zdo_bind_req_param_t bind_req = {0}; zb_device_params_t *light = (zb_device_params_t *)malloc(sizeof(zb_device_params_t)); light->endpoint = endpoint; light->short_addr = addr; @@ -94,7 +94,7 @@ void ZigbeeSwitch::findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req) { // Methods to control the light void ZigbeeSwitch::lightToggle() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_TOGGLE_ID; @@ -109,7 +109,7 @@ void ZigbeeSwitch::lightToggle() { void ZigbeeSwitch::lightToggle(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -125,7 +125,7 @@ void ZigbeeSwitch::lightToggle(uint16_t group_addr) { void ZigbeeSwitch::lightToggle(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -142,7 +142,7 @@ void ZigbeeSwitch::lightToggle(uint8_t endpoint, uint16_t short_addr) { void ZigbeeSwitch::lightToggle(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -162,7 +162,7 @@ void ZigbeeSwitch::lightToggle(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { void ZigbeeSwitch::lightOn() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_ON_ID; @@ -177,7 +177,7 @@ void ZigbeeSwitch::lightOn() { void ZigbeeSwitch::lightOn(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -193,7 +193,7 @@ void ZigbeeSwitch::lightOn(uint16_t group_addr) { void ZigbeeSwitch::lightOn(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -210,7 +210,7 @@ void ZigbeeSwitch::lightOn(uint8_t endpoint, uint16_t short_addr) { void ZigbeeSwitch::lightOn(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -230,7 +230,7 @@ void ZigbeeSwitch::lightOn(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { void ZigbeeSwitch::lightOff() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_OFF_ID; @@ -245,7 +245,7 @@ void ZigbeeSwitch::lightOff() { void ZigbeeSwitch::lightOff(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -261,7 +261,7 @@ void ZigbeeSwitch::lightOff(uint16_t group_addr) { void ZigbeeSwitch::lightOff(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -278,7 +278,7 @@ void ZigbeeSwitch::lightOff(uint8_t endpoint, uint16_t short_addr) { void ZigbeeSwitch::lightOff(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req; + esp_zb_zcl_on_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -298,7 +298,7 @@ void ZigbeeSwitch::lightOff(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { void ZigbeeSwitch::lightOffWithEffect(uint8_t effect_id, uint8_t effect_variant) { if (_is_bound) { - esp_zb_zcl_on_off_off_with_effect_cmd_t cmd_req; + esp_zb_zcl_on_off_off_with_effect_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.effect_id = effect_id; @@ -314,7 +314,7 @@ void ZigbeeSwitch::lightOffWithEffect(uint8_t effect_id, uint8_t effect_variant) void ZigbeeSwitch::lightOnWithSceneRecall() { if (_is_bound) { - esp_zb_zcl_on_off_on_with_recall_global_scene_cmd_t cmd_req; + esp_zb_zcl_on_off_on_with_recall_global_scene_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; log_v("Sending 'light on with scene recall' command"); @@ -327,7 +327,7 @@ void ZigbeeSwitch::lightOnWithSceneRecall() { } void ZigbeeSwitch::lightOnWithTimedOff(uint8_t on_off_control, uint16_t time_on, uint16_t time_off) { if (_is_bound) { - esp_zb_zcl_on_off_on_with_timed_off_cmd_t cmd_req; + esp_zb_zcl_on_off_on_with_timed_off_cmd_t cmd_req = {0}; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_control = on_off_control; //TODO: Test how it works, then maybe change API diff --git a/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp b/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp index 633a60d0ff9..f3bfd7d981b 100644 --- a/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp @@ -65,7 +65,7 @@ void ZigbeeThermostat::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uin ZigbeeThermostat *instance = static_cast(user_ctx); if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) { log_i("Found temperature sensor"); - esp_zb_zdo_bind_req_param_t bind_req; + esp_zb_zdo_bind_req_param_t bind_req = {0}; /* Store the information of the remote device */ zb_device_params_t *sensor = (zb_device_params_t *)malloc(sizeof(zb_device_params_t)); sensor->endpoint = endpoint; @@ -115,31 +115,38 @@ void ZigbeeThermostat::findEndpoint(esp_zb_zdo_match_desc_req_param_t *param) { esp_zb_zdo_match_cluster(param, ZigbeeThermostat::findCbWrapper, this); } -void ZigbeeThermostat::zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) { +void ZigbeeThermostat::zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) { static uint8_t read_config = 0; if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT) { if (attribute->id == ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_S16) { int16_t value = attribute->data.value ? *(int16_t *)attribute->data.value : 0; - if (_on_temp_recieve) { - _on_temp_recieve(zb_s16_to_temperature(value)); + if (_on_temp_receive) { + _on_temp_receive(zb_s16_to_temperature(value)); + } + if (_on_temp_receive_with_source) { + _on_temp_receive_with_source(zb_s16_to_temperature(value), src_endpoint, src_address); } } if (attribute->id == ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MIN_VALUE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_S16) { int16_t min_value = attribute->data.value ? *(int16_t *)attribute->data.value : 0; _min_temp = zb_s16_to_temperature(min_value); read_config++; + log_d("Received min temperature: %.2f°C from endpoint %d", _min_temp, src_endpoint); } if (attribute->id == ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MAX_VALUE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_S16) { int16_t max_value = attribute->data.value ? *(int16_t *)attribute->data.value : 0; _max_temp = zb_s16_to_temperature(max_value); read_config++; + log_d("Received max temperature: %.2f°C from endpoint %d", _max_temp, src_endpoint); } if (attribute->id == ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_TOLERANCE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { uint16_t tolerance = attribute->data.value ? *(uint16_t *)attribute->data.value : 0; _tolerance = 1.0 * tolerance / 100; read_config++; + log_d("Received tolerance: %.2f°C from endpoint %d", _tolerance, src_endpoint); } if (read_config == 3) { + log_d("All config attributes processed"); read_config = 0; xSemaphoreGive(lock); } @@ -147,14 +154,14 @@ void ZigbeeThermostat::zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_att } void ZigbeeThermostat::getTemperature() { - /* Send "read attributes" command to the bound sensor */ - esp_zb_zcl_read_attr_cmd_t read_req; + /* Send "read attributes" command to all bound sensors */ + esp_zb_zcl_read_attr_cmd_t read_req = {0}; read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; read_req.zcl_basic_cmd.src_endpoint = _endpoint; read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID}; - read_req.attr_number = ZB_ARRAY_LENTH(attributes); + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); read_req.attr_field = attributes; log_i("Sending 'read temperature' command"); @@ -163,9 +170,68 @@ void ZigbeeThermostat::getTemperature() { esp_zb_lock_release(); } +void ZigbeeThermostat::getTemperature(uint16_t group_addr) { + /* Send "read attributes" command to the group */ + esp_zb_zcl_read_attr_cmd_t read_req = {0}; + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + + uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID}; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i("Sending 'read temperature' command to group address 0x%x", group_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); +} + +void ZigbeeThermostat::getTemperature(uint8_t endpoint, uint16_t short_addr) { + /* Send "read attributes" command to specific endpoint */ + esp_zb_zcl_read_attr_cmd_t read_req = {0}; + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + + uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID}; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i("Sending 'read temperature' command to endpoint %d, address 0x%x", endpoint, short_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); +} + +void ZigbeeThermostat::getTemperature(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { + /* Send "read attributes" command to specific endpoint */ + esp_zb_zcl_read_attr_cmd_t read_req = {0}; + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + + uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID}; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i( + "Sending 'read temperature' command to endpoint %d, ieee address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6], + ieee_addr[5], ieee_addr[4], ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0] + ); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); +} + void ZigbeeThermostat::getSensorSettings() { - /* Send "read attributes" command to the bound sensor */ - esp_zb_zcl_read_attr_cmd_t read_req; + /* Send "read attributes" command to all bound sensors */ + esp_zb_zcl_read_attr_cmd_t read_req = {0}; read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; read_req.zcl_basic_cmd.src_endpoint = _endpoint; read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; @@ -173,10 +239,102 @@ void ZigbeeThermostat::getSensorSettings() { uint16_t attributes[] = { ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MIN_VALUE_ID, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MAX_VALUE_ID, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_TOLERANCE_ID }; - read_req.attr_number = ZB_ARRAY_LENTH(attributes); + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); read_req.attr_field = attributes; - log_i("Sending 'read temperature' command"); + log_i("Sending 'read sensor settings' command"); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + + //Take semaphore to wait for response of all attributes + if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) { + log_e("Error while reading attributes"); + return; + } else { + //Call the callback function when all attributes are read + _on_config_receive(_min_temp, _max_temp, _tolerance); + } +} + +void ZigbeeThermostat::getSensorSettings(uint16_t group_addr) { + /* Send "read attributes" command to the group */ + esp_zb_zcl_read_attr_cmd_t read_req = {0}; + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + + uint16_t attributes[] = { + ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MIN_VALUE_ID, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MAX_VALUE_ID, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_TOLERANCE_ID + }; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i("Sending 'read sensor settings' command to group address 0x%x", group_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + + //Take semaphore to wait for response of all attributes + if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) { + log_e("Error while reading attributes"); + return; + } else { + //Call the callback function when all attributes are read + _on_config_receive(_min_temp, _max_temp, _tolerance); + } +} + +void ZigbeeThermostat::getSensorSettings(uint8_t endpoint, uint16_t short_addr) { + /* Send "read attributes" command to specific endpoint */ + esp_zb_zcl_read_attr_cmd_t read_req = {0}; + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + + uint16_t attributes[] = { + ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MIN_VALUE_ID, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MAX_VALUE_ID, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_TOLERANCE_ID + }; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i("Sending 'read sensor settings' command to endpoint %d, address 0x%x", endpoint, short_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + + //Take semaphore to wait for response of all attributes + if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) { + log_e("Error while reading attributes"); + return; + } else { + //Call the callback function when all attributes are read + _on_config_receive(_min_temp, _max_temp, _tolerance); + } +} + +void ZigbeeThermostat::getSensorSettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { + /* Send "read attributes" command to specific endpoint */ + esp_zb_zcl_read_attr_cmd_t read_req = {0}; + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + + uint16_t attributes[] = { + ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MIN_VALUE_ID, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MAX_VALUE_ID, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_TOLERANCE_ID + }; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i( + "Sending 'read sensor settings' command to endpoint %d, ieee address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6], + ieee_addr[5], ieee_addr[4], ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0] + ); esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_read_attr_cmd_req(&read_req); esp_zb_lock_release(); @@ -187,13 +345,13 @@ void ZigbeeThermostat::getSensorSettings() { return; } else { //Call the callback function when all attributes are read - _on_config_recieve(_min_temp, _max_temp, _tolerance); + _on_config_receive(_min_temp, _max_temp, _tolerance); } } void ZigbeeThermostat::setTemperatureReporting(uint16_t min_interval, uint16_t max_interval, float delta) { - /* Send "configure report attribute" command to the bound sensor */ - esp_zb_zcl_config_report_cmd_t report_cmd; + /* Send "configure report attribute" command to all bound sensors */ + esp_zb_zcl_config_report_cmd_t report_cmd = {0}; report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; report_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; @@ -206,10 +364,10 @@ void ZigbeeThermostat::setTemperatureReporting(uint16_t min_interval, uint16_t m .attrType = ESP_ZB_ZCL_ATTR_TYPE_S16, .min_interval = min_interval, .max_interval = max_interval, - .reportable_change = &report_change, + .reportable_change = (void *)&report_change, }, }; - report_cmd.record_number = ZB_ARRAY_LENTH(records); + report_cmd.record_number = ZB_ARRAY_LENGHT(records); report_cmd.record_field = records; log_i("Sending 'configure reporting' command"); @@ -218,4 +376,93 @@ void ZigbeeThermostat::setTemperatureReporting(uint16_t min_interval, uint16_t m esp_zb_lock_release(); } +void ZigbeeThermostat::setTemperatureReporting(uint16_t group_addr, uint16_t min_interval, uint16_t max_interval, float delta) { + /* Send "configure report attribute" command to the group */ + esp_zb_zcl_config_report_cmd_t report_cmd = {0}; + report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; + report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_cmd.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; + report_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + + int16_t report_change = (int16_t)delta * 100; + esp_zb_zcl_config_report_record_t records[] = { + { + .direction = ESP_ZB_ZCL_REPORT_DIRECTION_SEND, + .attributeID = ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, + .attrType = ESP_ZB_ZCL_ATTR_TYPE_S16, + .min_interval = min_interval, + .max_interval = max_interval, + .reportable_change = (void *)&report_change, + }, + }; + report_cmd.record_number = ZB_ARRAY_LENGHT(records); + report_cmd.record_field = records; + + log_i("Sending 'configure reporting' command to group address 0x%x", group_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_config_report_cmd_req(&report_cmd); + esp_zb_lock_release(); +} + +void ZigbeeThermostat::setTemperatureReporting(uint8_t endpoint, uint16_t short_addr, uint16_t min_interval, uint16_t max_interval, float delta) { + /* Send "configure report attribute" command to specific endpoint */ + esp_zb_zcl_config_report_cmd_t report_cmd = {0}; + report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_cmd.zcl_basic_cmd.dst_endpoint = endpoint; + report_cmd.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; + report_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + + int16_t report_change = (int16_t)delta * 100; + esp_zb_zcl_config_report_record_t records[] = { + { + .direction = ESP_ZB_ZCL_REPORT_DIRECTION_SEND, + .attributeID = ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, + .attrType = ESP_ZB_ZCL_ATTR_TYPE_S16, + .min_interval = min_interval, + .max_interval = max_interval, + .reportable_change = (void *)&report_change, + }, + }; + report_cmd.record_number = ZB_ARRAY_LENGHT(records); + report_cmd.record_field = records; + + log_i("Sending 'configure reporting' command to endpoint %d, address 0x%x", endpoint, short_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_config_report_cmd_req(&report_cmd); + esp_zb_lock_release(); +} + +void ZigbeeThermostat::setTemperatureReporting(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr, uint16_t min_interval, uint16_t max_interval, float delta) { + /* Send "configure report attribute" command to specific endpoint */ + esp_zb_zcl_config_report_cmd_t report_cmd = {0}; + report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_cmd.zcl_basic_cmd.dst_endpoint = endpoint; + report_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + memcpy(report_cmd.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + + int16_t report_change = (int16_t)delta * 100; + esp_zb_zcl_config_report_record_t records[] = { + { + .direction = ESP_ZB_ZCL_REPORT_DIRECTION_SEND, + .attributeID = ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, + .attrType = ESP_ZB_ZCL_ATTR_TYPE_S16, + .min_interval = min_interval, + .max_interval = max_interval, + .reportable_change = (void *)&report_change, + }, + }; + report_cmd.record_number = ZB_ARRAY_LENGHT(records); + report_cmd.record_field = records; + + log_i( + "Sending 'configure reporting' command to endpoint %d, ieee address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6], + ieee_addr[5], ieee_addr[4], ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0] + ); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_config_report_cmd_req(&report_cmd); + esp_zb_lock_release(); +} + #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeThermostat.h b/libraries/Zigbee/src/ep/ZigbeeThermostat.h index acdcd68e512..c10a9d7f974 100644 --- a/libraries/Zigbee/src/ep/ZigbeeThermostat.h +++ b/libraries/Zigbee/src/ep/ZigbeeThermostat.h @@ -34,24 +34,39 @@ class ZigbeeThermostat : public ZigbeeEP { ZigbeeThermostat(uint8_t endpoint); ~ZigbeeThermostat() {} - void onTempRecieve(void (*callback)(float)) { - _on_temp_recieve = callback; + void onTempReceive(void (*callback)(float)) { + _on_temp_receive = callback; } - void onConfigRecieve(void (*callback)(float, float, float)) { - _on_config_recieve = callback; + void onTempReceiveWithSource(void (*callback)(float, uint8_t, esp_zb_zcl_addr_t)) { + _on_temp_receive_with_source = callback; + } + void onConfigReceive(void (*callback)(float, float, float)) { + _on_config_receive = callback; } void getTemperature(); + void getTemperature(uint16_t group_addr); + void getTemperature(uint8_t endpoint, uint16_t short_addr); + void getTemperature(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + void getSensorSettings(); + void getSensorSettings(uint16_t group_addr); + void getSensorSettings(uint8_t endpoint, uint16_t short_addr); + void getSensorSettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + void setTemperatureReporting(uint16_t min_interval, uint16_t max_interval, float delta); + void setTemperatureReporting(uint16_t group_addr, uint16_t min_interval, uint16_t max_interval, float delta); + void setTemperatureReporting(uint8_t endpoint, uint16_t short_addr, uint16_t min_interval, uint16_t max_interval, float delta); + void setTemperatureReporting(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr, uint16_t min_interval, uint16_t max_interval, float delta); private: // save instance of the class in order to use it in static functions static ZigbeeThermostat *_instance; zb_device_params_t *_device; - void (*_on_temp_recieve)(float); - void (*_on_config_recieve)(float, float, float); + void (*_on_temp_receive)(float); + void (*_on_temp_receive_with_source)(float, uint8_t, esp_zb_zcl_addr_t); + void (*_on_config_receive)(float, float, float); float _min_temp; float _max_temp; float _tolerance; @@ -62,7 +77,7 @@ class ZigbeeThermostat : public ZigbeeEP { static void bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx); static void findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx); - void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) override; + void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) override; }; #endif // CONFIG_ZB_ENABLED From e3018b67191ec621cbd28b872f99c259de83a7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Wed, 4 Jun 2025 16:40:52 +0200 Subject: [PATCH 07/42] feat(zigbee): Add method to set/get/report analog output (#11431) * feat(zigbee): Add methot to set,get,report analog output * fix(ci): Update json file for example * fix(zigbee): Add missing keywords * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../Zigbee_Analog_Input_Output.ino | 12 +++-- .../Zigbee_Analog_Input_Output/ci.json | 3 +- libraries/Zigbee/keywords.txt | 11 ++++- libraries/Zigbee/src/ep/ZigbeeAnalog.cpp | 49 +++++++++++++++++-- libraries/Zigbee/src/ep/ZigbeeAnalog.h | 14 ++++-- 5 files changed, 74 insertions(+), 15 deletions(-) diff --git a/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/Zigbee_Analog_Input_Output.ino b/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/Zigbee_Analog_Input_Output.ino index db8a62b091e..59c4b514db1 100644 --- a/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/Zigbee_Analog_Input_Output.ino +++ b/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/Zigbee_Analog_Input_Output.ino @@ -26,8 +26,8 @@ * Modified by Pat Clay */ -#ifndef ZIGBEE_MODE_ED -#error "Zigbee end device mode is not selected in Tools->Zigbee mode" +#ifndef ZIGBEE_MODE_ZCZR +#error "Zigbee coordinator/router device mode is not selected in Tools->Zigbee mode" #endif #include "Zigbee.h" @@ -70,6 +70,7 @@ void setup() { zbAnalogDevice.addAnalogOutput(); zbAnalogDevice.setAnalogOutputApplication(ESP_ZB_ZCL_AI_RPM_OTHER); zbAnalogDevice.setAnalogOutputDescription("Fan Speed (RPM)"); + zbAnalogDevice.setAnalogOutputResolution(1); // If analog output cluster is added, set callback function for analog output change zbAnalogDevice.onAnalogOutputChange(onAnalogOutputChange); @@ -99,8 +100,8 @@ void setup() { Zigbee.addEndpoint(&zbAnalogPercent); Serial.println("Starting Zigbee..."); - // When all EPs are registered, start Zigbee in End Device mode - if (!Zigbee.begin()) { + // When all EPs are registered, start Zigbee in Router Device mode + if (!Zigbee.begin(ZIGBEE_ROUTER)) { Serial.println("Zigbee failed to start!"); Serial.println("Rebooting..."); ESP.restart(); @@ -151,6 +152,9 @@ void loop() { Zigbee.factoryReset(); } } + // For demonstration purposes, increment the analog output value by 100 + zbAnalogDevice.setAnalogOutput(zbAnalogDevice.getAnalogOutput() + 100); + zbAnalogDevice.reportAnalogOutput(); } delay(100); } diff --git a/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/ci.json b/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/ci.json index ceacc367801..15d6190e4ae 100644 --- a/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/ci.json +++ b/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/ci.json @@ -1,7 +1,6 @@ { - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", + "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", "CONFIG_ZB_ENABLED=y" ] } diff --git a/libraries/Zigbee/keywords.txt b/libraries/Zigbee/keywords.txt index 5ce1e8f6f51..556b6408ea2 100644 --- a/libraries/Zigbee/keywords.txt +++ b/libraries/Zigbee/keywords.txt @@ -143,14 +143,21 @@ setSensorType KEYWORD2 setCarbonDioxide KEYWORD2 # ZigbeeAnalog -addAnalogValue KEYWORD2 addAnalogInput KEYWORD2 addAnalogOutput KEYWORD2 onAnalogOutputChange KEYWORD2 -setAnalogValue KEYWORD2 setAnalogInput KEYWORD2 +setAnalogOutput KEYWORD2 +getAnalogOutput KEYWORD2 reportAnalogInput KEYWORD2 +reportAnalogOutput KEYWORD2 setAnalogInputReporting KEYWORD2 +setAnalogInputApplication KEYWORD2 +setAnalogInputDescription KEYWORD2 +setAnalogInputResolution KEYWORD2 +setAnalogOutputApplication KEYWORD2 +setAnalogOutputDescription KEYWORD2 +setAnalogOutputResolution KEYWORD2 # ZigbeeCarbonDioxideSensor setCarbonDioxide KEYWORD2 diff --git a/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp b/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp index 6e073c345bc..893a9854ecc 100644 --- a/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp @@ -129,8 +129,8 @@ bool ZigbeeAnalog::setAnalogOutputApplication(uint32_t application_type) { void ZigbeeAnalog::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT) { if (message->attribute.id == ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_SINGLE) { - float analog_output = *(float *)message->attribute.data.value; - analogOutputChanged(analog_output); + _output_state = *(float *)message->attribute.data.value; + analogOutputChanged(); } else { log_w("Received message ignored. Attribute ID: %d not supported for Analog Output", message->attribute.id); } @@ -139,9 +139,9 @@ void ZigbeeAnalog::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *mes } } -void ZigbeeAnalog::analogOutputChanged(float analog_output) { +void ZigbeeAnalog::analogOutputChanged() { if (_on_analog_output_change) { - _on_analog_output_change(analog_output); + _on_analog_output_change(_output_state); } else { log_w("No callback function set for analog output change"); } @@ -166,6 +166,26 @@ bool ZigbeeAnalog::setAnalogInput(float analog) { return true; } +bool ZigbeeAnalog::setAnalogOutput(float analog) { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + _output_state = analog; + analogOutputChanged(); + + log_v("Updating analog output to %.2f", analog); + /* Update analog output */ + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID, &_output_state, false + ); + esp_zb_lock_release(); + + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set analog output: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + return false; + } + return true; +} + bool ZigbeeAnalog::reportAnalogInput() { /* Send report attributes command */ esp_zb_zcl_report_attr_cmd_t report_attr_cmd; @@ -187,6 +207,27 @@ bool ZigbeeAnalog::reportAnalogInput() { return true; } +bool ZigbeeAnalog::reportAnalogOutput() { + /* Send report attributes command */ + esp_zb_zcl_report_attr_cmd_t report_attr_cmd; + report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID; + report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; + report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT; + report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd); + esp_zb_lock_release(); + if (ret != ESP_OK) { + log_e("Failed to send Analog Output report: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + log_v("Analog Output report sent"); + return true; +} + bool ZigbeeAnalog::setAnalogInputReporting(uint16_t min_interval, uint16_t max_interval, float delta) { esp_zb_zcl_reporting_info_t reporting_info; memset(&reporting_info, 0, sizeof(esp_zb_zcl_reporting_info_t)); diff --git a/libraries/Zigbee/src/ep/ZigbeeAnalog.h b/libraries/Zigbee/src/ep/ZigbeeAnalog.h index e0c3fc11c5a..bbc5f6d6fc2 100644 --- a/libraries/Zigbee/src/ep/ZigbeeAnalog.h +++ b/libraries/Zigbee/src/ep/ZigbeeAnalog.h @@ -46,11 +46,18 @@ class ZigbeeAnalog : public ZigbeeEP { _on_analog_output_change = callback; } - // Set the analog input value + // Set the Analog Input/Output value bool setAnalogInput(float analog); + bool setAnalogOutput(float analog); - // Report Analog Input value + // Get the Analog Output value + float getAnalogOutput() { + return _output_state; + } + + // Report Analog Input/Output bool reportAnalogInput(); + bool reportAnalogOutput(); // Set reporting for Analog Input bool setAnalogInputReporting(uint16_t min_interval, uint16_t max_interval, float delta); @@ -59,9 +66,10 @@ class ZigbeeAnalog : public ZigbeeEP { void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; void (*_on_analog_output_change)(float); - void analogOutputChanged(float analog_output); + void analogOutputChanged(); uint8_t _analog_clusters; + float _output_state; }; #endif // CONFIG_ZB_ENABLED From 31d22e6ed05a26a7fd7765b9ae42fd292559690f Mon Sep 17 00:00:00 2001 From: SooD <45733639+syong0921@users.noreply.github.com> Date: Thu, 5 Jun 2025 00:49:09 +0900 Subject: [PATCH 08/42] fix: change geekble nano board setting (#11432) add PSRAM Setting --- boards.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/boards.txt b/boards.txt index d92f057a78f..7e86af52a4f 100644 --- a/boards.txt +++ b/boards.txt @@ -41778,6 +41778,13 @@ Geekble_Nano_ESP32S3.menu.PartitionScheme.custom=Custom Geekble_Nano_ESP32S3.menu.PartitionScheme.custom.build.partitions= Geekble_Nano_ESP32S3.menu.PartitionScheme.custom.upload.maximum_size=16777216 +Geekble_Nano_ESP32S3.menu.PSRAM.disabled=Disabled +Geekble_Nano_ESP32S3.menu.PSRAM.disabled.build.defines= +Geekble_Nano_ESP32S3.menu.PSRAM.disabled.build.psram_type=qspi +Geekble_Nano_ESP32S3.menu.PSRAM.enabled=Enabled +Geekble_Nano_ESP32S3.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +Geekble_Nano_ESP32S3.menu.PSRAM.enabled.build.psram_type=qspi + Geekble_Nano_ESP32S3.menu.DebugLevel.none=None Geekble_Nano_ESP32S3.menu.DebugLevel.none.build.code_debug=0 Geekble_Nano_ESP32S3.menu.DebugLevel.error=Error From 1bac8de384c6e27abde8901b4fb3ca712d11cf1b Mon Sep 17 00:00:00 2001 From: SooD <45733639+syong0921@users.noreply.github.com> Date: Tue, 10 Jun 2025 16:29:11 +0900 Subject: [PATCH 09/42] fix: Updated the tools options for Geekble Mini (#11437) fix: Updated the tools options for Geekble Mini --- boards.txt | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/boards.txt b/boards.txt index 7e86af52a4f..49a4d9a737d 100644 --- a/boards.txt +++ b/boards.txt @@ -41589,11 +41589,6 @@ Geekble_ESP32C3.build.boot=qio Geekble_ESP32C3.build.partitions=default Geekble_ESP32C3.build.defines= -Geekble_ESP32C3.menu.CDCOnBoot.default=Enabled -Geekble_ESP32C3.menu.CDCOnBoot.default.build.cdc_on_boot=1 -Geekble_ESP32C3.menu.CDCOnBoot.cdc=Disabled -Geekble_ESP32C3.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 - Geekble_ESP32C3.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) Geekble_ESP32C3.menu.PartitionScheme.default.build.partitions=default Geekble_ESP32C3.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -41614,39 +41609,6 @@ Geekble_ESP32C3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) Geekble_ESP32C3.menu.PartitionScheme.huge_app.build.partitions=huge_app Geekble_ESP32C3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -Geekble_ESP32C3.menu.CPUFreq.160=160MHz (WiFi) (Default) -Geekble_ESP32C3.menu.CPUFreq.160.build.f_cpu=160000000L -Geekble_ESP32C3.menu.CPUFreq.80=80MHz (WiFi) -Geekble_ESP32C3.menu.CPUFreq.80.build.f_cpu=80000000L -Geekble_ESP32C3.menu.CPUFreq.40=40MHz -Geekble_ESP32C3.menu.CPUFreq.40.build.f_cpu=40000000L -Geekble_ESP32C3.menu.CPUFreq.20=20MHz -Geekble_ESP32C3.menu.CPUFreq.20.build.f_cpu=20000000L -Geekble_ESP32C3.menu.CPUFreq.10=10MHz -Geekble_ESP32C3.menu.CPUFreq.10.build.f_cpu=10000000L - -Geekble_ESP32C3.menu.FlashMode.qio=QIO (Default) -Geekble_ESP32C3.menu.FlashMode.qio.build.flash_mode=dio -Geekble_ESP32C3.menu.FlashMode.qio.build.boot=qio -Geekble_ESP32C3.menu.FlashMode.dio=DIO -Geekble_ESP32C3.menu.FlashMode.dio.build.flash_mode=dio -Geekble_ESP32C3.menu.FlashMode.dio.build.boot=dio -Geekble_ESP32C3.menu.FlashMode.qout=QOUT -Geekble_ESP32C3.menu.FlashMode.qout.build.flash_mode=dout -Geekble_ESP32C3.menu.FlashMode.qout.build.boot=qout -Geekble_ESP32C3.menu.FlashMode.dout=DOUT -Geekble_ESP32C3.menu.FlashMode.dout.build.flash_mode=dout - -Geekble_ESP32C3.menu.FlashFreq.80=80MHz (Default) -Geekble_ESP32C3.menu.FlashFreq.80.build.flash_freq=80m -Geekble_ESP32C3.menu.FlashFreq.40=40MHz -Geekble_ESP32C3.menu.FlashFreq.40.build.flash_freq=40m - -Geekble_ESP32C3.menu.FlashSize.4M=4MB (Default) -Geekble_ESP32C3.menu.FlashSize.4M.build.flash_size=4MB -Geekble_ESP32C3.menu.FlashSize.2M=2MB -Geekble_ESP32C3.menu.FlashSize.2M.build.flash_size=2MB - Geekble_ESP32C3.menu.UploadSpeed.921600=921600 (Default) Geekble_ESP32C3.menu.UploadSpeed.921600.upload.speed=921600 Geekble_ESP32C3.menu.UploadSpeed.115200=115200 From 610d951f9d1ddf690b786600bccddb515f1f0b5b Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Tue, 10 Jun 2025 09:29:29 +0200 Subject: [PATCH 10/42] include "esp_bt.h" only when existing (#11438) --- cores/esp32/esp32-hal-misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index 594acd38153..aadd08ceffc 100644 --- a/cores/esp32/esp32-hal-misc.c +++ b/cores/esp32/esp32-hal-misc.c @@ -25,7 +25,7 @@ #include "esp_ota_ops.h" #endif //CONFIG_APP_ROLLBACK_ENABLE #include "esp_private/startup_internal.h" -#if defined(CONFIG_BT_BLUEDROID_ENABLED) && SOC_BT_SUPPORTED +#if defined(CONFIG_BT_BLUEDROID_ENABLED) && SOC_BT_SUPPORTED && __has_include("esp_bt.h") #include "esp_bt.h" #endif //CONFIG_BT_BLUEDROID_ENABLED #include From ee347baa7dca0cbe87dbd579465bbe00924f9b03 Mon Sep 17 00:00:00 2001 From: i3water <121024123@qq.com> Date: Tue, 10 Jun 2025 15:29:46 +0800 Subject: [PATCH 11/42] feat(boards): update wifiduinov2&wifiduino32s3 boards setting (#11440) * update wifiduinov2&wifiduino32s3 boards setting * fix wifiduinov2&wifiduino32s3 build board error. * fix wifiduinov2(esp32c3) board setting * fix wifiduinov2(esp32c3) cdc on boot default setting. * fix wifiduino32s3 spi pin set * change wifiduino32s3 spi pin to spi1 * remove 32Mb flash size * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- boards.txt | 46 +++++++++---------- variants/wifiduino32s3/pins_arduino.h | 66 ++++++++++++++++----------- 2 files changed, 62 insertions(+), 50 deletions(-) diff --git a/boards.txt b/boards.txt index 49a4d9a737d..fefc6cdfb71 100644 --- a/boards.txt +++ b/boards.txt @@ -31513,16 +31513,16 @@ wifiduino32c3.build.target=esp wifiduino32c3.build.mcu=esp32c3 wifiduino32c3.build.core=esp32 wifiduino32c3.build.variant=wifiduinov2 -wifiduino32c3.build.board=WiFiduinoV2 +wifiduino32c3.build.board=WIFIDUINOV2 wifiduino32c3.build.bootloader_addr=0x0 wifiduino32c3.build.cdc_on_boot=0 wifiduino32c3.build.f_cpu=160000000L wifiduino32c3.build.flash_size=4MB wifiduino32c3.build.flash_freq=80m -wifiduino32c3.build.flash_mode=qio -wifiduino32c3.build.boot=qio -wifiduino32c3.build.partitions=default +wifiduino32c3.build.flash_mode=dio +wifiduino32c3.build.boot=dio +wifiduino32c3.build.partitions=no_ota wifiduino32c3.build.defines= wifiduino32c3.menu.CDCOnBoot.default=Disabled @@ -31530,6 +31530,9 @@ wifiduino32c3.menu.CDCOnBoot.default.build.cdc_on_boot=0 wifiduino32c3.menu.CDCOnBoot.cdc=Enabled wifiduino32c3.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 +wifiduino32c3.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +wifiduino32c3.menu.PartitionScheme.no_ota.build.partitions=no_ota +wifiduino32c3.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 wifiduino32c3.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) wifiduino32c3.menu.PartitionScheme.default.build.partitions=default wifiduino32c3.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -31539,9 +31542,6 @@ wifiduino32c3.menu.PartitionScheme.default_8MB.build.partitions=default_8MB wifiduino32c3.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 wifiduino32c3.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) wifiduino32c3.menu.PartitionScheme.minimal.build.partitions=minimal -wifiduino32c3.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) -wifiduino32c3.menu.PartitionScheme.no_ota.build.partitions=no_ota -wifiduino32c3.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 wifiduino32c3.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) wifiduino32c3.menu.PartitionScheme.noota_3g.build.partitions=noota_3g wifiduino32c3.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 @@ -31584,12 +31584,12 @@ wifiduino32c3.menu.CPUFreq.20.build.f_cpu=20000000L wifiduino32c3.menu.CPUFreq.10=10MHz wifiduino32c3.menu.CPUFreq.10.build.f_cpu=10000000L -wifiduino32c3.menu.FlashMode.qio=QIO -wifiduino32c3.menu.FlashMode.qio.build.flash_mode=dio -wifiduino32c3.menu.FlashMode.qio.build.boot=qio wifiduino32c3.menu.FlashMode.dio=DIO wifiduino32c3.menu.FlashMode.dio.build.flash_mode=dio wifiduino32c3.menu.FlashMode.dio.build.boot=dio +wifiduino32c3.menu.FlashMode.qio=QIO +wifiduino32c3.menu.FlashMode.qio.build.flash_mode=dio +wifiduino32c3.menu.FlashMode.qio.build.boot=qio wifiduino32c3.menu.FlashFreq.80=80MHz wifiduino32c3.menu.FlashFreq.80.build.flash_freq=80m @@ -31665,34 +31665,34 @@ wifiduino32s3.build.target=esp32s3 wifiduino32s3.build.mcu=esp32s3 wifiduino32s3.build.core=esp32 wifiduino32s3.build.variant=wifiduino32s3 -wifiduino32s3.build.board=WiFiduino32S3 +wifiduino32s3.build.board=WIFIDUINO32S3 wifiduino32s3.build.usb_mode=1 wifiduino32s3.build.cdc_on_boot=0 wifiduino32s3.build.msc_on_boot=0 wifiduino32s3.build.dfu_on_boot=0 wifiduino32s3.build.f_cpu=240000000L -wifiduino32s3.build.flash_size=4MB +wifiduino32s3.build.flash_size=16MB wifiduino32s3.build.flash_freq=80m wifiduino32s3.build.flash_mode=dio wifiduino32s3.build.boot=qio wifiduino32s3.build.boot_freq=80m -wifiduino32s3.build.partitions=default +wifiduino32s3.build.partitions=app3M_fat9M_16MB wifiduino32s3.build.defines= wifiduino32s3.build.loop_core= wifiduino32s3.build.event_core= -wifiduino32s3.build.psram_type=qspi +wifiduino32s3.build.psram_type=opi wifiduino32s3.build.memory_type={build.boot}_{build.psram_type} +wifiduino32s3.menu.PSRAM.opi=OPI PSRAM +wifiduino32s3.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM +wifiduino32s3.menu.PSRAM.opi.build.psram_type=opi wifiduino32s3.menu.PSRAM.disabled=Disabled wifiduino32s3.menu.PSRAM.disabled.build.defines= wifiduino32s3.menu.PSRAM.disabled.build.psram_type=qspi wifiduino32s3.menu.PSRAM.enabled=QSPI PSRAM wifiduino32s3.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM wifiduino32s3.menu.PSRAM.enabled.build.psram_type=qspi -wifiduino32s3.menu.PSRAM.opi=OPI PSRAM -wifiduino32s3.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM -wifiduino32s3.menu.PSRAM.opi.build.psram_type=opi wifiduino32s3.menu.FlashMode.qio=QIO 80MHz wifiduino32s3.menu.FlashMode.qio.build.flash_mode=dio @@ -31715,12 +31715,10 @@ wifiduino32s3.menu.FlashMode.opi.build.boot=opi wifiduino32s3.menu.FlashMode.opi.build.boot_freq=80m wifiduino32s3.menu.FlashMode.opi.build.flash_freq=80m -wifiduino32s3.menu.FlashSize.4M=4MB (32Mb) -wifiduino32s3.menu.FlashSize.4M.build.flash_size=4MB -wifiduino32s3.menu.FlashSize.8M=8MB (64Mb) -wifiduino32s3.menu.FlashSize.8M.build.flash_size=8MB wifiduino32s3.menu.FlashSize.16M=16MB (128Mb) wifiduino32s3.menu.FlashSize.16M.build.flash_size=16MB +wifiduino32s3.menu.FlashSize.8M=8MB (64Mb) +wifiduino32s3.menu.FlashSize.8M.build.flash_size=8MB #wifiduino32s3.menu.FlashSize.32M=32MB (256Mb) #wifiduino32s3.menu.FlashSize.32M.build.flash_size=32MB @@ -31761,6 +31759,9 @@ wifiduino32s3.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) wifiduino32s3.menu.UploadMode.cdc.upload.use_1200bps_touch=true wifiduino32s3.menu.UploadMode.cdc.upload.wait_for_upload_port=true +wifiduino32s3.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +wifiduino32s3.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +wifiduino32s3.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 wifiduino32s3.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) wifiduino32s3.menu.PartitionScheme.default.build.partitions=default wifiduino32s3.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -31791,9 +31792,6 @@ wifiduino32s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 wifiduino32s3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) wifiduino32s3.menu.PartitionScheme.fatflash.build.partitions=ffat wifiduino32s3.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -wifiduino32s3.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -wifiduino32s3.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -wifiduino32s3.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 wifiduino32s3.menu.PartitionScheme.rainmaker=RainMaker 4MB wifiduino32s3.menu.PartitionScheme.rainmaker.build.partitions=rainmaker wifiduino32s3.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 diff --git a/variants/wifiduino32s3/pins_arduino.h b/variants/wifiduino32s3/pins_arduino.h index d26e415910e..4fc08139c0e 100644 --- a/variants/wifiduino32s3/pins_arduino.h +++ b/variants/wifiduino32s3/pins_arduino.h @@ -2,43 +2,57 @@ #define Pins_Arduino_h #include - -#define USB_VID 0x303a -#define USB_PID 0x1001 - -// No USER LED or NeoLED - -static const uint8_t TX = 45; +#include "soc/soc_caps.h" + +#define USB_VID 0x303a +#define USB_PID 0x1001 +#define USB_MANUFACTURER "openjumper" +#define USB_PRODUCT "Wifiduino32-S3" +#define USB_SERIAL "" // Empty string for MAC address + +// Some boards have too low voltage on this pin (board design bug) +// Use different pin with 3V and connect with 48 +// and change this setup for the chosen pin (for example 38) +#define PIN_RGB_LED 48 +// BUILTIN_LED can be used in new Arduino API digitalWrite() like in Blink.ino +static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + PIN_RGB_LED; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN +// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API rgbLedWrite() +#define RGB_BUILTIN LED_BUILTIN +#define RGB_BRIGHTNESS 64 + +static const uint8_t TX = 43; static const uint8_t RX = 44; static const uint8_t SDA = 4; static const uint8_t SCL = 5; -static const uint8_t SS = 46; -static const uint8_t MOSI = 3; -static const uint8_t MISO = 20; -static const uint8_t SCK = 19; +static const uint8_t SS = 10; +static const uint8_t MOSI = 11; +static const uint8_t MISO = 13; +static const uint8_t SCK = 12; -static const uint8_t A0 = 7; -static const uint8_t A1 = 6; +static const uint8_t A0 = 0; +static const uint8_t A1 = 1; static const uint8_t A2 = 2; -static const uint8_t A3 = 1; +static const uint8_t A3 = 3; static const uint8_t A4 = 4; static const uint8_t A5 = 5; static const uint8_t D0 = 44; -static const uint8_t D1 = 45; -static const uint8_t D2 = 42; -static const uint8_t D3 = 41; -static const uint8_t D4 = 0; -static const uint8_t D5 = 45; -static const uint8_t D6 = 48; -static const uint8_t D7 = 47; +static const uint8_t D1 = 43; +static const uint8_t D2 = 45; +static const uint8_t D3 = 46; +static const uint8_t D4 = 47; +static const uint8_t D5 = 48; +static const uint8_t D6 = 18; +static const uint8_t D7 = 17; static const uint8_t D8 = 21; -static const uint8_t D9 = 14; -static const uint8_t D10 = 46; -static const uint8_t D11 = 3; -static const uint8_t D12 = 20; -static const uint8_t D13 = 19; +static const uint8_t D9 = 42; +static const uint8_t D10 = 41; +static const uint8_t D11 = 40; +static const uint8_t D12 = 38; +static const uint8_t D13 = 39; #endif /* Pins_Arduino_h */ From af47bd30089bee71849b634b97dd329e6564a10a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 10 Jun 2025 09:59:56 +0200 Subject: [PATCH 12/42] fix(ci): Process only needed files in publish sizes (#11439) --- .github/workflows/publishsizes.yml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publishsizes.yml b/.github/workflows/publishsizes.yml index 69c18cf1835..8ff591e052b 100644 --- a/.github/workflows/publishsizes.yml +++ b/.github/workflows/publishsizes.yml @@ -44,16 +44,17 @@ jobs: gh api "$artifacts_url" -q '.artifacts[] | [.name, .archive_download_url] | @tsv' | while read artifact do IFS=$'\t' read name url <<< "$artifact" - gh api $url > "$name.zip" - unzip -j "$name.zip" -d "temp_$name" - if [[ "$name" == "pr_number" ]]; then - mv "temp_$name"/* sizes-report - elif [[ "$name" == "pr_cli"* ]]; then - mv "temp_$name"/* sizes-report/pr - else - mv "temp_$name"/* sizes-report + # Only process pr_number and pr_cli_compile artifacts + if [[ "$name" == "pr_number" || "$name" =~ ^pr_cli_compile_[0-9]+$ ]]; then + gh api $url > "$name.zip" + unzip -o -j "$name.zip" -d "temp_$name" + if [[ "$name" == "pr_number" ]]; then + mv "temp_$name"/* sizes-report + elif [[ "$name" =~ ^pr_cli_compile_[0-9]+$ ]]; then + mv "temp_$name"/* sizes-report/pr + fi + rm -r "temp_$name" fi - rm -r "temp_$name" done echo "Contents of parent directory:" ls -R .. From 0007815a1148c7a3d24a3f6986739bc2b26eb5c7 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 10 Jun 2025 11:00:20 +0300 Subject: [PATCH 13/42] feat(p4): Add 32MB Flash Partitions to ESP32-P4 (#11453) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(p4): Add 32MB Flash Partitions to ESP32-P4 * feat(p4): Add 32MB flash size option --------- Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com> --- boards.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/boards.txt b/boards.txt index fefc6cdfb71..15a75eaad8b 100644 --- a/boards.txt +++ b/boards.txt @@ -282,6 +282,15 @@ esp32p4.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 esp32p4.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) esp32p4.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB esp32p4.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 +esp32p4.menu.PartitionScheme.app5M_fat24M_32MB=32M Flash (4.8MB APP/22MB FATFS) +esp32p4.menu.PartitionScheme.app5M_fat24M_32MB.build.partitions=large_fat_32MB +esp32p4.menu.PartitionScheme.app5M_fat24M_32MB.upload.maximum_size=4718592 +esp32p4.menu.PartitionScheme.app5M_little24M_32MB=32M Flash (4.8MB APP/22MB LittleFS) +esp32p4.menu.PartitionScheme.app5M_little24M_32MB.build.partitions=large_littlefs_32MB +esp32p4.menu.PartitionScheme.app5M_little24M_32MB.upload.maximum_size=4718592 +esp32p4.menu.PartitionScheme.app13M_data7M_32MB=32M Flash (13MB APP/6.75MB SPIFFS) +esp32p4.menu.PartitionScheme.app13M_data7M_32MB.build.partitions=default_32MB +esp32p4.menu.PartitionScheme.app13M_data7M_32MB.upload.maximum_size=13107200 esp32p4.menu.PartitionScheme.custom=Custom esp32p4.menu.PartitionScheme.custom.build.partitions= esp32p4.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -314,6 +323,8 @@ esp32p4.menu.FlashSize.2M.build.flash_size=2MB esp32p4.menu.FlashSize.2M.build.partitions=minimal esp32p4.menu.FlashSize.16M=16MB (128Mb) esp32p4.menu.FlashSize.16M.build.flash_size=16MB +esp32p4.menu.FlashSize.32M=32MB (256Mb) +esp32p4.menu.FlashSize.32M.build.flash_size=32MB esp32p4.menu.UploadSpeed.921600=921600 esp32p4.menu.UploadSpeed.921600.upload.speed=921600 From 0aada091e1afb56bb4e2103297233b17a9463bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 10 Jun 2025 10:31:17 +0200 Subject: [PATCH 14/42] feat(zigbee): Support min/max setting for Analog EP (#11451) * feat(zigbee): Support min max for Analog EP * feat(zigbee): Use cfloat FLT_MAX * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../Zigbee_Analog_Input_Output.ino | 3 + libraries/Zigbee/src/ep/ZigbeeAnalog.cpp | 84 +++++++++++++++++++ libraries/Zigbee/src/ep/ZigbeeAnalog.h | 4 + 3 files changed, 91 insertions(+) diff --git a/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/Zigbee_Analog_Input_Output.ino b/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/Zigbee_Analog_Input_Output.ino index 59c4b514db1..f1cc54bda64 100644 --- a/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/Zigbee_Analog_Input_Output.ino +++ b/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/Zigbee_Analog_Input_Output.ino @@ -72,6 +72,9 @@ void setup() { zbAnalogDevice.setAnalogOutputDescription("Fan Speed (RPM)"); zbAnalogDevice.setAnalogOutputResolution(1); + // Set the min and max values for the analog output which is used by HA to limit the range of the analog output + zbAnalogDevice.setAnalogOutputMinMax(-10000, 10000); //-10000 to 10000 RPM + // If analog output cluster is added, set callback function for analog output change zbAnalogDevice.onAnalogOutputChange(onAnalogOutputChange); diff --git a/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp b/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp index 893a9854ecc..309739d54c9 100644 --- a/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp @@ -1,5 +1,6 @@ #include "ZigbeeAnalog.h" #if CONFIG_ZB_ENABLED +#include ZigbeeAnalog::ZigbeeAnalog(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID; @@ -20,6 +21,8 @@ bool ZigbeeAnalog::addAnalogInput() { "Analog Input"; uint32_t application_type = 0x00000000 | (ESP_ZB_ZCL_AI_GROUP_ID << 24); float resolution = 0.1; // Default resolution of 0.1 + float min = -FLT_MAX; // Default min value for float + float max = FLT_MAX; // Default max value for float esp_err_t ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_DESCRIPTION_ID, (void *)default_description); if (ret != ESP_OK) { @@ -39,11 +42,24 @@ bool ZigbeeAnalog::addAnalogInput() { return false; } + ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_MIN_PRESENT_VALUE_ID, (void *)&min); + if (ret != ESP_OK) { + log_e("Failed to set min value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_MAX_PRESENT_VALUE_ID, (void *)&max); + if (ret != ESP_OK) { + log_e("Failed to set max value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + ret = esp_zb_cluster_list_add_analog_input_cluster(_cluster_list, esp_zb_analog_input_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); if (ret != ESP_OK) { log_e("Failed to add Analog Input cluster: 0x%x: %s", ret, esp_err_to_name(ret)); return false; } + _analog_clusters |= ANALOG_INPUT; return true; } @@ -76,6 +92,8 @@ bool ZigbeeAnalog::addAnalogOutput() { "Analog Output"; uint32_t application_type = 0x00000000 | (ESP_ZB_ZCL_AO_GROUP_ID << 24); float resolution = 1; // Default resolution of 1 + float min = -FLT_MAX; // Default min value for float + float max = FLT_MAX; // Default max value for float esp_err_t ret = esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_DESCRIPTION_ID, (void *)default_description); @@ -96,6 +114,18 @@ bool ZigbeeAnalog::addAnalogOutput() { return false; } + ret = esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_MIN_PRESENT_VALUE_ID, (void *)&min); + if (ret != ESP_OK) { + log_e("Failed to set min value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_MAX_PRESENT_VALUE_ID, (void *)&max); + if (ret != ESP_OK) { + log_e("Failed to set max value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + ret = esp_zb_cluster_list_add_analog_output_cluster(_cluster_list, esp_zb_analog_output_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); if (ret != ESP_OK) { log_e("Failed to add Analog Output cluster: 0x%x: %s", ret, esp_err_to_name(ret)); @@ -376,4 +406,58 @@ bool ZigbeeAnalog::setAnalogOutputResolution(float resolution) { return true; } +bool ZigbeeAnalog::setAnalogOutputMinMax(float min, float max) { + if (!(_analog_clusters & ANALOG_OUTPUT)) { + log_e("Analog Output cluster not added"); + return false; + } + + esp_zb_attribute_list_t *analog_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (analog_output_cluster == nullptr) { + log_e("Failed to get analog output cluster"); + return false; + } + + esp_err_t ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_MIN_PRESENT_VALUE_ID, (void *)&min); + if (ret != ESP_OK) { + log_e("Failed to set min value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_MAX_PRESENT_VALUE_ID, (void *)&max); + if (ret != ESP_OK) { + log_e("Failed to set max value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeAnalog::setAnalogInputMinMax(float min, float max) { + if (!(_analog_clusters & ANALOG_INPUT)) { + log_e("Analog Input cluster not added"); + return false; + } + + esp_zb_attribute_list_t *analog_input_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (analog_input_cluster == nullptr) { + log_e("Failed to get analog input cluster"); + return false; + } + + esp_err_t ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_MIN_PRESENT_VALUE_ID, (void *)&min); + if (ret != ESP_OK) { + log_e("Failed to set min value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_MAX_PRESENT_VALUE_ID, (void *)&max); + if (ret != ESP_OK) { + log_e("Failed to set max value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeAnalog.h b/libraries/Zigbee/src/ep/ZigbeeAnalog.h index bbc5f6d6fc2..5218a0b7d60 100644 --- a/libraries/Zigbee/src/ep/ZigbeeAnalog.h +++ b/libraries/Zigbee/src/ep/ZigbeeAnalog.h @@ -41,6 +41,10 @@ class ZigbeeAnalog : public ZigbeeEP { bool setAnalogOutputDescription(const char *description); bool setAnalogOutputResolution(float resolution); + // Set the min and max values for the analog Input/Output + bool setAnalogOutputMinMax(float min, float max); + bool setAnalogInputMinMax(float min, float max); + // Use to set a cb function to be called on analog output change void onAnalogOutputChange(void (*callback)(float analog)) { _on_analog_output_change = callback; From d6a76da0a5134bce8dfd4961af7e9c828bf21328 Mon Sep 17 00:00:00 2001 From: Niki Waibel Date: Tue, 10 Jun 2025 10:55:43 +0200 Subject: [PATCH 15/42] fix(libraries/asyncudp): IPv4 ONLY listenMulticast() (#11444) AsyncUDP::listenMulticast() properly receives packets sent to IPv4 multicast addresses like 239.1.2.3, but it is not receiving packets sent to IPv6 multicast addresses like ff12::6ood:cafe. The root cause is a bit hidden: listen(NULL, port) would match AsyncUDP::listen(const ip_addr_t *addr, uint16_t port), which calls _udp_bind(_pcb, addr, port), which uses the lwIP API to call udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) at the end. If lwIP has LWIP_IPV4 enabled, it checks if ipaddr == NULL and sets it to IP4_ADDR_ANY. So an IPv6 address is never bound. This fix checks the IP address passed to AsyncUDP::listenMulticast(). If it is an IPv6 address, it constructs and passes the IPv6 any address (::); otherwise (IPv4), it constructs and passes the IPv4 any address (0.0.0.0). --- libraries/AsyncUDP/src/AsyncUDP.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/libraries/AsyncUDP/src/AsyncUDP.cpp b/libraries/AsyncUDP/src/AsyncUDP.cpp index f44cc839c97..cab9c951921 100644 --- a/libraries/AsyncUDP/src/AsyncUDP.cpp +++ b/libraries/AsyncUDP/src/AsyncUDP.cpp @@ -682,6 +682,8 @@ static esp_err_t joinMulticastGroup(const ip_addr_t *addr, bool join, tcpip_adap } bool AsyncUDP::listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if) { + ip_addr_t bind_addr; + if (!ip_addr_ismulticast(addr)) { return false; } @@ -690,7 +692,18 @@ bool AsyncUDP::listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl return false; } - if (!listen(NULL, port)) { +#if CONFIG_LWIP_IPV6 + if (IP_IS_V6(addr)) { + IP_SET_TYPE(&bind_addr, IPADDR_TYPE_V6); + ip6_addr_set_any(&bind_addr.u_addr.ip6); + } else { +#endif + IP_SET_TYPE(&bind_addr, IPADDR_TYPE_V4); + ip4_addr_set_any(&bind_addr.u_addr.ip4); +#if CONFIG_LWIP_IPV6 + } +#endif + if (!listen(&bind_addr, port)) { return false; } From cbdaee6f52b78fa747693dbb718c2cafe99015b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 10 Jun 2025 11:57:07 +0200 Subject: [PATCH 16/42] feat(ledc): Improve timer management with frequency/resolution matching (#11452) * feat(ledc): Improve timer management with frequency/resolution matching * fix(ci): Fix uninitialized timer variable warning * Update cores/esp32/esp32-hal-ledc.c Co-authored-by: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- cores/esp32/esp32-hal-ledc.c | 124 +++++++++++++++++++++++++++++++---- cores/esp32/esp32-hal-ledc.h | 2 + 2 files changed, 115 insertions(+), 11 deletions(-) diff --git a/cores/esp32/esp32-hal-ledc.c b/cores/esp32/esp32-hal-ledc.c index 039fa1312f1..764c2803b4b 100644 --- a/cores/esp32/esp32-hal-ledc.c +++ b/cores/esp32/esp32-hal-ledc.c @@ -45,6 +45,93 @@ typedef struct { ledc_periph_t ledc_handle = {0}; +// Helper function to find a timer with matching frequency and resolution +static bool find_matching_timer(uint8_t speed_mode, uint32_t freq, uint8_t resolution, uint8_t *timer_num) { + log_d("Searching for timer with freq=%u, resolution=%u", freq, resolution); + // Check all channels to find one with matching frequency and resolution + for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) { + if (!perimanPinIsValid(i)) { + continue; + } + peripheral_bus_type_t type = perimanGetPinBusType(i); + if (type == ESP32_BUS_TYPE_LEDC) { + ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC); + if (bus != NULL && (bus->channel / 8) == speed_mode && bus->freq_hz == freq && bus->channel_resolution == resolution) { + log_d("Found matching timer %u for freq=%u, resolution=%u", bus->timer_num, freq, resolution); + *timer_num = bus->timer_num; + return true; + } + } + } + log_d("No matching timer found for freq=%u, resolution=%u", freq, resolution); + return false; +} + +// Helper function to find an unused timer +static bool find_free_timer(uint8_t speed_mode, uint8_t *timer_num) { + // Check which timers are in use + uint8_t used_timers = 0; + for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) { + if (!perimanPinIsValid(i)) { + continue; + } + peripheral_bus_type_t type = perimanGetPinBusType(i); + if (type == ESP32_BUS_TYPE_LEDC) { + ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC); + if (bus != NULL && (bus->channel / 8) == speed_mode) { + log_d("Timer %u is in use by channel %u", bus->timer_num, bus->channel); + used_timers |= (1 << bus->timer_num); + } + } + } + + // Find first unused timer + for (uint8_t i = 0; i < SOC_LEDC_TIMER_NUM; i++) { + if (!(used_timers & (1 << i))) { + log_d("Found free timer %u", i); + *timer_num = i; + return true; + } + } + log_e("No free timers available"); + return false; +} + +// Helper function to remove a channel from a timer and clear timer if no channels are using it +static void remove_channel_from_timer(uint8_t speed_mode, uint8_t timer_num, uint8_t channel) { + log_d("Removing channel %u from timer %u in speed_mode %u", channel, timer_num, speed_mode); + + // Check if any other channels are using this timer + bool timer_in_use = false; + for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) { + if (!perimanPinIsValid(i)) { + continue; + } + peripheral_bus_type_t type = perimanGetPinBusType(i); + if (type == ESP32_BUS_TYPE_LEDC) { + ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC); + if (bus != NULL && (bus->channel / 8) == speed_mode && bus->timer_num == timer_num && bus->channel != channel) { + log_d("Timer %u is still in use by channel %u", timer_num, bus->channel); + timer_in_use = true; + break; + } + } + } + + if (!timer_in_use) { + log_d("No other channels using timer %u, deconfiguring timer", timer_num); + // Stop the timer + ledc_timer_pause(speed_mode, timer_num); + // Deconfigure the timer + ledc_timer_config_t ledc_timer; + memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t)); + ledc_timer.speed_mode = speed_mode; + ledc_timer.timer_num = timer_num; + ledc_timer.deconfigure = true; + ledc_timer_config(&ledc_timer); + } +} + static bool fade_initialized = false; static ledc_clk_cfg_t clock_source = LEDC_DEFAULT_CLK; @@ -81,6 +168,8 @@ static bool ledcDetachBus(void *bus) { } pinMatrixOutDetach(handle->pin, false, false); if (!channel_found) { + uint8_t group = (handle->channel / 8); + remove_channel_from_timer(group, handle->timer_num, handle->channel % 8); ledc_handle.used_channels &= ~(1UL << handle->channel); } free(handle); @@ -117,8 +206,10 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c return false; } - uint8_t group = (channel / 8), timer = ((channel / 2) % 4); + uint8_t group = (channel / 8); + uint8_t timer = 0; bool channel_used = ledc_handle.used_channels & (1UL << channel); + if (channel_used) { log_i("Channel %u is already set up, given frequency and resolution will be ignored", channel); if (ledc_set_pin(pin, group, channel % 8) != ESP_OK) { @@ -126,17 +217,26 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c return false; } } else { - ledc_timer_config_t ledc_timer; - memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t)); - ledc_timer.speed_mode = group; - ledc_timer.timer_num = timer; - ledc_timer.duty_resolution = resolution; - ledc_timer.freq_hz = freq; - ledc_timer.clk_cfg = clock_source; + // Find a timer with matching frequency and resolution, or a free timer + if (!find_matching_timer(group, freq, resolution, &timer)) { + if (!find_free_timer(group, &timer)) { + log_e("No free timers available for speed mode %u", group); + return false; + } - if (ledc_timer_config(&ledc_timer) != ESP_OK) { - log_e("ledc setup failed!"); - return false; + // Configure the timer if we're using a new one + ledc_timer_config_t ledc_timer; + memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t)); + ledc_timer.speed_mode = group; + ledc_timer.timer_num = timer; + ledc_timer.duty_resolution = resolution; + ledc_timer.freq_hz = freq; + ledc_timer.clk_cfg = clock_source; + + if (ledc_timer_config(&ledc_timer) != ESP_OK) { + log_e("ledc setup failed!"); + return false; + } } uint32_t duty = ledc_get_duty(group, (channel % 8)); @@ -157,6 +257,8 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c ledc_channel_handle_t *handle = (ledc_channel_handle_t *)malloc(sizeof(ledc_channel_handle_t)); handle->pin = pin; handle->channel = channel; + handle->timer_num = timer; + handle->freq_hz = freq; #ifndef SOC_LEDC_SUPPORT_FADE_STOP handle->lock = NULL; #endif diff --git a/cores/esp32/esp32-hal-ledc.h b/cores/esp32/esp32-hal-ledc.h index 5b44aaad452..f1a27dd4f7a 100644 --- a/cores/esp32/esp32-hal-ledc.h +++ b/cores/esp32/esp32-hal-ledc.h @@ -51,6 +51,8 @@ typedef struct { uint8_t pin; // Pin assigned to channel uint8_t channel; // Channel number uint8_t channel_resolution; // Resolution of channel + uint8_t timer_num; // Timer number used by this channel + uint32_t freq_hz; // Frequency configured for this channel voidFuncPtr fn; void *arg; #ifndef SOC_LEDC_SUPPORT_FADE_STOP From c21ef70a156aa0071f00ee6d7a00e0be2d0aa17e Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 10 Jun 2025 07:09:27 -0300 Subject: [PATCH 17/42] fix(release): Replace all assets with chinese mirrors (#11323) * fix(release): Replace all assets with chinese mirrors * feat(release): Add script to append "-cn" to versions * docs(install): Add instructions for users in China --- .github/scripts/on-release.sh | 10 +++-- .github/scripts/release_append_cn.py | 56 ++++++++++++++++++++++++++++ docs/en/installing.rst | 2 + 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100755 .github/scripts/release_append_cn.py diff --git a/.github/scripts/on-release.sh b/.github/scripts/on-release.sh index dafbf3d6a1c..275c74f8ea5 100755 --- a/.github/scripts/on-release.sh +++ b/.github/scripts/on-release.sh @@ -342,12 +342,14 @@ jq_arg=".packages[0].platforms[0].version = \"$RELEASE_TAG\" | \ echo "Generating $PACKAGE_JSON_DEV ..." cat "$PACKAGE_JSON_TEMPLATE" | jq "$jq_arg" > "$OUTPUT_DIR/$PACKAGE_JSON_DEV" # On MacOS the sed command won't skip the first match. Use gsed instead. -sed '0,/github\.com\/espressif\//!s|github\.com/espressif/|dl.espressif.cn/github_assets/espressif/|g' "$OUTPUT_DIR/$PACKAGE_JSON_DEV" > "$OUTPUT_DIR/$PACKAGE_JSON_DEV_CN" +sed '0,/github\.com\//!s|github\.com/|dl.espressif.cn/github_assets/|g' "$OUTPUT_DIR/$PACKAGE_JSON_DEV" > "$OUTPUT_DIR/$PACKAGE_JSON_DEV_CN" +python "$SCRIPTS_DIR/release_append_cn.py" "$OUTPUT_DIR/$PACKAGE_JSON_DEV_CN" if [ "$RELEASE_PRE" == "false" ]; then echo "Generating $PACKAGE_JSON_REL ..." cat "$PACKAGE_JSON_TEMPLATE" | jq "$jq_arg" > "$OUTPUT_DIR/$PACKAGE_JSON_REL" # On MacOS the sed command won't skip the first match. Use gsed instead. - sed '0,/github\.com\/espressif\//!s|github\.com/espressif/|dl.espressif.cn/github_assets/espressif/|g' "$OUTPUT_DIR/$PACKAGE_JSON_REL" > "$OUTPUT_DIR/$PACKAGE_JSON_REL_CN" + sed '0,/github\.com\//!s|github\.com/|dl.espressif.cn/github_assets/|g' "$OUTPUT_DIR/$PACKAGE_JSON_REL" > "$OUTPUT_DIR/$PACKAGE_JSON_REL_CN" + python "$SCRIPTS_DIR/release_append_cn.py" "$OUTPUT_DIR/$PACKAGE_JSON_REL_CN" fi # Figure out the last release or pre-release @@ -456,14 +458,14 @@ echo "Uploading $PACKAGE_JSON_DEV ..." echo "Download URL: $(git_safe_upload_asset "$OUTPUT_DIR/$PACKAGE_JSON_DEV")" echo "Pages URL: $(git_safe_upload_to_pages "$PACKAGE_JSON_DEV" "$OUTPUT_DIR/$PACKAGE_JSON_DEV")" echo "Download CN URL: $(git_safe_upload_asset "$OUTPUT_DIR/$PACKAGE_JSON_DEV_CN")" -echo "Pages CN URL: $(git_safe_upload_to_pages "$PACKAGE_JSON_DEV" "$OUTPUT_DIR/$PACKAGE_JSON_DEV_CN")" +echo "Pages CN URL: $(git_safe_upload_to_pages "$PACKAGE_JSON_DEV_CN" "$OUTPUT_DIR/$PACKAGE_JSON_DEV_CN")" echo if [ "$RELEASE_PRE" == "false" ]; then echo "Uploading $PACKAGE_JSON_REL ..." echo "Download URL: $(git_safe_upload_asset "$OUTPUT_DIR/$PACKAGE_JSON_REL")" echo "Pages URL: $(git_safe_upload_to_pages "$PACKAGE_JSON_REL" "$OUTPUT_DIR/$PACKAGE_JSON_REL")" echo "Download CN URL: $(git_safe_upload_asset "$OUTPUT_DIR/$PACKAGE_JSON_REL_CN")" - echo "Pages CN URL: $(git_safe_upload_to_pages "$PACKAGE_JSON_REL" "$OUTPUT_DIR/$PACKAGE_JSON_REL_CN")" + echo "Pages CN URL: $(git_safe_upload_to_pages "$PACKAGE_JSON_REL_CN" "$OUTPUT_DIR/$PACKAGE_JSON_REL_CN")" echo fi diff --git a/.github/scripts/release_append_cn.py b/.github/scripts/release_append_cn.py new file mode 100755 index 00000000000..b29fe0c31ba --- /dev/null +++ b/.github/scripts/release_append_cn.py @@ -0,0 +1,56 @@ + +#!/usr/bin/env python3 + +# Arduino IDE provides by default a package file for the ESP32. This causes version conflicts +# when the user tries to use the JSON file with the Chinese mirrors. +# +# The downside is that the Arduino IDE will always warn the user that updates are available as it +# will consider the version from the Chinese mirrors as a pre-release version. +# +# This script is used to append "-cn" to all versions in the package_esp32_index_cn.json file so that +# the user can select the Chinese mirrors without conflicts. +# +# If Arduino ever stops providing the package_esp32_index.json file by default, +# this script can be removed and the tags reverted. + +import json + +def append_cn_to_versions(obj): + if isinstance(obj, dict): + # dfu-util comes from arduino.cc and not from the Chinese mirrors, so we skip it + if obj.get("name") == "dfu-util": + return + + for key, value in obj.items(): + if key == "version" and isinstance(value, str): + if not value.endswith("-cn"): + obj[key] = value + "-cn" + else: + append_cn_to_versions(value) + + elif isinstance(obj, list): + for item in obj: + append_cn_to_versions(item) + +def process_json_file(input_path, output_path=None): + with open(input_path, "r", encoding="utf-8") as f: + data = json.load(f) + + append_cn_to_versions(data) + + if output_path is None: + output_path = input_path + + with open(output_path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2) + + print(f"Updated JSON written to {output_path}") + +if __name__ == "__main__": + import sys + if len(sys.argv) < 2: + print("Usage: python release_append_cn.py input.json [output.json]") + else: + input_file = sys.argv[1] + output_file = sys.argv[2] if len(sys.argv) > 2 else None + process_json_file(input_file, output_file) diff --git a/docs/en/installing.rst b/docs/en/installing.rst index 35342020864..3ca0881c398 100644 --- a/docs/en/installing.rst +++ b/docs/en/installing.rst @@ -70,6 +70,8 @@ To start the installation process using the Boards Manager, follow these steps: :figclass: align-center - Open Boards Manager from Tools > Board menu and install *esp32* platform (and do not forget to select your ESP32 board from Tools > Board menu after installation). + Users in China must select the package version with the "-cn" suffix and perform updates manually. + Automatic updates are not supported in this region, as they target the default package without the "-cn" suffix, resulting in download failures. .. figure:: ../_static/install_guide_boards_manager_esp32.png :align: center From d71135e2ca72cef49ea417d00ae821b35cf95da3 Mon Sep 17 00:00:00 2001 From: whatsABetterNick Date: Tue, 10 Jun 2025 18:27:47 +0800 Subject: [PATCH 18/42] Fix(I2S example): make fix to the ESP32 I2S simple tone example (#10954) * made some fix to the ESP32 I2S simple tone example * edit the I2S - simple tone example * edit the I2S - simple tone example * some edit * edit comment * edit * edit * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../examples/Simple_tone/Simple_tone.ino | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/libraries/ESP_I2S/examples/Simple_tone/Simple_tone.ino b/libraries/ESP_I2S/examples/Simple_tone/Simple_tone.ino index 935aa4bc50f..bba7d4f4d9d 100644 --- a/libraries/ESP_I2S/examples/Simple_tone/Simple_tone.ino +++ b/libraries/ESP_I2S/examples/Simple_tone/Simple_tone.ino @@ -24,10 +24,17 @@ 2nd September 2021 Lucas Saavedra Vaz (lucasssvaz) 22nd December 2023 + anon + 10nd February 2025 */ #include +// The GPIO pins are not fixed, most other pins could be used for the I2S function. +#define I2S_LRC 25 +#define I2S_BCLK 5 +#define I2S_DIN 26 + const int frequency = 440; // frequency of square wave in Hz const int amplitude = 500; // amplitude of square wave const int sampleRate = 8000; // sample rate in Hz @@ -36,10 +43,10 @@ i2s_data_bit_width_t bps = I2S_DATA_BIT_WIDTH_16BIT; i2s_mode_t mode = I2S_MODE_STD; i2s_slot_mode_t slot = I2S_SLOT_MODE_STEREO; -const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave +const unsigned int halfWavelength = sampleRate / frequency / 2; // half wavelength of square wave int32_t sample = amplitude; // current sample value -int count = 0; +unsigned int count = 0; I2SClass i2s; @@ -47,6 +54,8 @@ void setup() { Serial.begin(115200); Serial.println("I2S simple tone"); + i2s.setPins(I2S_BCLK, I2S_LRC, I2S_DIN); + // start I2S at the sample rate with 16-bits per sample if (!i2s.begin(mode, sampleRate, bps, slot)) { Serial.println("Failed to initialize I2S!"); @@ -60,8 +69,13 @@ void loop() { sample = -1 * sample; } - i2s.write(sample); // Right channel - i2s.write(sample); // Left channel + // Left channel, the low 8 bits then high 8 bits + i2s.write(sample); + i2s.write(sample >> 8); + + // Right channel, the low 8 bits then high 8 bits + i2s.write(sample); + i2s.write(sample >> 8); // increment the counter for the next sample count++; From 422e52684b824a3fde85989ea90abf6624f2b0eb Mon Sep 17 00:00:00 2001 From: Paula Scharf <48286621+PaulaScharf@users.noreply.github.com> Date: Tue, 10 Jun 2025 13:29:27 +0200 Subject: [PATCH 19/42] fix(msc): remove weak function declaration of tud_msc_is_writable_cb (#11353) Co-authored-by: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> --- cores/esp32/esp32-hal-tinyusb.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/cores/esp32/esp32-hal-tinyusb.c b/cores/esp32/esp32-hal-tinyusb.c index 0991e08d27f..30a827baa01 100644 --- a/cores/esp32/esp32-hal-tinyusb.c +++ b/cores/esp32/esp32-hal-tinyusb.c @@ -466,9 +466,6 @@ __attribute__((weak)) int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint __attribute__((weak)) int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) { return -1; } -__attribute__((weak)) bool tud_msc_is_writable_cb(uint8_t lun) { - return false; -} #endif #if CFG_TUD_NCM __attribute__((weak)) bool tud_network_recv_cb(const uint8_t *src, uint16_t size) { From 228393708d49f20f9c83dfbc0bd51300464d873d Mon Sep 17 00:00:00 2001 From: SooDragon <82627949+SooDragon@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:12:33 +0900 Subject: [PATCH 20/42] fix: Update Pin compatability (#11473) fix: Update Pin compatability --- variants/Geekble_ESP32C3/pins_arduino.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/variants/Geekble_ESP32C3/pins_arduino.h b/variants/Geekble_ESP32C3/pins_arduino.h index 660313ce849..56bda115a7e 100644 --- a/variants/Geekble_ESP32C3/pins_arduino.h +++ b/variants/Geekble_ESP32C3/pins_arduino.h @@ -22,6 +22,13 @@ static const uint8_t MOSI = 6; static const uint8_t MISO = 5; static const uint8_t SCK = 4; +static const uint8_t D5 = 5; +static const uint8_t D6 = 6; +static const uint8_t D7 = 7; +static const uint8_t D8 = 8; +static const uint8_t D9 = 9; +static const uint8_t D10 = 10; + static const uint8_t A0 = 0; static const uint8_t A1 = 1; static const uint8_t A2 = 2; From ef995b6564612f300ed1f32627aa78a1eb65d614 Mon Sep 17 00:00:00 2001 From: Sugar Glider Date: Mon, 16 Jun 2025 07:15:54 -0300 Subject: [PATCH 21/42] feat(openthread): adds native api (#11474) * feat(openthread): adds native api * feat(openthread): adds source code to CMakeLists.txt * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- CMakeLists.txt | 1 + libraries/OpenThread/README.md | 201 +++++++++- .../examples/{ => CLI}/COAP/coap_lamp/ci.json | 0 .../{ => CLI}/COAP/coap_lamp/coap_lamp.ino | 9 +- .../{ => CLI}/COAP/coap_switch/ci.json | 0 .../COAP/coap_switch/coap_switch.ino | 9 +- .../{ => CLI}/SimpleCLI/SimpleCLI.ino | 5 +- .../examples/{ => CLI}/SimpleCLI/ci.json | 0 .../{ => CLI}/SimpleNode/SimpleNode.ino | 9 +- .../examples/{ => CLI}/SimpleNode/ci.json | 0 .../ExtendedRouterNode/ExtendedRouterNode.ino | 9 +- .../ExtendedRouterNode/ci.json | 0 .../LeaderNode/LeaderNode.ino | 9 +- .../SimpleThreadNetwork/LeaderNode/ci.json | 0 .../RouterNode/RouterNode.ino | 9 +- .../SimpleThreadNetwork/RouterNode/ci.json | 0 .../{ => CLI}/ThreadScan/ThreadScan.ino | 7 +- .../examples/{ => CLI}/ThreadScan/ci.json | 0 .../examples/{ => CLI}/onReceive/ci.json | 0 .../{ => CLI}/onReceive/onReceive.ino | 3 +- .../LeaderNode/LeaderNode.ino | 34 ++ .../SimpleThreadNetwork/LeaderNode/ci.json | 6 + .../RouterNode/RouterNode.ino | 29 ++ .../SimpleThreadNetwork/RouterNode/ci.json | 6 + libraries/OpenThread/keywords.txt | 26 ++ libraries/OpenThread/src/OThread.cpp | 364 ++++++++++++++++++ libraries/OpenThread/src/OThread.h | 107 +++++ libraries/OpenThread/src/OThreadCLI.cpp | 156 ++------ libraries/OpenThread/src/OThreadCLI.h | 6 +- libraries/OpenThread/src/OThreadCLI_Util.cpp | 22 +- libraries/OpenThread/src/OThreadCLI_Util.h | 11 - 31 files changed, 833 insertions(+), 205 deletions(-) rename libraries/OpenThread/examples/{ => CLI}/COAP/coap_lamp/ci.json (100%) rename libraries/OpenThread/examples/{ => CLI}/COAP/coap_lamp/coap_lamp.ino (95%) rename libraries/OpenThread/examples/{ => CLI}/COAP/coap_switch/ci.json (100%) rename libraries/OpenThread/examples/{ => CLI}/COAP/coap_switch/coap_switch.ino (95%) rename libraries/OpenThread/examples/{ => CLI}/SimpleCLI/SimpleCLI.ino (89%) rename libraries/OpenThread/examples/{ => CLI}/SimpleCLI/ci.json (100%) rename libraries/OpenThread/examples/{ => CLI}/SimpleNode/SimpleNode.ino (82%) rename libraries/OpenThread/examples/{ => CLI}/SimpleNode/ci.json (100%) rename libraries/OpenThread/examples/{ => CLI}/SimpleThreadNetwork/ExtendedRouterNode/ExtendedRouterNode.ino (89%) rename libraries/OpenThread/examples/{ => CLI}/SimpleThreadNetwork/ExtendedRouterNode/ci.json (100%) rename libraries/OpenThread/examples/{ => CLI}/SimpleThreadNetwork/LeaderNode/LeaderNode.ino (93%) rename libraries/OpenThread/examples/{ => CLI}/SimpleThreadNetwork/LeaderNode/ci.json (100%) rename libraries/OpenThread/examples/{ => CLI}/SimpleThreadNetwork/RouterNode/RouterNode.ino (92%) rename libraries/OpenThread/examples/{ => CLI}/SimpleThreadNetwork/RouterNode/ci.json (100%) rename libraries/OpenThread/examples/{ => CLI}/ThreadScan/ThreadScan.ino (89%) rename libraries/OpenThread/examples/{ => CLI}/ThreadScan/ci.json (100%) rename libraries/OpenThread/examples/{ => CLI}/onReceive/ci.json (100%) rename libraries/OpenThread/examples/{ => CLI}/onReceive/onReceive.ino (96%) create mode 100644 libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/LeaderNode.ino create mode 100644 libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/ci.json create mode 100644 libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/RouterNode.ino create mode 100644 libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/ci.json create mode 100644 libraries/OpenThread/src/OThread.cpp create mode 100644 libraries/OpenThread/src/OThread.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d53d612962d..e8f44ac5ee0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,6 +165,7 @@ set(ARDUINO_LIBRARY_LittleFS_SRCS libraries/LittleFS/src/LittleFS.cpp) set(ARDUINO_LIBRARY_NetBIOS_SRCS libraries/NetBIOS/src/NetBIOS.cpp) set(ARDUINO_LIBRARY_OpenThread_SRCS + libraries/OpenThread/src/OThread.cpp libraries/OpenThread/src/OThreadCLI.cpp libraries/OpenThread/src/OThreadCLI_Util.cpp) diff --git a/libraries/OpenThread/README.md b/libraries/OpenThread/README.md index cd9deb9ebf6..8d0386f34fb 100644 --- a/libraries/OpenThread/README.md +++ b/libraries/OpenThread/README.md @@ -1,9 +1,177 @@ | Supported Targets | ESP32-C6 | ESP32-H2 | | ----------------- | -------- | -------- | -# ESP32 Arduino OpenThreadCLI +# General View -The `OpenThreadCLI` class is an Arduino API for interacting with the OpenThread Command Line Interface (CLI). It allows you to manage and configure the Thread stack using a command-line interface. +This Arduino OpenThread Library allows using ESP OpenThread implementation using CLI and/or Native OpenThread API. + +The Library implements 3 C++ Classes: +- `OThread` Class for Native OpenThread API +- `OThreadCLI` Class for CLI OpenThread API +- `DataSet` Class for OpenThread dataset manipulation using Native `OThread` Class + +# ESP32 Arduino OpenThread Native + +The `OThread` class provides methods for managing the OpenThread instance and controlling the Thread network. It allows you to initialize, start, stop, and manage the Thread network using native OpenThread APIs. + +## Class Definition + +```cpp +class OpenThread { + public: + static bool otStarted; // Indicates whether the OpenThread stack is running. + + // Get the current Thread device role (e.g., Leader, Router, Child, etc.). + static ot_device_role_t otGetDeviceRole(); + + // Get the current Thread device role as a string. + static const char *otGetStringDeviceRole(); + + // Print network information (e.g., network name, channel, PAN ID) to the specified stream. + static void otPrintNetworkInformation(Stream &output); + + OpenThread(); + ~OpenThread(); + + // Returns true if the OpenThread stack is running. + operator bool() const; + + // Initialize the OpenThread stack. + static void begin(bool OThreadAutoStart = true); + + // Deinitialize the OpenThread stack. + static void end(); + + // Start the Thread network. + void start(); + + // Stop the Thread network. + void stop(); + + // Bring up the Thread network interface (equivalent to "ifconfig up"). + void networkInterfaceUp(); + + // Bring down the Thread network interface (equivalent to "ifconfig down"). + void networkInterfaceDown(); + + // Commit a dataset to the OpenThread instance. + void commitDataSet(const DataSet &dataset); + +private: + static otInstance *mInstance; // Pointer to the OpenThread instance. + DataSet mCurrentDataSet; // Current dataset being used by the OpenThread instance. +}; + +extern OpenThread OThread; +``` +## Class Overview + +The `OThread` class provides a simple and intuitive interface for managing the OpenThread stack and Thread network. It abstracts the complexity of the OpenThread APIs and provides Arduino-style methods for common operations. + +## Public Methods +### Initialization and Deinitialization +- `begin(bool OThreadAutoStart = true)`: Initializes the OpenThread stack. If `OThreadAutoStart` is `true`, the Thread network will start automatically using NVS data. +- `end()`: Deinitializes the OpenThread stack and releases resources. +### Thread Network Control +- `start()`: Starts the Thread network. This is equivalent to the CLI command "thread start". +- `stop()`: Stops the Thread network. This is equivalent to the CLI command "thread stop". +### Network Interface Control +- `networkInterfaceUp()`: Brings up the Thread network interface. This is equivalent to the CLI command "ifconfig up". +- `networkInterfaceDown()`: Brings down the Thread network interface. This is equivalent to the CLI command "ifconfig down". +### Dataset Management +- `commitDataSet(const DataSet &dataset)`: Commits a dataset to the OpenThread instance. This is used to configure the Thread network with specific parameters (e.g., network name, channel, PAN ID). +### Network Information +- `otGetDeviceRole()`: Returns the current Thread device role as an `ot_device_role_t` enum (e.g., `OT_ROLE_LEADER`, `OT_ROLE_ROUTER`). +- `otGetStringDeviceRole()`: Returns the current Thread device role as a string (e.g., "Leader", "Router"). +- `otPrintNetworkInformation(Stream &output)`: Prints the current network information (e.g., network name, channel, PAN ID) to the specified stream. + +## Key Features +- **Initialization and Cleanup**: Easily initialize and deinitialize the OpenThread stack. +- **Network Control**: Start and stop the Thread network with simple method calls. +- **Dataset Management**: Configure the Thread network using the `DataSet` class and commit it to the OpenThread instance. +- **Network Information**: Retrieve and print the current network information and device role. + +## Notes +- The `OThread` class is designed to simplify the use of OpenThread APIs in Arduino sketches. +- It works seamlessly with the DataSet class for managing Thread network configurations. +- Ensure that the OpenThread stack is initialized (`OThread.begin()`) before calling other methods. + +This documentation provides a comprehensive overview of the `OThread` class, its methods, and example usage. It is designed to help developers quickly integrate OpenThread functionality into their Arduino projects. + +# DataSet Class + +The `DataSet` class provides a structured way to manage and configure Thread network datasets using native OpenThread APIs. It allows you to set and retrieve network parameters such as the network name, channel, PAN ID, and more. The `DataSet` class works seamlessly with the `OThread` class to apply these configurations to the OpenThread instance. + +## Class Definition + +```cpp +class DataSet { +public: + DataSet(); + void clear(); + void initNew(); + const otOperationalDataset &getDataset() const; + + // Setters + void setNetworkName(const char *name); + void setExtendedPanId(const uint8_t *extPanId); + void setNetworkKey(const uint8_t *key); + void setChannel(uint8_t channel); + void setPanId(uint16_t panId); + + // Getters + const char *getNetworkName() const; + const uint8_t *getExtendedPanId() const; + const uint8_t *getNetworkKey() const; + uint8_t getChannel() const; + uint16_t getPanId() const; + + // Apply the dataset to the OpenThread instance + void apply(otInstance *instance); + +private: + otOperationalDataset mDataset; // Internal representation of the dataset +}; +``` + +## Class Overview +The DataSet` class simplifies the management of Thread network datasets by providing intuitive methods for setting, retrieving, and applying network parameters. It abstracts the complexity of the OpenThread dataset APIs and provides Arduino-style methods for common operations. + +## Public Methods +### Initialization +- `DataSet()`: Constructor that initializes an empty dataset. +- `void clear()`: Clears the dataset, resetting all fields to their default values. +- `void initNew()`: Initializes a new dataset with default values (equivalent to the CLI command dataset init new). +### Setters +- `void setNetworkName(const char *name)`: Sets the network name. +- `void setExtendedPanId(const uint8_t *extPanId)`: Sets the extended PAN ID. +- `void setNetworkKey(const uint8_t *key)`: Sets the network key. +- `void setChannel(uint8_t channel)`: Sets the channel. +- `void setPanId(uint16_t panId)`: Sets the PAN ID. +### Getters +- `const char *getNetworkName() const`: Retrieves the network name. +- `const uint8_t *getExtendedPanId() const`: Retrieves the extended PAN ID. +- `const uint8_t *getNetworkKey() const`: Retrieves the network key. +- `uint8_t getChannel() const`: Retrieves the channel. +- `uint16_t getPanId() const`: Retrieves the PAN ID. +### Dataset Application +- `void apply(otInstance *instance)`: Applies the dataset to the specified OpenThread instance. + +## Key Features +- **Dataset Initialization**: Easily initialize a new dataset with default values using initNew(). +- **Custom Configuration**: Set custom network parameters such as the network name, channel, and PAN ID using setter methods. +- **Dataset Application**: Apply the configured dataset to the OpenThread instance using apply(). + +** Notes +- The `DataSet` class is designed to work seamlessly with the `OThread` class for managing Thread network configurations. +- Ensure that the OpenThread stack is initialized (`OThread.begin()`) before applying a dataset. +- The initNew()`` method provides default values for the dataset, which can be customized using the setter methods. + +This documentation provides a comprehensive overview of the `DataSet` class, its methods, and example usage. It is designed to help developers easily manage Thread network configurations in their Arduino projects. + +# OpenThreadCLI Class + +The `OpenThreadCLI` class is an Arduino API for interacting with the OpenThread Command Line Interface (CLI). It allows you to send commands to the OpenThread stack and receive responses. This class is designed to simplify the use of OpenThread CLI commands in Arduino sketches. There is one main class called `OpenThreadCLI` and a global object used to operate OpenThread CLI, called `OThreadCLI`.\ Some [helper functions](helper_functions.md) were made available for working with the OpenThread CLI environment. @@ -20,7 +188,7 @@ Below are the details of the class: class OpenThreadCLI : public Stream { private: static size_t setBuffer(QueueHandle_t &queue, size_t len); - bool otStarted = false; + static bool otCLIStarted = false; public: OpenThreadCLI(); @@ -59,14 +227,35 @@ extern OpenThreadCLI OThreadCLI; - You can customize the console behavior by adjusting parameters such as echoback and buffer sizes. ## Public Methods +### Initialization and Deinitialization +- `begin()`: Initializes the OpenThread stack (optional auto-start). +- `end()`: Deinitializes the OpenThread stack and releases resources. +### Console Management - `startConsole(Stream& otStream, bool echoback = true, const char* prompt = "ot> ")`: Starts the OpenThread console with the specified stream, echoback option, and prompt. - `stopConsole()`: Stops the OpenThread console. - `setPrompt(char* prompt)`: Changes the console prompt (set to NULL for an empty prompt). - `setEchoBack(bool echoback)`: Changes the console echoback option. - `setStream(Stream& otStream)`: Changes the console Stream object. - `onReceive(OnReceiveCb_t func)`: Sets a callback function to handle complete lines of output from the OT CLI. -- `begin(bool OThreadAutoStart = true)`: Initializes the OpenThread stack (optional auto-start). -- `end()`: Deinitializes the OpenThread stack. +### Buffer Management - `setTxBufferSize(size_t tx_queue_len)`: Sets the transmit buffer size (default is 256 bytes). - `setRxBufferSize(size_t rx_queue_len)`: Sets the receive buffer size (default is 1024 bytes). -- `write(uint8_t)`, `available()`, `read()`, `peek()`, `flush()`: Standard Stream methods implementation for OpenThread CLI object. +### Stream Methods +- `write(uint8_t)`: Writes a byte to the CLI. +- `available()`: Returns the number of bytes available to read. +- `read()`: Reads a byte from the CLI. +- `peek()`: Returns the next byte without removing it from the buffer. +- `flush()`: Flushes the CLI buffer. + +## Key Features +- **Arduino Stream Compatibility**: Inherits from the Stream class, making it compatible with Arduino's standard I/O functions. +- **Customizable Console**: Allows customization of the CLI prompt, echoback behavior, and buffer sizes. +- **Callback Support**: Provides a callback mechanism to handle CLI responses asynchronously. +- **Seamless Integration**: Designed to work seamlessly with the OThread and DataSet classes + +## Notes +- The `OThreadCLI` class is designed to simplify the use of OpenThread CLI commands in Arduino sketches. +- It works seamlessly with the `OThread` and `DataSet` classes for managing Thread networks. +- Ensure that the OpenThread stack is initialized (`OThreadCLI.begin()`) before starting the CLI console. + +This documentation provides a comprehensive overview of the `OThreadCLI` class, its methods, and example usage. It is designed to help developers easily integrate OpenThread CLI functionality into their Arduino projects. diff --git a/libraries/OpenThread/examples/COAP/coap_lamp/ci.json b/libraries/OpenThread/examples/CLI/COAP/coap_lamp/ci.json similarity index 100% rename from libraries/OpenThread/examples/COAP/coap_lamp/ci.json rename to libraries/OpenThread/examples/CLI/COAP/coap_lamp/ci.json diff --git a/libraries/OpenThread/examples/COAP/coap_lamp/coap_lamp.ino b/libraries/OpenThread/examples/CLI/COAP/coap_lamp/coap_lamp.ino similarity index 95% rename from libraries/OpenThread/examples/COAP/coap_lamp/coap_lamp.ino rename to libraries/OpenThread/examples/CLI/COAP/coap_lamp/coap_lamp.ino index 51483bb4c7c..bcaf8ae9793 100644 --- a/libraries/OpenThread/examples/COAP/coap_lamp/coap_lamp.ino +++ b/libraries/OpenThread/examples/CLI/COAP/coap_lamp/coap_lamp.ino @@ -75,18 +75,18 @@ bool otDeviceSetup(const char **otSetupCmds, uint8_t nCmds1, const char **otCoap Serial.println("OpenThread started.\r\nWaiting for activating correct Device Role."); // wait for the expected Device Role to start uint8_t tries = 24; // 24 x 2.5 sec = 1 min - while (tries && otGetDeviceRole() != expectedRole) { + while (tries && OThread.otGetDeviceRole() != expectedRole) { Serial.print("."); delay(2500); tries--; } Serial.println(); if (!tries) { - log_e("Sorry, Device Role failed by timeout! Current Role: %s.", otGetStringDeviceRole()); + log_e("Sorry, Device Role failed by timeout! Current Role: %s.", OThread.otGetStringDeviceRole()); rgbLedWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed! return false; } - Serial.printf("Device is %s.\r\n", otGetStringDeviceRole()); + Serial.printf("Device is %s.\r\n", OThread.otGetStringDeviceRole()); for (i = 0; i < nCmds2; i++) { if (!otExecCommand(otCoapCmds[i * 2], otCoapCmds[i * 2 + 1])) { break; @@ -151,7 +151,8 @@ void setup() { Serial.begin(115200); // LED starts RED, indicating not connected to Thread network. rgbLedWrite(RGB_BUILTIN, 64, 0, 0); - OThreadCLI.begin(false); // No AutoStart is necessary + OThread.begin(false); // No AutoStart is necessary + OThreadCLI.begin(); OThreadCLI.setTimeout(250); // waits 250ms for the OpenThread CLI response setupNode(); // LED goes Green when all is ready and Red when failed. diff --git a/libraries/OpenThread/examples/COAP/coap_switch/ci.json b/libraries/OpenThread/examples/CLI/COAP/coap_switch/ci.json similarity index 100% rename from libraries/OpenThread/examples/COAP/coap_switch/ci.json rename to libraries/OpenThread/examples/CLI/COAP/coap_switch/ci.json diff --git a/libraries/OpenThread/examples/COAP/coap_switch/coap_switch.ino b/libraries/OpenThread/examples/CLI/COAP/coap_switch/coap_switch.ino similarity index 95% rename from libraries/OpenThread/examples/COAP/coap_switch/coap_switch.ino rename to libraries/OpenThread/examples/CLI/COAP/coap_switch/coap_switch.ino index aac5db0bc82..bacac47ddeb 100644 --- a/libraries/OpenThread/examples/COAP/coap_switch/coap_switch.ino +++ b/libraries/OpenThread/examples/CLI/COAP/coap_switch/coap_switch.ino @@ -69,18 +69,18 @@ bool otDeviceSetup( Serial.println("OpenThread started.\r\nWaiting for activating correct Device Role."); // wait for the expected Device Role to start uint8_t tries = 24; // 24 x 2.5 sec = 1 min - while (tries && otGetDeviceRole() != expectedRole1 && otGetDeviceRole() != expectedRole2) { + while (tries && OThread.otGetDeviceRole() != expectedRole1 && OThread.otGetDeviceRole() != expectedRole2) { Serial.print("."); delay(2500); tries--; } Serial.println(); if (!tries) { - log_e("Sorry, Device Role failed by timeout! Current Role: %s.", otGetStringDeviceRole()); + log_e("Sorry, Device Role failed by timeout! Current Role: %s.", OThread.otGetStringDeviceRole()); rgbLedWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed! return false; } - Serial.printf("Device is %s.\r\n", otGetStringDeviceRole()); + Serial.printf("Device is %s.\r\n", OThread.otGetStringDeviceRole()); for (i = 0; i < nCmds2; i++) { if (!otExecCommand(otCoapCmds[i * 2], otCoapCmds[i * 2 + 1])) { break; @@ -176,7 +176,8 @@ void setup() { Serial.begin(115200); // LED starts RED, indicating not connected to Thread network. rgbLedWrite(RGB_BUILTIN, 64, 0, 0); - OThreadCLI.begin(false); // No AutoStart is necessary + OThread.begin(false); // No AutoStart is necessary + OThreadCLI.begin(); OThreadCLI.setTimeout(250); // waits 250ms for the OpenThread CLI response setupNode(); // LED goes and keeps Blue when all is ready and Red when failed. diff --git a/libraries/OpenThread/examples/SimpleCLI/SimpleCLI.ino b/libraries/OpenThread/examples/CLI/SimpleCLI/SimpleCLI.ino similarity index 89% rename from libraries/OpenThread/examples/SimpleCLI/SimpleCLI.ino rename to libraries/OpenThread/examples/CLI/SimpleCLI/SimpleCLI.ino index feef800c0fa..dec3aaeb218 100644 --- a/libraries/OpenThread/examples/SimpleCLI/SimpleCLI.ino +++ b/libraries/OpenThread/examples/CLI/SimpleCLI/SimpleCLI.ino @@ -1,4 +1,4 @@ -// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// 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. @@ -26,7 +26,8 @@ void setup() { Serial.begin(115200); - OThreadCLI.begin(false); // No AutoStart - fresh start + OThread.begin(false); // No AutoStart - fresh start + OThreadCLI.begin(); Serial.println("OpenThread CLI started - type 'help' for a list of commands."); OThreadCLI.startConsole(Serial); } diff --git a/libraries/OpenThread/examples/SimpleCLI/ci.json b/libraries/OpenThread/examples/CLI/SimpleCLI/ci.json similarity index 100% rename from libraries/OpenThread/examples/SimpleCLI/ci.json rename to libraries/OpenThread/examples/CLI/SimpleCLI/ci.json diff --git a/libraries/OpenThread/examples/SimpleNode/SimpleNode.ino b/libraries/OpenThread/examples/CLI/SimpleNode/SimpleNode.ino similarity index 82% rename from libraries/OpenThread/examples/SimpleNode/SimpleNode.ino rename to libraries/OpenThread/examples/CLI/SimpleNode/SimpleNode.ino index 95bf7a2401a..d17b692cc74 100644 --- a/libraries/OpenThread/examples/SimpleNode/SimpleNode.ino +++ b/libraries/OpenThread/examples/CLI/SimpleNode/SimpleNode.ino @@ -1,4 +1,4 @@ -// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// 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. @@ -35,12 +35,13 @@ void setup() { Serial.begin(115200); - OThreadCLI.begin(); // AutoStart using Thread default settings - otPrintNetworkInformation(Serial); // Print Current Thread Network Information + OThread.begin(); // AutoStart using Thread default settings + OThreadCLI.begin(); + OThread.otPrintNetworkInformation(Serial); // Print Current Thread Network Information } void loop() { Serial.print("Thread Node State: "); - Serial.println(otGetStringDeviceRole()); + Serial.println(OThread.otGetStringDeviceRole()); delay(5000); } diff --git a/libraries/OpenThread/examples/SimpleNode/ci.json b/libraries/OpenThread/examples/CLI/SimpleNode/ci.json similarity index 100% rename from libraries/OpenThread/examples/SimpleNode/ci.json rename to libraries/OpenThread/examples/CLI/SimpleNode/ci.json diff --git a/libraries/OpenThread/examples/SimpleThreadNetwork/ExtendedRouterNode/ExtendedRouterNode.ino b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ExtendedRouterNode.ino similarity index 89% rename from libraries/OpenThread/examples/SimpleThreadNetwork/ExtendedRouterNode/ExtendedRouterNode.ino rename to libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ExtendedRouterNode.ino index 4fc8a921584..40f046aeab5 100644 --- a/libraries/OpenThread/examples/SimpleThreadNetwork/ExtendedRouterNode/ExtendedRouterNode.ino +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ExtendedRouterNode.ino @@ -1,4 +1,4 @@ -// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// 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. @@ -22,7 +22,8 @@ bool otStatus = true; void setup() { Serial.begin(115200); - OThreadCLI.begin(false); // No AutoStart - fresh start + OThread.begin(false); // No AutoStart - fresh start + OThreadCLI.begin(); Serial.println("Setting up OpenThread Node as Router/Child"); Serial.println("Make sure the Leader Node is already running"); @@ -39,7 +40,7 @@ void setup() { } // wait for the node to enter in the router state uint32_t timeout = millis() + 90000; // waits 90 seconds to - while (otGetDeviceRole() != OT_ROLE_CHILD && otGetDeviceRole() != OT_ROLE_ROUTER) { + while (OThread.otGetDeviceRole() != OT_ROLE_CHILD && OThread.otGetDeviceRole() != OT_ROLE_ROUTER) { Serial.print("."); if (millis() > timeout) { Serial.println("\r\n\t===> Timeout! Failed."); @@ -70,7 +71,7 @@ void loop() { if (otStatus) { Serial.println("Thread NetworkInformation: "); Serial.println("---------------------------"); - otPrintNetworkInformation(Serial); + OThread.otPrintNetworkInformation(Serial); Serial.println("---------------------------"); } else { Serial.println("Some OpenThread operation has failed..."); diff --git a/libraries/OpenThread/examples/SimpleThreadNetwork/ExtendedRouterNode/ci.json b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ci.json similarity index 100% rename from libraries/OpenThread/examples/SimpleThreadNetwork/ExtendedRouterNode/ci.json rename to libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ci.json diff --git a/libraries/OpenThread/examples/SimpleThreadNetwork/LeaderNode/LeaderNode.ino b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/LeaderNode.ino similarity index 93% rename from libraries/OpenThread/examples/SimpleThreadNetwork/LeaderNode/LeaderNode.ino rename to libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/LeaderNode.ino index 7b709717692..a945a5c8f77 100644 --- a/libraries/OpenThread/examples/SimpleThreadNetwork/LeaderNode/LeaderNode.ino +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/LeaderNode.ino @@ -1,4 +1,4 @@ -// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// 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. @@ -35,7 +35,8 @@ otInstance *aInstance = NULL; void setup() { Serial.begin(115200); - OThreadCLI.begin(false); // No AutoStart - fresh start + OThread.begin(false); // No AutoStart - fresh start + OThreadCLI.begin(); Serial.println(); Serial.println("Setting up OpenThread Node as Leader"); aInstance = esp_openthread_get_instance(); @@ -51,11 +52,11 @@ void setup() { void loop() { Serial.println("============================================="); Serial.print("Thread Node State: "); - Serial.println(otGetStringDeviceRole()); + Serial.println(OThread.otGetStringDeviceRole()); // Native OpenThread API calls: // wait until the node become Child or Router - if (otGetDeviceRole() == OT_ROLE_LEADER) { + if (OThread.otGetDeviceRole() == OT_ROLE_LEADER) { // Network Name const char *networkName = otThreadGetNetworkName(aInstance); Serial.printf("Network Name: %s\r\n", networkName); diff --git a/libraries/OpenThread/examples/SimpleThreadNetwork/LeaderNode/ci.json b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/ci.json similarity index 100% rename from libraries/OpenThread/examples/SimpleThreadNetwork/LeaderNode/ci.json rename to libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/ci.json diff --git a/libraries/OpenThread/examples/SimpleThreadNetwork/RouterNode/RouterNode.ino b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/RouterNode.ino similarity index 92% rename from libraries/OpenThread/examples/SimpleThreadNetwork/RouterNode/RouterNode.ino rename to libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/RouterNode.ino index 45475fa0c6a..f802bd7ef01 100644 --- a/libraries/OpenThread/examples/SimpleThreadNetwork/RouterNode/RouterNode.ino +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/RouterNode.ino @@ -1,4 +1,4 @@ -// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// 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. @@ -34,7 +34,8 @@ otInstance *aInstance = NULL; void setup() { Serial.begin(115200); - OThreadCLI.begin(false); // No AutoStart - fresh start + OThread.begin(false); // No AutoStart - fresh start + OThreadCLI.begin(); Serial.println(); Serial.println("Setting up OpenThread Node as Router/Child"); Serial.println("Make sure the Leader Node is already running"); @@ -51,11 +52,11 @@ void setup() { void loop() { Serial.println("============================================="); Serial.print("Thread Node State: "); - Serial.println(otGetStringDeviceRole()); + Serial.println(OThread.otGetStringDeviceRole()); // Native OpenThread API calls: // wait until the node become Child or Router - if (otGetDeviceRole() == OT_ROLE_CHILD || otGetDeviceRole() == OT_ROLE_ROUTER) { + if (OThread.otGetDeviceRole() == OT_ROLE_CHILD || OThread.otGetDeviceRole() == OT_ROLE_ROUTER) { // Network Name const char *networkName = otThreadGetNetworkName(aInstance); Serial.printf("Network Name: %s\r\n", networkName); diff --git a/libraries/OpenThread/examples/SimpleThreadNetwork/RouterNode/ci.json b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/ci.json similarity index 100% rename from libraries/OpenThread/examples/SimpleThreadNetwork/RouterNode/ci.json rename to libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/ci.json diff --git a/libraries/OpenThread/examples/ThreadScan/ThreadScan.ino b/libraries/OpenThread/examples/CLI/ThreadScan/ThreadScan.ino similarity index 89% rename from libraries/OpenThread/examples/ThreadScan/ThreadScan.ino rename to libraries/OpenThread/examples/CLI/ThreadScan/ThreadScan.ino index 9d0074bb180..87c29339fb7 100644 --- a/libraries/OpenThread/examples/ThreadScan/ThreadScan.ino +++ b/libraries/OpenThread/examples/CLI/ThreadScan/ThreadScan.ino @@ -1,4 +1,4 @@ -// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// 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. @@ -27,7 +27,8 @@ void setup() { Serial.begin(115200); - OThreadCLI.begin(true); // For scanning, AutoStart must be active, any setup + OThread.begin(true); // For scanning, AutoStart must be active, any setup + OThreadCLI.begin(); OThreadCLI.setTimeout(100); // Set a timeout for the CLI response Serial.println(); Serial.println("This sketch will continuously scan the Thread Local Network and all devices IEEE 802.15.4 compatible"); @@ -41,7 +42,7 @@ void loop() { Serial.println("Scan Failed..."); } delay(5000); - if (otGetDeviceRole() < OT_ROLE_CHILD) { + if (OThread.otGetDeviceRole() < OT_ROLE_CHILD) { Serial.println(); Serial.println("This device has not started Thread yet, bypassing Discovery Scan"); return; diff --git a/libraries/OpenThread/examples/ThreadScan/ci.json b/libraries/OpenThread/examples/CLI/ThreadScan/ci.json similarity index 100% rename from libraries/OpenThread/examples/ThreadScan/ci.json rename to libraries/OpenThread/examples/CLI/ThreadScan/ci.json diff --git a/libraries/OpenThread/examples/onReceive/ci.json b/libraries/OpenThread/examples/CLI/onReceive/ci.json similarity index 100% rename from libraries/OpenThread/examples/onReceive/ci.json rename to libraries/OpenThread/examples/CLI/onReceive/ci.json diff --git a/libraries/OpenThread/examples/onReceive/onReceive.ino b/libraries/OpenThread/examples/CLI/onReceive/onReceive.ino similarity index 96% rename from libraries/OpenThread/examples/onReceive/onReceive.ino rename to libraries/OpenThread/examples/CLI/onReceive/onReceive.ino index b37c2fc7931..f53cc33f5ec 100644 --- a/libraries/OpenThread/examples/onReceive/onReceive.ino +++ b/libraries/OpenThread/examples/CLI/onReceive/onReceive.ino @@ -39,7 +39,8 @@ void otReceivedLine() { void setup() { Serial.begin(115200); - OThreadCLI.begin(); // AutoStart + OThread.begin(); // AutoStart + OThreadCLI.begin(); OThreadCLI.onReceive(otReceivedLine); } diff --git a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/LeaderNode.ino b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/LeaderNode.ino new file mode 100644 index 00000000000..dfea9776838 --- /dev/null +++ b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/LeaderNode.ino @@ -0,0 +1,34 @@ +#include "OThread.h" + +OpenThread threadLeaderNode; +DataSet dataset; + +void setup() { + Serial.begin(115200); + + // Start OpenThread Stack - false for not using NVS dataset information + threadLeaderNode.begin(false); + + // Create a new Thread Network Dataset for a Leader Node + dataset.initNew(); + // Configure the dataset + dataset.setNetworkName("ESP_OpenThread"); + uint8_t extPanId[OT_EXT_PAN_ID_SIZE] = {0xDE, 0xAD, 0x00, 0xBE, 0xEF, 0x00, 0xCA, 0xFE}; + dataset.setExtendedPanId(extPanId); + uint8_t networkKey[OT_NETWORK_KEY_SIZE] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + dataset.setNetworkKey(networkKey); + dataset.setChannel(15); + dataset.setPanId(0x1234); + + // Apply the dataset and start the network + threadLeaderNode.commitDataSet(dataset); + threadLeaderNode.networkInterfaceUp(); + threadLeaderNode.start(); +} + +void loop() { + // Print network information every 5 seconds + Serial.println("=============================================="); + threadLeaderNode.otPrintNetworkInformation(Serial); + delay(5000); +} diff --git a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/ci.json b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/ci.json new file mode 100644 index 00000000000..2ee6af3490e --- /dev/null +++ b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/ci.json @@ -0,0 +1,6 @@ +{ + "requires": [ + "CONFIG_OPENTHREAD_ENABLED=y", + "CONFIG_SOC_IEEE802154_SUPPORTED=y" + ] +} diff --git a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/RouterNode.ino b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/RouterNode.ino new file mode 100644 index 00000000000..5ffa535ad51 --- /dev/null +++ b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/RouterNode.ino @@ -0,0 +1,29 @@ +#include "OThread.h" + +OpenThread threadChildNode; +DataSet dataset; + +void setup() { + Serial.begin(115200); + + // Start OpenThread Stack - false for not using NVS dataset information + threadChildNode.begin(false); + + // clear dataset + dataset.clear(); + // Configure the dataset with the same Network Key of the Leader Node + uint8_t networkKey[OT_NETWORK_KEY_SIZE] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + dataset.setNetworkKey(networkKey); + + // Apply the dataset and start the network + threadChildNode.commitDataSet(dataset); + threadChildNode.networkInterfaceUp(); + threadChildNode.start(); +} + +void loop() { + // Print network information every 5 seconds + Serial.println("=============================================="); + threadChildNode.otPrintNetworkInformation(Serial); + delay(5000); +} diff --git a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/ci.json b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/ci.json new file mode 100644 index 00000000000..2ee6af3490e --- /dev/null +++ b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/ci.json @@ -0,0 +1,6 @@ +{ + "requires": [ + "CONFIG_OPENTHREAD_ENABLED=y", + "CONFIG_SOC_IEEE802154_SUPPORTED=y" + ] +} diff --git a/libraries/OpenThread/keywords.txt b/libraries/OpenThread/keywords.txt index d7193de188d..b62c2c23ddc 100644 --- a/libraries/OpenThread/keywords.txt +++ b/libraries/OpenThread/keywords.txt @@ -7,7 +7,10 @@ ####################################### OThreadCLI KEYWORD1 +OThread KEYWORD1 OpenThreadCLI KEYWORD1 +OpenThread KEYWORD1 +DataSet KEYWORD1 ot_cmd_return_t KEYWORD1 ot_device_role_t KEYWORD1 @@ -35,6 +38,27 @@ otGetRespCmd KEYWORD2 otExecCommand KEYWORD2 otPrintRespCLI KEYWORD2 otPrintNetworkInformation KEYWORD2 +clear KEYWORD2 +initNew KEYWORD2 +getDataset KEYWORD2 +setNetworkName KEYWORD2 +getNetworkName KEYWORD2 +setExtendedPanId KEYWORD2 +getExtendedPanId KEYWORD2 +setNetworkKey KEYWORD2 +getNetworkKey KEYWORD2 +setChannel KEYWORD2 +getChannel KEYWORD2 +setPanId KEYWORD2 +getPanId KEYWORD2 +apply KEYWORD2 +otStarted KEYWORD2 +otCLIStarted KEYWORD2 +start KEYWORD2 +stop KEYWORD2 +networkInterfaceUp KEYWORD2 +networkInterfaceDown KEYWORD2 +commitDataSet KEYWORD2 ####################################### # Constants (LITERAL1) @@ -45,3 +69,5 @@ OT_ROLE_DETACHED LITERAL1 OT_ROLE_CHILD LITERAL1 OT_ROLE_ROUTER LITERAL1 OT_ROLE_LEADER LITERAL1 +OT_EXT_PAN_ID_SIZE LITERAL1 +OT_NETWORK_KEY_SIZE LITERAL1 diff --git a/libraries/OpenThread/src/OThread.cpp b/libraries/OpenThread/src/OThread.cpp new file mode 100644 index 00000000000..43d8ce02918 --- /dev/null +++ b/libraries/OpenThread/src/OThread.cpp @@ -0,0 +1,364 @@ +#include "OThread.h" +#if SOC_IEEE802154_SUPPORTED +#if CONFIG_OPENTHREAD_ENABLED + +#include "esp_err.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "esp_netif_types.h" +#include "esp_vfs_eventfd.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" + +#include "esp_netif_net_stack.h" +#include "esp_openthread_netif_glue.h" +#include "lwip/netif.h" + +static esp_openthread_platform_config_t ot_native_config; +static esp_netif_t *openthread_netif = NULL; + +const char *otRoleString[] = { + "Disabled", ///< The Thread stack is disabled. + "Detached", ///< Not currently participating in a Thread network/partition. + "Child", ///< The Thread Child role. + "Router", ///< The Thread Router role. + "Leader", ///< The Thread Leader role. + "Unknown", ///< Unknown role, not initialized or not started. +}; + +static TaskHandle_t s_ot_task = NULL; +#if CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM +static struct netif *ot_lwip_netif = NULL; +#endif + +#if CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM +extern "C" int lwip_hook_ip6_input(struct pbuf *p, struct netif *inp) { + if (ot_lwip_netif && ot_lwip_netif == inp) { + return 0; + } + if (ip6_addr_isany_val(inp->ip6_addr[0].u_addr.ip6)) { + // We don't have an LL address -> eat this packet here, so it won't get accepted on input netif + pbuf_free(p); + return 1; + } + return 0; +} +#endif + +static void ot_task_worker(void *aContext) { + esp_vfs_eventfd_config_t eventfd_config = { + .max_fds = 3, + }; + bool err = false; + if (ESP_OK != esp_event_loop_create_default()) { + log_e("Failed to create OpentThread event loop"); + err = true; + } + if (!err && ESP_OK != esp_netif_init()) { + log_e("Failed to initialize OpentThread netif"); + err = true; + } + if (!err && ESP_OK != esp_vfs_eventfd_register(&eventfd_config)) { + log_e("Failed to register OpentThread eventfd"); + err = true; + } + + // Initialize the OpenThread stack + if (!err && ESP_OK != esp_openthread_init(&ot_native_config)) { + log_e("Failed to initialize OpenThread stack"); + err = true; + } + if (!err) { + // Initialize the esp_netif bindings + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); + openthread_netif = esp_netif_new(&cfg); +#if CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM + // Get LwIP Netif + if (openthread_netif != NULL) { + ot_lwip_netif = (struct netif *)esp_netif_get_netif_impl(openthread_netif); + if (ot_lwip_netif == NULL) { + log_e("Failed to get OpenThread LwIP netif"); + } + } +#endif + } + if (!err && openthread_netif == NULL) { + log_e("Failed to create OpenThread esp_netif"); + err = true; + } + if (!err && ESP_OK != esp_netif_attach(openthread_netif, esp_openthread_netif_glue_init(&ot_native_config))) { + log_e("Failed to attach OpenThread esp_netif"); + err = true; + } + if (!err && ESP_OK != esp_netif_set_default_netif(openthread_netif)) { + log_e("Failed to set default OpenThread esp_netif"); + err = true; + } + if (!err) { + // only returns in case there is an OpenThread Stack failure... + esp_openthread_launch_mainloop(); + } + // Clean up + esp_openthread_netif_glue_deinit(); + esp_netif_destroy(openthread_netif); + esp_vfs_eventfd_unregister(); + vTaskDelete(NULL); +} + +// DataSet Implementation +DataSet::DataSet() { + memset(&mDataset, 0, sizeof(mDataset)); +} + +void DataSet::clear() { + memset(&mDataset, 0, sizeof(mDataset)); +} + +void DataSet::initNew() { + otInstance *mInstance = esp_openthread_get_instance(); + if (!mInstance) { + log_e("OpenThread not started. Please begin() it before initializing a new dataset."); + return; + } + clear(); + otDatasetCreateNewNetwork(mInstance, &mDataset); +} + +const otOperationalDataset &DataSet::getDataset() const { + return mDataset; +} + +void DataSet::setNetworkName(const char *name) { + strncpy(mDataset.mNetworkName.m8, name, sizeof(mDataset.mNetworkName.m8)); + mDataset.mComponents.mIsNetworkNamePresent = true; +} + +void DataSet::setExtendedPanId(const uint8_t *extPanId) { + memcpy(mDataset.mExtendedPanId.m8, extPanId, OT_EXT_PAN_ID_SIZE); + mDataset.mComponents.mIsExtendedPanIdPresent = true; +} + +void DataSet::setNetworkKey(const uint8_t *key) { + memcpy(mDataset.mNetworkKey.m8, key, OT_NETWORK_KEY_SIZE); + mDataset.mComponents.mIsNetworkKeyPresent = true; +} + +void DataSet::setChannel(uint8_t channel) { + mDataset.mChannel = channel; + mDataset.mComponents.mIsChannelPresent = true; +} + +void DataSet::setPanId(uint16_t panId) { + mDataset.mPanId = panId; + mDataset.mComponents.mIsPanIdPresent = true; +} + +const char *DataSet::getNetworkName() const { + return mDataset.mNetworkName.m8; +} + +const uint8_t *DataSet::getExtendedPanId() const { + return mDataset.mExtendedPanId.m8; +} + +const uint8_t *DataSet::getNetworkKey() const { + return mDataset.mNetworkKey.m8; +} + +uint8_t DataSet::getChannel() const { + return mDataset.mChannel; +} + +uint16_t DataSet::getPanId() const { + return mDataset.mPanId; +} + +void DataSet::apply(otInstance *instance) { + otDatasetSetActive(instance, &mDataset); +} + +// OpenThread Implementation +bool OpenThread::otStarted = false; + +otInstance *OpenThread::mInstance = nullptr; +OpenThread::OpenThread() {} + +OpenThread::~OpenThread() { + end(); +} + +OpenThread::operator bool() const { + return otStarted; +} + +void OpenThread::begin(bool OThreadAutoStart) { + if (otStarted) { + log_w("OpenThread already started"); + return; + } + + memset(&ot_native_config, 0, sizeof(esp_openthread_platform_config_t)); + ot_native_config.radio_config.radio_mode = RADIO_MODE_NATIVE; + ot_native_config.host_config.host_connection_mode = HOST_CONNECTION_MODE_NONE; + ot_native_config.port_config.storage_partition_name = "nvs"; + ot_native_config.port_config.netif_queue_size = 10; + ot_native_config.port_config.task_queue_size = 10; + + // Initialize OpenThread stack + xTaskCreate(ot_task_worker, "ot_main_loop", 10240, NULL, 20, &s_ot_task); + if (s_ot_task == NULL) { + log_e("Error: Failed to create OpenThread task"); + return; + } + log_d("OpenThread task created successfully"); + // get the OpenThread instance that will be used for all operations + mInstance = esp_openthread_get_instance(); + if (!mInstance) { + log_e("Error: Failed to initialize OpenThread instance"); + end(); + return; + } + // starts Thread with default dataset from NVS or from IDF default settings + if (OThreadAutoStart) { + otOperationalDatasetTlvs dataset; + otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); + // error = OT_ERROR_FAILED; // teste para forçar NULL dataset + if (error != OT_ERROR_NONE) { + log_i("Failed to get active NVS dataset from OpenThread"); + } else { + log_i("Got active NVS dataset from OpenThread"); + } + esp_err_t err = esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL); + if (err != ESP_OK) { + log_i("Failed to AUTO start OpenThread"); + } else { + log_i("AUTO start OpenThread done"); + } + } + otStarted = true; +} + +void OpenThread::end() { + if (s_ot_task != NULL) { + vTaskDelete(s_ot_task); + s_ot_task = NULL; + // Clean up + esp_openthread_deinit(); + esp_openthread_netif_glue_deinit(); +#if CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM + ot_lwip_netif = NULL; +#endif + esp_netif_destroy(openthread_netif); + esp_vfs_eventfd_unregister(); + } + otStarted = false; +} + +void OpenThread::start() { + if (!mInstance) { + log_w("Error: OpenThread instance not initialized"); + return; + } + otThreadSetEnabled(mInstance, true); + log_d("Thread network started"); +} + +void OpenThread::stop() { + if (!mInstance) { + log_w("Error: OpenThread instance not initialized"); + return; + } + otThreadSetEnabled(mInstance, false); + log_d("Thread network stopped"); +} + +void OpenThread::networkInterfaceUp() { + if (!mInstance) { + log_w("Error: OpenThread instance not initialized"); + return; + } + // Enable the Thread interface (equivalent to CLI Command "ifconfig up") + otError error = otIp6SetEnabled(mInstance, true); + if (error != OT_ERROR_NONE) { + log_e("Error: Failed to enable Thread interface (error code: %d)\n", error); + } + log_d("OpenThread Network Interface is up"); +} + +void OpenThread::networkInterfaceDown() { + if (!mInstance) { + log_w("Error: OpenThread instance not initialized"); + return; + } + // Disable the Thread interface (equivalent to CLI Command "ifconfig down") + otError error = otIp6SetEnabled(mInstance, false); + if (error != OT_ERROR_NONE) { + log_e("Error: Failed to disable Thread interface (error code: %d)\n", error); + } + log_d("OpenThread Network Interface is down"); +} + +void OpenThread::commitDataSet(const DataSet &dataset) { + if (!mInstance) { + log_w("Error: OpenThread instance not initialized"); + return; + } + // Commit the dataset as the active dataset + otError error = otDatasetSetActive(mInstance, &(dataset.getDataset())); + if (error != OT_ERROR_NONE) { + log_e("Error: Failed to commit dataset (error code: %d)\n", error); + return; + } + log_d("Dataset committed successfully"); +} + +ot_device_role_t OpenThread::otGetDeviceRole() { + if (!mInstance) { + log_w("Error: OpenThread instance not initialized"); + return OT_ROLE_DISABLED; + } + return (ot_device_role_t)otThreadGetDeviceRole(mInstance); +} + +const char *OpenThread::otGetStringDeviceRole() { + return otRoleString[otGetDeviceRole()]; +} + +void OpenThread::otPrintNetworkInformation(Stream &output) { + if (!mInstance) { + log_w("Error: OpenThread instance not initialized"); + return; + } + + output.printf("Role: %s", otGetStringDeviceRole()); + output.println(); + output.printf("Network Name: %s", otThreadGetNetworkName(mInstance)); + output.println(); + output.printf("Channel: %d", otLinkGetChannel(mInstance)); + output.println(); + output.printf("PAN ID: 0x%04x", otLinkGetPanId(mInstance)); + output.println(); + + const otExtendedPanId *extPanId = otThreadGetExtendedPanId(mInstance); + output.print("Extended PAN ID: "); + for (int i = 0; i < OT_EXT_PAN_ID_SIZE; i++) { + output.printf("%02x", extPanId->m8[i]); + } + output.println(); + + otNetworkKey networkKey; + otThreadGetNetworkKey(mInstance, &networkKey); + output.print("Network Key: "); + for (int i = 0; i < OT_NETWORK_KEY_SIZE; i++) { + output.printf("%02x", networkKey.m8[i]); + } + output.println(); +} + +OpenThread OThread; + +#endif /* CONFIG_OPENTHREAD_ENABLED */ +#endif /* SOC_IEEE802154_SUPPORTED */ diff --git a/libraries/OpenThread/src/OThread.h b/libraries/OpenThread/src/OThread.h new file mode 100644 index 00000000000..359d581bb9d --- /dev/null +++ b/libraries/OpenThread/src/OThread.h @@ -0,0 +1,107 @@ +// 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 "soc/soc_caps.h" +#include "sdkconfig.h" +#if SOC_IEEE802154_SUPPORTED +#if CONFIG_OPENTHREAD_ENABLED + +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + OT_ROLE_DISABLED = 0, ///< The Thread stack is disabled. + OT_ROLE_DETACHED = 1, ///< Not currently participating in a Thread network/partition. + OT_ROLE_CHILD = 2, ///< The Thread Child role. + OT_ROLE_ROUTER = 3, ///< The Thread Router role. + OT_ROLE_LEADER = 4, ///< The Thread Leader role. +} ot_device_role_t; +extern const char *otRoleString[]; + +class DataSet { +public: + DataSet(); + void clear(); + void initNew(); + const otOperationalDataset &getDataset() const; + + // Setters + void setNetworkName(const char *name); + void setExtendedPanId(const uint8_t *extPanId); + void setNetworkKey(const uint8_t *key); + void setChannel(uint8_t channel); + void setPanId(uint16_t panId); + + // Getters + const char *getNetworkName() const; + const uint8_t *getExtendedPanId() const; + const uint8_t *getNetworkKey() const; + uint8_t getChannel() const; + uint16_t getPanId() const; + + // Apply the dataset to the OpenThread instance + void apply(otInstance *instance); + +private: + otOperationalDataset mDataset; +}; + +class OpenThread { +public: + static bool otStarted; + static ot_device_role_t otGetDeviceRole(); + static const char *otGetStringDeviceRole(); + static void otPrintNetworkInformation(Stream &output); + + OpenThread(); + ~OpenThread(); + // returns true if OpenThread Stack is running + operator bool() const; + + // Initialize OpenThread + static void begin(bool OThreadAutoStart = true); + + // Initialize OpenThread + static void end(); + + // Start the Thread network + void start(); + + // Stop the Thread network + void stop(); + + // Start Thread Network Interface + void networkInterfaceUp(); + + // Stop Thread Network Interface + void networkInterfaceDown(); + + // Set the dataset + void commitDataSet(const DataSet &dataset); + +private: + static otInstance *mInstance; + DataSet mCurrentDataSet; +}; + +extern OpenThread OThread; + +#endif /* CONFIG_OPENTHREAD_ENABLED */ +#endif /* SOC_IEEE802154_SUPPORTED */ diff --git a/libraries/OpenThread/src/OThreadCLI.cpp b/libraries/OpenThread/src/OThreadCLI.cpp index 85ba03563e8..2f0f97a0539 100644 --- a/libraries/OpenThread/src/OThreadCLI.cpp +++ b/libraries/OpenThread/src/OThreadCLI.cpp @@ -33,18 +33,12 @@ #include "esp_netif_net_stack.h" #include "lwip/netif.h" +bool OpenThreadCLI::otCLIStarted = false; static TaskHandle_t s_cli_task = NULL; static TaskHandle_t s_console_cli_task = NULL; static QueueHandle_t rx_queue = NULL; static QueueHandle_t tx_queue = NULL; -static esp_openthread_platform_config_t ot_native_config; -static TaskHandle_t s_ot_task = NULL; -static esp_netif_t *openthread_netif = NULL; -#if CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM -static struct netif *ot_lwip_netif = NULL; -#endif - #define OT_CLI_MAX_LINE_LENGTH 512 typedef struct { @@ -53,21 +47,7 @@ typedef struct { String prompt; OnReceiveCb_t responseCallBack; } ot_cli_console_t; -static ot_cli_console_t otConsole = {NULL, false, (const char *)NULL, NULL}; - -#if CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM -extern "C" int lwip_hook_ip6_input(struct pbuf *p, struct netif *inp) { - if (ot_lwip_netif && ot_lwip_netif == inp) { - return 0; - } - if (ip6_addr_isany_val(inp->ip6_addr[0].u_addr.ip6)) { - // We don't have an LL address -> eat this packet here, so it won't get accepted on input netif - pbuf_free(p); - return 1; - } - return 0; -} -#endif +static ot_cli_console_t otConsole = {nullptr, false, (const char *)nullptr, nullptr}; // process the CLI commands sent to the OpenThread stack static void ot_cli_loop(void *context) { @@ -131,7 +111,7 @@ static int ot_cli_output_callback(void *context, const char *format, va_list arg } // if there is a user callback function in place, it shall have the priority // to process/consume the Stream data received from OpenThread CLI, which is available in its RX Buffer - if (otConsole.responseCallBack != NULL) { + if (otConsole.responseCallBack != nullptr) { otConsole.responseCallBack(); } } @@ -205,7 +185,7 @@ void OpenThreadCLI::setEchoBack(bool echoback) { } void OpenThreadCLI::setPrompt(char *prompt) { - otConsole.prompt = prompt; // NULL will make the prompt not visible + otConsole.prompt = prompt; // nullptr can make the prompt not visible } void OpenThreadCLI::setStream(Stream &otStream) { @@ -213,12 +193,12 @@ void OpenThreadCLI::setStream(Stream &otStream) { } void OpenThreadCLI::onReceive(OnReceiveCb_t func) { - otConsole.responseCallBack = func; // NULL will set it off + otConsole.responseCallBack = func; // nullptr will set it off } // Stream object shall be already started and configured before calling this function void OpenThreadCLI::startConsole(Stream &otStream, bool echoback, const char *prompt) { - if (!otStarted) { + if (!otCLIStarted) { log_e("OpenThread CLI has not started. Please begin() it before starting the console."); return; } @@ -226,7 +206,7 @@ void OpenThreadCLI::startConsole(Stream &otStream, bool echoback, const char *pr if (s_console_cli_task == NULL) { otConsole.cliStream = &otStream; otConsole.echoback = echoback; - otConsole.prompt = prompt; // NULL will invalidate the String + otConsole.prompt = prompt; // nullptr will invalidate the String // it will run in the same priority (1) as the Arduino setup()/loop() task xTaskCreate(ot_cli_console_worker, "ot_cli_console", 4096, &otConsole, 1, &s_console_cli_task); } else { @@ -242,12 +222,6 @@ void OpenThreadCLI::stopConsole() { } OpenThreadCLI::OpenThreadCLI() { - memset(&ot_native_config, 0, sizeof(esp_openthread_platform_config_t)); - ot_native_config.radio_config.radio_mode = RADIO_MODE_NATIVE; - ot_native_config.host_config.host_connection_mode = HOST_CONNECTION_MODE_NONE; - ot_native_config.port_config.storage_partition_name = "nvs"; - ot_native_config.port_config.netif_queue_size = 10; - ot_native_config.port_config.task_queue_size = 10; //sTxString = ""; } @@ -256,79 +230,19 @@ OpenThreadCLI::~OpenThreadCLI() { } OpenThreadCLI::operator bool() const { - return otStarted; + return otCLIStarted; } -static void ot_task_worker(void *aContext) { - esp_vfs_eventfd_config_t eventfd_config = { - .max_fds = 3, - }; - bool err = false; - if (ESP_OK != esp_event_loop_create_default()) { - log_e("Failed to create OpentThread event loop"); - err = true; - } - if (!err && ESP_OK != esp_netif_init()) { - log_e("Failed to initialize OpentThread netif"); - err = true; - } - if (!err && ESP_OK != esp_vfs_eventfd_register(&eventfd_config)) { - log_e("Failed to register OpentThread eventfd"); - err = true; - } - - // Initialize the OpenThread stack - if (!err && ESP_OK != esp_openthread_init(&ot_native_config)) { - log_e("Failed to initialize OpenThread stack"); - err = true; - } - if (!err) { - // Initialize the OpenThread cli - otCliInit(esp_openthread_get_instance(), ot_cli_output_callback, NULL); - - // Initialize the esp_netif bindings - esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); - openthread_netif = esp_netif_new(&cfg); -#if CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM - // Get LwIP Netif - if (openthread_netif != NULL) { - ot_lwip_netif = (struct netif *)esp_netif_get_netif_impl(openthread_netif); - if (ot_lwip_netif == NULL) { - log_e("Failed to get OpenThread LwIP netif"); - } - } -#endif - } - if (!err && openthread_netif == NULL) { - log_e("Failed to create OpenThread esp_netif"); - err = true; - } - if (!err && ESP_OK != esp_netif_attach(openthread_netif, esp_openthread_netif_glue_init(&ot_native_config))) { - log_e("Failed to attach OpenThread esp_netif"); - err = true; - } - if (!err && ESP_OK != esp_netif_set_default_netif(openthread_netif)) { - log_e("Failed to set default OpenThread esp_netif"); - err = true; - } - if (!err) { - // only returns in case there is an OpenThread Stack failure... - esp_openthread_launch_mainloop(); - } - // Clean up - esp_openthread_netif_glue_deinit(); - esp_netif_destroy(openthread_netif); - esp_vfs_eventfd_unregister(); - vTaskDelete(NULL); -} - -void OpenThreadCLI::begin(bool OThreadAutoStart) { - if (otStarted) { +void OpenThreadCLI::begin() { + if (otCLIStarted) { log_w("OpenThread CLI already started. Please end() it before starting again."); return; } - xTaskCreate(ot_task_worker, "ot_main_loop", 10240, NULL, 20, &s_ot_task); + if (!OpenThread::otStarted) { + log_w("OpenThread not started. Please begin() it before starting CLI."); + return; + } //RX Buffer default has 1024 bytes if not preset if (rx_queue == NULL) { @@ -342,55 +256,29 @@ void OpenThreadCLI::begin(bool OThreadAutoStart) { log_e("HW CDC RX Buffer error"); } } + xTaskCreate(ot_cli_loop, "ot_cli", 4096, xTaskGetCurrentTaskHandle(), 2, &s_cli_task); + // Initialize the OpenThread cli + otCliInit(esp_openthread_get_instance(), ot_cli_output_callback, NULL); - // starts Thread with default dataset from NVS or from IDF default settings - if (OThreadAutoStart) { - otOperationalDatasetTlvs dataset; - otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); - // error = OT_ERROR_FAILED; // teste para forçar NULL dataset - if (error != OT_ERROR_NONE) { - log_i("Failed to get active NVS dataset from OpenThread"); - } else { - log_i("Got active NVS dataset from OpenThread"); - } - esp_err_t err = esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL); - if (err != ESP_OK) { - log_i("Failed to AUTO start OpenThread"); - } else { - log_i("AUTO start OpenThread done"); - } - } - otStarted = true; + otCLIStarted = true; return; } void OpenThreadCLI::end() { - if (!otStarted) { + if (!otCLIStarted) { log_w("OpenThread CLI already stopped. Please begin() it before stopping again."); return; } - if (s_ot_task != NULL) { - vTaskDelete(s_ot_task); - // Clean up - esp_openthread_deinit(); - esp_openthread_netif_glue_deinit(); -#if CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM - ot_lwip_netif = NULL; -#endif - esp_netif_destroy(openthread_netif); - esp_vfs_eventfd_unregister(); - } if (s_cli_task != NULL) { vTaskDelete(s_cli_task); + s_cli_task = NULL; } - if (s_console_cli_task != NULL) { - vTaskDelete(s_console_cli_task); - } + stopConsole(); esp_event_loop_delete_default(); setRxBufferSize(0); setTxBufferSize(0); - otStarted = false; + otCLIStarted = false; } size_t OpenThreadCLI::write(uint8_t c) { diff --git a/libraries/OpenThread/src/OThreadCLI.h b/libraries/OpenThread/src/OThreadCLI.h index bc8dc5d2b19..788edc2709b 100644 --- a/libraries/OpenThread/src/OThreadCLI.h +++ b/libraries/OpenThread/src/OThreadCLI.h @@ -21,7 +21,6 @@ #include "esp_openthread.h" #include "esp_openthread_cli.h" #include "esp_openthread_lock.h" -#include "esp_openthread_netif_glue.h" #include "esp_openthread_types.h" #include "openthread/cli.h" @@ -31,13 +30,14 @@ #include "openthread/dataset_ftd.h" #include "Arduino.h" +#include "OThread.h" typedef std::function OnReceiveCb_t; class OpenThreadCLI : public Stream { private: static size_t setBuffer(QueueHandle_t &queue, size_t len); - bool otStarted = false; + static bool otCLIStarted; public: OpenThreadCLI(); @@ -53,7 +53,7 @@ class OpenThreadCLI : public Stream { void setStream(Stream &otStream); // changes the console Stream object void onReceive(OnReceiveCb_t func); // called on a complete line of output from OT CLI, as OT Response - void begin(bool OThreadAutoStart = true); + void begin(); void end(); // default size is 256 bytes diff --git a/libraries/OpenThread/src/OThreadCLI_Util.cpp b/libraries/OpenThread/src/OThreadCLI_Util.cpp index d21daa1effc..aad435107bb 100644 --- a/libraries/OpenThread/src/OThreadCLI_Util.cpp +++ b/libraries/OpenThread/src/OThreadCLI_Util.cpp @@ -19,26 +19,6 @@ #include "OThreadCLI_Util.h" #include -static const char *otRoleString[] = { - "Disabled", ///< The Thread stack is disabled. - "Detached", ///< Not currently participating in a Thread network/partition. - "Child", ///< The Thread Child role. - "Router", ///< The Thread Router role. - "Leader", ///< The Thread Leader role. -}; - -ot_device_role_t otGetDeviceRole() { - if (!OThreadCLI) { - return OT_ROLE_DISABLED; - } - otInstance *instance = esp_openthread_get_instance(); - return (ot_device_role_t)otThreadGetDeviceRole(instance); -} - -const char *otGetStringDeviceRole() { - return otRoleString[otGetDeviceRole()]; -} - bool otGetRespCmd(const char *cmd, char *resp, uint32_t respTimeout) { if (!OThreadCLI) { return false; @@ -174,7 +154,7 @@ bool otPrintRespCLI(const char *cmd, Stream &output, uint32_t respTimeout) { return true; } -void otPrintNetworkInformation(Stream &output) { +void otCLIPrintNetworkInformation(Stream &output) { if (!OThreadCLI) { return; } diff --git a/libraries/OpenThread/src/OThreadCLI_Util.h b/libraries/OpenThread/src/OThreadCLI_Util.h index 1ab2e061dfc..116b886e095 100644 --- a/libraries/OpenThread/src/OThreadCLI_Util.h +++ b/libraries/OpenThread/src/OThreadCLI_Util.h @@ -18,25 +18,14 @@ #if SOC_IEEE802154_SUPPORTED #if CONFIG_OPENTHREAD_ENABLED -typedef enum { - OT_ROLE_DISABLED = 0, ///< The Thread stack is disabled. - OT_ROLE_DETACHED = 1, ///< Not currently participating in a Thread network/partition. - OT_ROLE_CHILD = 2, ///< The Thread Child role. - OT_ROLE_ROUTER = 3, ///< The Thread Router role. - OT_ROLE_LEADER = 4, ///< The Thread Leader role. -} ot_device_role_t; - typedef struct { int errorCode; String errorMessage; } ot_cmd_return_t; -ot_device_role_t otGetDeviceRole(); -const char *otGetStringDeviceRole(); bool otGetRespCmd(const char *cmd, char *resp = NULL, uint32_t respTimeout = 5000); bool otExecCommand(const char *cmd, const char *arg, ot_cmd_return_t *returnCode = NULL); bool otPrintRespCLI(const char *cmd, Stream &output, uint32_t respTimeout); -void otPrintNetworkInformation(Stream &output); #endif /* CONFIG_OPENTHREAD_ENABLED */ #endif /* SOC_IEEE802154_SUPPORTED */ From 7462b09bb4818fd77a40bcaace5663e255bae8f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Mon, 16 Jun 2025 13:44:11 +0200 Subject: [PATCH 22/42] feat(LEDC): Add Gamma Fade support and enhance auto channel/timer selection for multi-group (#11464) * feat(ledc): Enhance LEDC auto channel/timer selection for multi-group support * feat(ledc): Add Gamma Fade support * fix(example): Update comments * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- cores/esp32/esp32-hal-ledc.c | 264 ++++++++++++++++-- cores/esp32/esp32-hal-ledc.h | 79 ++++++ .../AnalogOut/LEDCGammaFade/LEDCGammaFade.ino | 111 ++++++++ .../examples/AnalogOut/LEDCGammaFade/ci.json | 5 + 4 files changed, 428 insertions(+), 31 deletions(-) create mode 100644 libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino create mode 100644 libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.json diff --git a/cores/esp32/esp32-hal-ledc.c b/cores/esp32/esp32-hal-ledc.c index 764c2803b4b..18b722f80a2 100644 --- a/cores/esp32/esp32-hal-ledc.c +++ b/cores/esp32/esp32-hal-ledc.c @@ -22,6 +22,9 @@ #include "soc/gpio_sig_map.h" #include "esp_rom_gpio.h" #include "hal/ledc_ll.h" +#if SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED +#include +#endif #ifdef SOC_LEDC_SUPPORT_HS_MODE #define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM << 1) @@ -56,7 +59,7 @@ static bool find_matching_timer(uint8_t speed_mode, uint32_t freq, uint8_t resol peripheral_bus_type_t type = perimanGetPinBusType(i); if (type == ESP32_BUS_TYPE_LEDC) { ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC); - if (bus != NULL && (bus->channel / 8) == speed_mode && bus->freq_hz == freq && bus->channel_resolution == resolution) { + if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode && bus->freq_hz == freq && bus->channel_resolution == resolution) { log_d("Found matching timer %u for freq=%u, resolution=%u", bus->timer_num, freq, resolution); *timer_num = bus->timer_num; return true; @@ -78,7 +81,7 @@ static bool find_free_timer(uint8_t speed_mode, uint8_t *timer_num) { peripheral_bus_type_t type = perimanGetPinBusType(i); if (type == ESP32_BUS_TYPE_LEDC) { ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC); - if (bus != NULL && (bus->channel / 8) == speed_mode) { + if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode) { log_d("Timer %u is in use by channel %u", bus->timer_num, bus->channel); used_timers |= (1 << bus->timer_num); } @@ -110,7 +113,7 @@ static void remove_channel_from_timer(uint8_t speed_mode, uint8_t timer_num, uin peripheral_bus_type_t type = perimanGetPinBusType(i); if (type == ESP32_BUS_TYPE_LEDC) { ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC); - if (bus != NULL && (bus->channel / 8) == speed_mode && bus->timer_num == timer_num && bus->channel != channel) { + if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode && bus->timer_num == timer_num && bus->channel != channel) { log_d("Timer %u is still in use by channel %u", timer_num, bus->channel); timer_in_use = true; break; @@ -168,8 +171,8 @@ static bool ledcDetachBus(void *bus) { } pinMatrixOutDetach(handle->pin, false, false); if (!channel_found) { - uint8_t group = (handle->channel / 8); - remove_channel_from_timer(group, handle->timer_num, handle->channel % 8); + uint8_t group = (handle->channel / SOC_LEDC_CHANNEL_NUM); + remove_channel_from_timer(group, handle->timer_num, handle->channel % SOC_LEDC_CHANNEL_NUM); ledc_handle.used_channels &= ~(1UL << handle->channel); } free(handle); @@ -206,13 +209,13 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c return false; } - uint8_t group = (channel / 8); + uint8_t group = (channel / SOC_LEDC_CHANNEL_NUM); uint8_t timer = 0; bool channel_used = ledc_handle.used_channels & (1UL << channel); if (channel_used) { log_i("Channel %u is already set up, given frequency and resolution will be ignored", channel); - if (ledc_set_pin(pin, group, channel % 8) != ESP_OK) { + if (ledc_set_pin(pin, group, channel % SOC_LEDC_CHANNEL_NUM) != ESP_OK) { log_e("Attaching pin to already used channel failed!"); return false; } @@ -220,7 +223,7 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c // Find a timer with matching frequency and resolution, or a free timer if (!find_matching_timer(group, freq, resolution, &timer)) { if (!find_free_timer(group, &timer)) { - log_e("No free timers available for speed mode %u", group); + log_w("No free timers available for speed mode %u", group); return false; } @@ -239,12 +242,12 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c } } - uint32_t duty = ledc_get_duty(group, (channel % 8)); + uint32_t duty = ledc_get_duty(group, (channel % SOC_LEDC_CHANNEL_NUM)); ledc_channel_config_t ledc_channel; memset((void *)&ledc_channel, 0, sizeof(ledc_channel_config_t)); ledc_channel.speed_mode = group; - ledc_channel.channel = (channel % 8); + ledc_channel.channel = (channel % SOC_LEDC_CHANNEL_NUM); ledc_channel.timer_sel = timer; ledc_channel.intr_type = LEDC_INTR_DISABLE; ledc_channel.gpio_num = pin; @@ -274,7 +277,7 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c ledc_handle.used_channels |= 1UL << channel; } - if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_LEDC, (void *)handle, group, channel)) { + if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_LEDC, (void *)handle, channel, timer)) { ledcDetachBus((void *)handle); return false; } @@ -291,14 +294,40 @@ bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution) { } uint8_t channel = __builtin_ctz(free_channel); // Convert the free_channel bit to channel number - return ledcAttachChannel(pin, freq, resolution, channel); + // Try the first available channel + if (ledcAttachChannel(pin, freq, resolution, channel)) { + return true; + } + +#ifdef SOC_LEDC_SUPPORT_HS_MODE + // If first attempt failed and HS mode is supported, try to find a free channel in group 1 + if ((channel / SOC_LEDC_CHANNEL_NUM) == 0) { // First attempt was in group 0 + log_d("LEDC: Group 0 channel %u failed, trying to find a free channel in group 1", channel); + // Find free channels specifically in group 1 + uint32_t group1_mask = ((1UL << SOC_LEDC_CHANNEL_NUM) - 1) << SOC_LEDC_CHANNEL_NUM; + int group1_free_channel = (~ledc_handle.used_channels) & group1_mask; + if (group1_free_channel != 0) { + uint8_t group1_channel = __builtin_ctz(group1_free_channel); + if (ledcAttachChannel(pin, freq, resolution, group1_channel)) { + return true; + } + } + } +#endif + + log_e( + "No free timers available for freq=%u, resolution=%u. To attach a new channel, use the same frequency and resolution as an already attached channel to " + "share its timer.", + freq, resolution + ); + return false; } bool ledcWrite(uint8_t pin, uint32_t duty) { ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC); if (bus != NULL) { - uint8_t group = (bus->channel / 8), channel = (bus->channel % 8); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM); //Fixing if all bits in resolution is set = LEDC FULL ON uint32_t max_duty = (1 << bus->channel_resolution) - 1; @@ -307,8 +336,14 @@ bool ledcWrite(uint8_t pin, uint32_t duty) { duty = max_duty + 1; } - ledc_set_duty(group, channel, duty); - ledc_update_duty(group, channel); + if (ledc_set_duty(group, channel, duty) != ESP_OK) { + log_e("ledc_set_duty failed"); + return false; + } + if (ledc_update_duty(group, channel) != ESP_OK) { + log_e("ledc_update_duty failed"); + return false; + } return true; } @@ -321,7 +356,11 @@ bool ledcWriteChannel(uint8_t channel, uint32_t duty) { log_e("Channel %u is not available (maximum %u) or not used!", channel, LEDC_CHANNELS); return false; } - uint8_t group = (channel / 8), timer = ((channel / 2) % 4); + uint8_t group = (channel / SOC_LEDC_CHANNEL_NUM); + ledc_timer_t timer; + + // Get the actual timer being used by this channel + ledc_ll_get_channel_timer(LEDC_LL_GET_HW(), group, (channel % SOC_LEDC_CHANNEL_NUM), &timer); //Fixing if all bits in resolution is set = LEDC FULL ON uint32_t resolution = 0; @@ -333,8 +372,14 @@ bool ledcWriteChannel(uint8_t channel, uint32_t duty) { duty = max_duty + 1; } - ledc_set_duty(group, channel, duty); - ledc_update_duty(group, channel); + if (ledc_set_duty(group, channel, duty) != ESP_OK) { + log_e("ledc_set_duty failed"); + return false; + } + if (ledc_update_duty(group, channel) != ESP_OK) { + log_e("ledc_update_duty failed"); + return false; + } return true; } @@ -343,7 +388,7 @@ uint32_t ledcRead(uint8_t pin) { ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC); if (bus != NULL) { - uint8_t group = (bus->channel / 8), channel = (bus->channel % 8); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM); return ledc_get_duty(group, channel); } return 0; @@ -355,8 +400,8 @@ uint32_t ledcReadFreq(uint8_t pin) { if (!ledcRead(pin)) { return 0; } - uint8_t group = (bus->channel / 8), timer = ((bus->channel / 2) % 4); - return ledc_get_freq(group, timer); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM); + return ledc_get_freq(group, bus->timer_num); } return 0; } @@ -370,12 +415,12 @@ uint32_t ledcWriteTone(uint8_t pin, uint32_t freq) { return 0; } - uint8_t group = (bus->channel / 8), timer = ((bus->channel / 2) % 4); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM); ledc_timer_config_t ledc_timer; memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t)); ledc_timer.speed_mode = group; - ledc_timer.timer_num = timer; + ledc_timer.timer_num = bus->timer_num; ledc_timer.duty_resolution = 10; ledc_timer.freq_hz = freq; ledc_timer.clk_cfg = clock_source; @@ -386,7 +431,7 @@ uint32_t ledcWriteTone(uint8_t pin, uint32_t freq) { } bus->channel_resolution = 10; - uint32_t res_freq = ledc_get_freq(group, timer); + uint32_t res_freq = ledc_get_freq(group, bus->timer_num); ledcWrite(pin, 0x1FF); return res_freq; } @@ -427,12 +472,12 @@ uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution) { log_e("LEDC pin %u - resolution is zero or it is too big (maximum %u)", pin, LEDC_MAX_BIT_WIDTH); return 0; } - uint8_t group = (bus->channel / 8), timer = ((bus->channel / 2) % 4); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM); ledc_timer_config_t ledc_timer; memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t)); ledc_timer.speed_mode = group; - ledc_timer.timer_num = timer; + ledc_timer.timer_num = bus->timer_num; ledc_timer.duty_resolution = resolution; ledc_timer.freq_hz = freq; ledc_timer.clk_cfg = clock_source; @@ -442,7 +487,7 @@ uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution) { return 0; } bus->channel_resolution = resolution; - return ledc_get_freq(group, timer); + return ledc_get_freq(group, bus->timer_num); } return 0; } @@ -453,12 +498,14 @@ bool ledcOutputInvert(uint8_t pin, bool out_invert) { gpio_set_level(pin, out_invert); #ifdef CONFIG_IDF_TARGET_ESP32P4 - esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT_PAD_OUT0_IDX + ((bus->channel) % 8), out_invert, 0); + esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT_PAD_OUT0_IDX + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0); #else #ifdef SOC_LEDC_SUPPORT_HS_MODE - esp_rom_gpio_connect_out_signal(pin, ((bus->channel / 8 == 0) ? LEDC_HS_SIG_OUT0_IDX : LEDC_LS_SIG_OUT0_IDX) + ((bus->channel) % 8), out_invert, 0); + esp_rom_gpio_connect_out_signal( + pin, ((bus->channel / SOC_LEDC_CHANNEL_NUM == 0) ? LEDC_HS_SIG_OUT0_IDX : LEDC_LS_SIG_OUT0_IDX) + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0 + ); #else - esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + ((bus->channel) % 8), out_invert, 0); + esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0); #endif #endif // ifdef CONFIG_IDF_TARGET_ESP32P4 return true; @@ -505,7 +552,7 @@ static bool ledcFadeConfig(uint8_t pin, uint32_t start_duty, uint32_t target_dut } #endif #endif - uint8_t group = (bus->channel / 8), channel = (bus->channel % 8); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM); // Initialize fade service. if (!fade_initialized) { @@ -562,6 +609,161 @@ bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_ return ledcFadeConfig(pin, start_duty, target_duty, max_fade_time_ms, userFunc, arg); } +#ifdef SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED +// Default gamma factor for gamma correction (common value for LEDs) +static float ledcGammaFactor = 2.8; +// Gamma correction LUT support +static const float *ledcGammaLUT = NULL; +static uint16_t ledcGammaLUTSize = 0; +// Global variable to store current resolution for gamma callback +static uint8_t ledcGammaResolution = 13; + +bool ledcSetGammaTable(const float *gamma_table, uint16_t size) { + if (gamma_table == NULL || size == 0) { + log_e("Invalid gamma table or size"); + return false; + } + ledcGammaLUT = gamma_table; + ledcGammaLUTSize = size; + log_i("Custom gamma LUT set with %u entries", size); + return true; +} + +void ledcClearGammaTable(void) { + ledcGammaLUT = NULL; + ledcGammaLUTSize = 0; + log_i("Gamma LUT cleared, using mathematical calculation"); +} + +void ledcSetGammaFactor(float factor) { + ledcGammaFactor = factor; +} + +// Gamma correction calculator function +static uint32_t ledcGammaCorrection(uint32_t duty) { + if (duty == 0) { + return 0; + } + + uint32_t max_duty = (1U << ledcGammaResolution) - 1; + if (duty >= (1U << ledcGammaResolution)) { + return max_duty; + } + + // Use LUT if provided, otherwise use mathematical calculation + if (ledcGammaLUT != NULL && ledcGammaLUTSize > 0) { + // LUT-based gamma correction + uint32_t lut_index = (duty * (ledcGammaLUTSize - 1)) / max_duty; + if (lut_index >= ledcGammaLUTSize) { + lut_index = ledcGammaLUTSize - 1; + } + + float corrected_normalized = ledcGammaLUT[lut_index]; + return (uint32_t)(corrected_normalized * max_duty); + } else { + // Mathematical gamma correction + double normalized = (double)duty / (1U << ledcGammaResolution); + double corrected = pow(normalized, ledcGammaFactor); + return (uint32_t)(corrected * (1U << ledcGammaResolution)); + } +} + +static bool ledcFadeGammaConfig(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) { + ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC); + if (bus != NULL) { + +#ifndef SOC_LEDC_SUPPORT_FADE_STOP +#if !CONFIG_DISABLE_HAL_LOCKS + if (bus->lock == NULL) { + bus->lock = xSemaphoreCreateBinary(); + if (bus->lock == NULL) { + log_e("xSemaphoreCreateBinary failed"); + return false; + } + xSemaphoreGive(bus->lock); + } + //acquire lock + if (xSemaphoreTake(bus->lock, 0) != pdTRUE) { + log_e("LEDC Fade is still running on pin %u! SoC does not support stopping fade.", pin); + return false; + } +#endif +#endif + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM); + + // Initialize fade service. + if (!fade_initialized) { + ledc_fade_func_install(0); + fade_initialized = true; + } + + bus->fn = (voidFuncPtr)userFunc; + bus->arg = arg; + + ledc_cbs_t callbacks = {.fade_cb = ledcFnWrapper}; + ledc_cb_register(group, channel, &callbacks, (void *)bus); + + // Prepare gamma curve fade parameters + ledc_fade_param_config_t fade_params[SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX]; + uint32_t actual_fade_ranges = 0; + + // Use a moderate number of linear segments for smooth gamma curve + const uint32_t linear_fade_segments = 12; + + // Set the global resolution for gamma correction + ledcGammaResolution = bus->channel_resolution; + + // Fill multi-fade parameter list using ESP-IDF API + esp_err_t err = ledc_fill_multi_fade_param_list( + group, channel, start_duty, target_duty, linear_fade_segments, max_fade_time_ms, ledcGammaCorrection, SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX, fade_params, + &actual_fade_ranges + ); + + if (err != ESP_OK) { + log_e("ledc_fill_multi_fade_param_list failed: %s", esp_err_to_name(err)); + return false; + } + + // Apply the gamma-corrected start duty + uint32_t gamma_start_duty = ledcGammaCorrection(start_duty); + + // Set multi-fade parameters + err = ledc_set_multi_fade(group, channel, gamma_start_duty, fade_params, actual_fade_ranges); + if (err != ESP_OK) { + log_e("ledc_set_multi_fade failed: %s", esp_err_to_name(err)); + return false; + } + + // Start the gamma curve fade + err = ledc_fade_start(group, channel, LEDC_FADE_NO_WAIT); + if (err != ESP_OK) { + log_e("ledc_fade_start failed: %s", esp_err_to_name(err)); + return false; + } + + log_d("Gamma curve fade started on pin %u: %u -> %u over %dms", pin, start_duty, target_duty, max_fade_time_ms); + + } else { + log_e("Pin %u is not attached to LEDC. Call ledcAttach first!", pin); + return false; + } + return true; +} + +bool ledcFadeGamma(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms) { + return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, NULL, NULL); +} + +bool ledcFadeGammaWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, voidFuncPtr userFunc) { + return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, (voidFuncPtrArg)userFunc, NULL); +} + +bool ledcFadeGammaWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) { + return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, userFunc, arg); +} + +#endif /* SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED */ + static uint8_t analog_resolution = 8; static int analog_frequency = 1000; void analogWrite(uint8_t pin, int value) { diff --git a/cores/esp32/esp32-hal-ledc.h b/cores/esp32/esp32-hal-ledc.h index f1a27dd4f7a..1663b884a3f 100644 --- a/cores/esp32/esp32-hal-ledc.h +++ b/cores/esp32/esp32-hal-ledc.h @@ -232,6 +232,85 @@ bool ledcFadeWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_dut */ bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg); +//Gamma Curve Fade functions - only available on supported chips +#ifdef SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED + +/** + * @brief Set a custom gamma correction lookup table for gamma curve fading. + * The LUT should contain normalized values (0.0 to 1.0) representing + * the gamma-corrected brightness curve. + * + * @param gamma_table Pointer to array of float values (0.0 to 1.0) + * @param size Number of entries in the lookup table + * + * @return true if gamma table was successfully set, false otherwise. + * + * @note The LUT array must remain valid for as long as gamma fading is used. + * Larger tables provide smoother transitions but use more memory. + */ +bool ledcSetGammaTable(const float *gamma_table, uint16_t size); + +/** + * @brief Clear the current gamma correction lookup table. + * After calling this, gamma correction will use mathematical + * calculation with the default gamma factor (2.8). + */ +void ledcClearGammaTable(void); + +/** + * @brief Set the gamma factor for gamma correction. + * + * @param factor Gamma factor to use for gamma correction. + */ +void ledcSetGammaFactor(float factor); + +/** + * @brief Setup and start a gamma curve fade on a given LEDC pin. + * Gamma correction makes LED brightness changes appear more gradual to human eyes. + * + * @param pin GPIO pin + * @param start_duty initial duty cycle of the fade + * @param target_duty target duty cycle of the fade + * @param max_fade_time_ms maximum fade time in milliseconds + * + * @return true if gamma fade was successfully set and started, false otherwise. + * + * @note This function is only available on ESP32 variants that support gamma curve fading. + */ +bool ledcFadeGamma(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms); + +/** + * @brief Setup and start a gamma curve fade on a given LEDC pin with a callback function. + * + * @param pin GPIO pin + * @param start_duty initial duty cycle of the fade + * @param target_duty target duty cycle of the fade + * @param max_fade_time_ms maximum fade time in milliseconds + * @param userFunc callback function to be called after fade is finished + * + * @return true if gamma fade was successfully set and started, false otherwise. + * + * @note This function is only available on ESP32 variants that support gamma curve fading. + */ +bool ledcFadeGammaWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void)); + +/** + * @brief Setup and start a gamma curve fade on a given LEDC pin with a callback function and argument. + * + * @param pin GPIO pin + * @param start_duty initial duty cycle of the fade + * @param target_duty target duty cycle of the fade + * @param max_fade_time_ms maximum fade time in milliseconds + * @param userFunc callback function to be called after fade is finished + * @param arg argument to be passed to the callback function + * + * @return true if gamma fade was successfully set and started, false otherwise. + * + * @note This function is only available on ESP32 variants that support gamma curve fading. + */ +bool ledcFadeGammaWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg); +#endif // SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED + #ifdef __cplusplus } #endif diff --git a/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino new file mode 100644 index 00000000000..4ca6c136ddf --- /dev/null +++ b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino @@ -0,0 +1,111 @@ +/* LEDC Gamma Curve Fade Arduino Example + + This example demonstrates gamma curve fading on ESP32 variants that support it. + Gamma correction makes LED brightness changes appear more gradual and natural + to human eyes compared to linear fading. + + Two methods are supported: + 1. Using a pre-computed Gamma Look-Up Table (LUT) for better performance + 2. Using mathematical gamma correction with a gamma factor + + Supported chips: ESP32-C6, ESP32-C5, ESP32-H2, ESP32-P4 and future chips with Gamma Fade support + + Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/) +*/ + +// use 12 bit precision for LEDC timer +#define LEDC_TIMER_12_BIT 12 + +// use 5000 Hz as a LEDC base frequency +#define LEDC_BASE_FREQ 5000 + +// define starting duty, target duty and maximum fade time +#define LEDC_START_DUTY (0) +#define LEDC_TARGET_DUTY (4095) +#define LEDC_FADE_TIME (2000) + +// gamma factor for mathematical calculation +#define LEDC_GAMMA_FACTOR (2.6) + +// use gamma LUT for better performance instead of mathematical calculation (gamma factor) +#define USE_GAMMA_LUT 1 + +// fade LED pins +const uint8_t ledPinR = 4; +const uint8_t ledPinG = 5; +const uint8_t ledPinB = 6; + +uint8_t fade_ended = 0; // status of LED gamma fade +bool fade_in = true; + +#ifdef USE_GAMMA_LUT +// Custom Gamma LUT demonstration with 101 steps (Brightness 0 - 100% gamma correction look up table (gamma = 2.6)) +// Y = B ^ 2.6 - Pre-computed LUT to save runtime computation +static const float ledcGammaLUT[101] = { + 0.000000, 0.000006, 0.000038, 0.000110, 0.000232, 0.000414, 0.000666, 0.000994, 0.001406, 0.001910, 0.002512, 0.003218, 0.004035, 0.004969, 0.006025, + 0.007208, 0.008525, 0.009981, 0.011580, 0.013328, 0.015229, 0.017289, 0.019512, 0.021902, 0.024465, 0.027205, 0.030125, 0.033231, 0.036527, 0.040016, + 0.043703, 0.047593, 0.051688, 0.055993, 0.060513, 0.065249, 0.070208, 0.075392, 0.080805, 0.086451, 0.092333, 0.098455, 0.104821, 0.111434, 0.118298, + 0.125416, 0.132792, 0.140428, 0.148329, 0.156498, 0.164938, 0.173653, 0.182645, 0.191919, 0.201476, 0.211321, 0.221457, 0.231886, 0.242612, 0.253639, + 0.264968, 0.276603, 0.288548, 0.300805, 0.313378, 0.326268, 0.339480, 0.353016, 0.366879, 0.381073, 0.395599, 0.410461, 0.425662, 0.441204, 0.457091, + 0.473325, 0.489909, 0.506846, 0.524138, 0.541789, 0.559801, 0.578177, 0.596920, 0.616032, 0.635515, 0.655374, 0.675610, 0.696226, 0.717224, 0.738608, + 0.760380, 0.782542, 0.805097, 0.828048, 0.851398, 0.875148, 0.899301, 0.923861, 0.948829, 0.974208, 1.000000, +}; +#endif + +void ARDUINO_ISR_ATTR LED_FADE_ISR() { + fade_ended += 1; +} + +void setup() { + // Initialize serial communication at 115200 bits per second: + Serial.begin(115200); + + // Setup timer with given frequency, resolution and attach it to a led pin with auto-selected channel + ledcAttach(ledPinR, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT); + ledcAttach(ledPinG, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT); + ledcAttach(ledPinB, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT); + +#if USE_GAMMA_LUT // Use default gamma LUT for better performance + ledcSetGammaTable(ledcGammaLUT, 101); +#else // Use mathematical gamma correction (default, more flexible) + ledcSetGammaFactor(LEDC_GAMMA_FACTOR); // This is optional to set custom gamma factor (default is 2.8) +#endif + + // Setup and start gamma curve fade on led (duty from 0 to 4095) + ledcFadeGamma(ledPinR, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME); + ledcFadeGamma(ledPinG, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME); + ledcFadeGamma(ledPinB, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME); + Serial.println("LED Gamma Fade on started."); + + // Wait for fade to end + delay(LEDC_FADE_TIME); + + // Setup and start gamma curve fade off led and use ISR (duty from 4095 to 0) + ledcFadeGammaWithInterrupt(ledPinR, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinG, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinB, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + Serial.println("LED Gamma Fade off started."); +} + +void loop() { + // Check if fade_ended flag was set to true in ISR + if (fade_ended == 3) { + Serial.println("LED gamma fade ended"); + fade_ended = 0; + + // Check what gamma fade should be started next + if (fade_in) { + ledcFadeGammaWithInterrupt(ledPinR, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinG, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinB, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + Serial.println("LED Gamma Fade in started."); + fade_in = false; + } else { + ledcFadeGammaWithInterrupt(ledPinR, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinG, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinB, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + Serial.println("LED Gamma Fade out started."); + fade_in = true; + } + } +} diff --git a/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.json b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.json new file mode 100644 index 00000000000..a9d8603b7bf --- /dev/null +++ b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.json @@ -0,0 +1,5 @@ +{ + "requires": [ + "CONFIG_SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED=y" + ] +} From 4bc5ffc88d9246946c832d08408130d888e45b41 Mon Sep 17 00:00:00 2001 From: is-qian <2716275834@qq.com> Date: Tue, 17 Jun 2025 18:39:33 +0800 Subject: [PATCH 23/42] fix: Delete 8M flash option for xiao_esp32_s3_plus. (#11476) --- boards.txt | 6 ++---- variants/XIAO_ESP32S3_Plus/pins_arduino.h | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/boards.txt b/boards.txt index 15a75eaad8b..b51a8840757 100644 --- a/boards.txt +++ b/boards.txt @@ -36051,12 +36051,12 @@ XIAO_ESP32S3_Plus.build.cdc_on_boot=1 XIAO_ESP32S3_Plus.build.msc_on_boot=0 XIAO_ESP32S3_Plus.build.dfu_on_boot=0 XIAO_ESP32S3_Plus.build.f_cpu=240000000L -XIAO_ESP32S3_Plus.build.flash_size=8MB +XIAO_ESP32S3_Plus.build.flash_size=16MB XIAO_ESP32S3_Plus.build.flash_freq=80m XIAO_ESP32S3_Plus.build.flash_mode=dio XIAO_ESP32S3_Plus.build.boot=qio XIAO_ESP32S3_Plus.build.boot_freq=80m -XIAO_ESP32S3_Plus.build.partitions=default_8MB +XIAO_ESP32S3_Plus.build.partitions=ffat XIAO_ESP32S3_Plus.build.defines= XIAO_ESP32S3_Plus.build.loop_core= XIAO_ESP32S3_Plus.build.event_core= @@ -36093,8 +36093,6 @@ XIAO_ESP32S3_Plus.menu.FlashMode.dio.build.boot=dio XIAO_ESP32S3_Plus.menu.FlashMode.dio.build.boot_freq=80m XIAO_ESP32S3_Plus.menu.FlashMode.dio.build.flash_freq=80m -XIAO_ESP32S3_Plus.menu.FlashSize.8M=8MB (64Mb) -XIAO_ESP32S3_Plus.menu.FlashSize.8M.build.flash_size=8MB XIAO_ESP32S3_Plus.menu.FlashSize.16M=16MB (128Mb) XIAO_ESP32S3_Plus.menu.FlashSize.16M.build.flash_size=16MB diff --git a/variants/XIAO_ESP32S3_Plus/pins_arduino.h b/variants/XIAO_ESP32S3_Plus/pins_arduino.h index fb887287e35..de1c6093d44 100644 --- a/variants/XIAO_ESP32S3_Plus/pins_arduino.h +++ b/variants/XIAO_ESP32S3_Plus/pins_arduino.h @@ -4,7 +4,7 @@ #include #define USB_VID 0x2886 -#define USB_PID 0x0056 +#define USB_PID 0x0063 static const uint8_t LED_BUILTIN = 21; #define BUILTIN_LED LED_BUILTIN // backward compatibility From 6d4886cd1f821dbbd3827ce012dcef4e6bc14d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 17 Jun 2025 15:23:35 +0200 Subject: [PATCH 24/42] feat(spi): Add return values to SPI begin (#11477) --- cores/esp32/esp32-hal-spi.h | 4 ++-- libraries/SPI/src/SPI.cpp | 9 +++++---- libraries/SPI/src/SPI.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cores/esp32/esp32-hal-spi.h b/cores/esp32/esp32-hal-spi.h index 7d56f0820d3..565ca43f60d 100644 --- a/cores/esp32/esp32-hal-spi.h +++ b/cores/esp32/esp32-hal-spi.h @@ -38,8 +38,8 @@ extern "C" { #define HSPI 2 //SPI 2 bus normally mapped to pins 12 - 15, but can be matrixed to any pins #define VSPI 3 //SPI 3 bus normally attached to pins 5, 18, 19 and 23, but can be matrixed to any pins #else -#define FSPI 0 -#define HSPI 1 +#define FSPI 0 // ESP32C2, C3, C6, H2, S3, P4 - SPI 2 bus +#define HSPI 1 // ESP32S3, P4 - SPI 3 bus #endif // This defines are not representing the real Divider of the ESP32 diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index ae207a7ff3c..c0d7665df03 100644 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -63,9 +63,9 @@ SPIClass::~SPIClass() { #endif } -void SPIClass::begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) { +bool SPIClass::begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) { if (_spi) { - return; + return true; } if (!_div) { @@ -74,7 +74,7 @@ void SPIClass::begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) { _spi = spiStartBus(_spi_num, _div, SPI_MODE0, SPI_MSBFIRST); if (!_spi) { - return; + return false; } if (sck == -1 && miso == -1 && mosi == -1 && ss == -1) { @@ -110,10 +110,11 @@ void SPIClass::begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) { if (_mosi >= 0 && !spiAttachMOSI(_spi, _mosi)) { goto err; } - return; + return true; err: log_e("Attaching pins to SPI failed."); + return false; } void SPIClass::end() { diff --git a/libraries/SPI/src/SPI.h b/libraries/SPI/src/SPI.h index 628c2190f50..6c300e53df2 100644 --- a/libraries/SPI/src/SPI.h +++ b/libraries/SPI/src/SPI.h @@ -61,7 +61,7 @@ class SPIClass { public: SPIClass(uint8_t spi_bus = HSPI); ~SPIClass(); - void begin(int8_t sck = -1, int8_t miso = -1, int8_t mosi = -1, int8_t ss = -1); + bool begin(int8_t sck = -1, int8_t miso = -1, int8_t mosi = -1, int8_t ss = -1); void end(); void setHwCs(bool use); From f7889116b1080fbc1d802c67f795132adb69bd77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Wed, 18 Jun 2025 19:55:11 +0200 Subject: [PATCH 25/42] feat(openthread): Add RLOC16 in otPrintNetworkInformation() (#11480) * feat(openthread): Add RLOC16 in otPrintNetworkInformation() --- libraries/OpenThread/src/OThread.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/OpenThread/src/OThread.cpp b/libraries/OpenThread/src/OThread.cpp index 43d8ce02918..7714d870cce 100644 --- a/libraries/OpenThread/src/OThread.cpp +++ b/libraries/OpenThread/src/OThread.cpp @@ -335,6 +335,8 @@ void OpenThread::otPrintNetworkInformation(Stream &output) { output.printf("Role: %s", otGetStringDeviceRole()); output.println(); + output.printf("RLOC16: 0x%04x", otThreadGetRloc16(mInstance)); // RLOC16 + output.println(); output.printf("Network Name: %s", otThreadGetNetworkName(mInstance)); output.println(); output.printf("Channel: %d", otLinkGetChannel(mInstance)); From 51f1367d57e07f6dd68b400771b354b501e1844f Mon Sep 17 00:00:00 2001 From: Wulu Date: Fri, 20 Jun 2025 17:22:01 +0800 Subject: [PATCH 26/42] fix(docs): correct code block indentation in core_compatibility.rst (#11471) * fix(docs): correct code block indentation in core compatibility guide * fix(docs): remove extra colon causing rendering error in core_compatibility.rst --- docs/en/guides/core_compatibility.rst | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/en/guides/core_compatibility.rst b/docs/en/guides/core_compatibility.rst index cb530ac5e7c..8b6a8e79d26 100644 --- a/docs/en/guides/core_compatibility.rst +++ b/docs/en/guides/core_compatibility.rst @@ -9,28 +9,28 @@ Welcome to the compatibility guide for library developers aiming to support mult Code Adaptations ---------------- -To ensure compatibility with both versions of the ESP32 Arduino core, developers should utilize conditional compilation directives in their code. Below is an example of how to conditionally include code based on the ESP32 Arduino core version:: +To ensure compatibility with both versions of the ESP32 Arduino core, developers should utilize conditional compilation directives in their code. Below is an example of how to conditionally include code based on the ESP32 Arduino core version: - .. code-block:: cpp +.. code-block:: cpp - #ifdef ESP_ARDUINO_VERSION_MAJOR - #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) - // Code for version 3.x - #else - // Code for version 2.x - #endif - #else - // Code for version 1.x - #endif + #ifdef ESP_ARDUINO_VERSION_MAJOR + #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) + // Code for version 3.x + #else + // Code for version 2.x + #endif + #else + // Code for version 1.x + #endif Version Print ------------- -To easily print the ESP32 Arduino core version at runtime, developers can use the `ESP_ARDUINO_VERSION_STR` macro. Below is an example of how to print the ESP32 Arduino core version:: +To easily print the ESP32 Arduino core version at runtime, developers can use the `ESP_ARDUINO_VERSION_STR` macro. Below is an example of how to print the ESP32 Arduino core version: - .. code-block:: cpp +.. code-block:: cpp - Serial.printf(" ESP32 Arduino core version: %s\n", ESP_ARDUINO_VERSION_STR); + Serial.printf(" ESP32 Arduino core version: %s\n", ESP_ARDUINO_VERSION_STR); API Differences --------------- From 02be6e8826fb7fad02b1d5b8492eabf541b21e03 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 20 Jun 2025 06:23:18 -0300 Subject: [PATCH 27/42] feat(esptool): Upgrade to esptool v5 (#11487) --- .github/scripts/package_esptool.sh | 129 ++++++++++++++++++++++ package/package_esp32_index.template.json | 70 ++++++------ platform.txt | 12 +- 3 files changed, 170 insertions(+), 41 deletions(-) create mode 100755 .github/scripts/package_esptool.sh diff --git a/.github/scripts/package_esptool.sh b/.github/scripts/package_esptool.sh new file mode 100755 index 00000000000..32b87b277e9 --- /dev/null +++ b/.github/scripts/package_esptool.sh @@ -0,0 +1,129 @@ +#!/bin/bash + +set -euo pipefail + +# Check version argument +if [[ $# -ne 3 ]]; then + echo "Usage: $0 " + echo "Example: $0 5.0.dev1 /tmp/esptool /tmp/esptool-5.0.dev1.json" + exit 1 +fi + +VERSION=$1 +BASE_FOLDER=$2 +JSON_PATH=$3 + +export COPYFILE_DISABLE=1 + +shopt -s nullglob # So for loop doesn't run if no matches + +# Function to update JSON for a given host +function update_json_for_host { + local host=$1 + local archive=$2 + + # Extract the old url from the JSON for this host, then replace only the filename + old_url=$(jq -r --arg host "$host" ' + .packages[].tools[] | select(.name == "esptool_py") | .systems[] | select(.host == $host) | .url // empty + ' "$tmp_json") + if [[ -n "$old_url" ]]; then + base_url="${old_url%/*}" + url="$base_url/$archive" + else + echo "No old url found for $host" + exit 1 + fi + + archiveFileName="$archive" + checksum="SHA-256:$(shasum -a 256 "$archive" | awk '{print $1}')" + size=$(stat -f%z "$archive") + + # Use jq to update the JSON + jq --arg host "$host" \ + --arg url "$url" \ + --arg archiveFileName "$archiveFileName" \ + --arg checksum "$checksum" \ + --arg size "$size" \ + ' + .packages[].tools[] + |= if .name == "esptool_py" then + .systems = ( + ((.systems // []) | map(select(.host != $host))) + [{ + host: $host, + url: $url, + archiveFileName: $archiveFileName, + checksum: $checksum, + size: $size + }] + ) + else + . + end + ' "$tmp_json" > "$tmp_json.new" && mv "$tmp_json.new" "$tmp_json" +} + +cd "$BASE_FOLDER" + +# Delete all archives before starting +rm -f esptool-*.tar.gz esptool-*.zip + +for dir in esptool-*; do + # Check if directory exists and is a directory + if [[ ! -d "$dir" ]]; then + continue + fi + + base="${dir#esptool-}" + + # Add 'linux-' prefix if base doesn't contain linux/macos/win64 + if [[ "$base" != *linux* && "$base" != *macos* && "$base" != *win64* ]]; then + base="linux-${base}" + fi + + if [[ "$dir" == esptool-win* ]]; then + # Windows zip archive + zipfile="esptool-v${VERSION}-${base}.zip" + echo "Creating $zipfile from $dir ..." + zip -r "$zipfile" "$dir" + else + # Non-Windows: set permissions and tar.gz archive + tarfile="esptool-v${VERSION}-${base}.tar.gz" + echo "Setting permissions and creating $tarfile from $dir ..." + chmod -R u=rwx,g=rx,o=rx "$dir" + tar -cvzf "$tarfile" "$dir" + fi +done + +# After the for loop, update the JSON for each archive +# Create a temporary JSON file to accumulate changes +tmp_json="${JSON_PATH}.tmp" +cp "$JSON_PATH" "$tmp_json" + +for archive in esptool-v"${VERSION}"-*.tar.gz esptool-v"${VERSION}"-*.zip; do + [ -f "$archive" ] || continue + + echo "Updating JSON for $archive" + + # Determine host from archive name + case "$archive" in + *linux-amd64*) host="x86_64-pc-linux-gnu" ;; + *linux-armv7*) host="arm-linux-gnueabihf" ;; + *linux-aarch64*) host="aarch64-linux-gnu" ;; + *macos-amd64*) host="x86_64-apple-darwin" ;; + *macos-arm64*) host="arm64-apple-darwin" ;; + *win64*) hosts=("x86_64-mingw32" "i686-mingw32") ;; + *) echo "Unknown host for $archive"; continue ;; + esac + + # For win64, loop over both hosts; otherwise, use a single host + if [[ "$archive" == *win64* ]]; then + for host in "${hosts[@]}"; do + update_json_for_host "$host" "$archive" + done + else + update_json_for_host "$host" "$archive" + fi +done + +# After all archives are processed, move the temporary JSON to the final file +mv "$tmp_json" "$JSON_PATH" diff --git a/package/package_esp32_index.template.json b/package/package_esp32_index.template.json index 14aa8e9b4cf..22d3cc05455 100644 --- a/package/package_esp32_index.template.json +++ b/package/package_esp32_index.template.json @@ -81,7 +81,7 @@ { "packager": "esp32", "name": "esptool_py", - "version": "4.9.dev3" + "version": "5.0.dev1" }, { "packager": "esp32", @@ -469,56 +469,56 @@ }, { "name": "esptool_py", - "version": "4.9.dev3", + "version": "5.0.dev1", "systems": [ { - "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-linux-amd64.tar.gz", - "archiveFileName": "esptool-v4.9.dev3-linux-amd64.tar.gz", - "checksum": "SHA-256:4ecaf51836cbf4ea3c19840018bfef3b0b8cd8fc3c95f6e1e043ca5bbeab9bf0", - "size": "64958202" + "host": "aarch64-linux-gnu", + "url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-linux-aarch64.tar.gz", + "archiveFileName": "esptool-v5.0.dev1-linux-aarch64.tar.gz", + "checksum": "SHA-256:bfafa7a7723ebbabfd8b6e3ca5ae00bfead0331de923754aeddb43b2c116a078", + "size": "58241736" }, { - "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-linux-armv7.tar.gz", - "archiveFileName": "esptool-v4.9.dev3-linux-armv7.tar.gz", - "checksum": "SHA-256:fff818573bce483ee793ac83c8211f6abf764aa3350f198228859f696a0a0b36", - "size": "31530030" + "host": "x86_64-pc-linux-gnu", + "url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-linux-amd64.tar.gz", + "archiveFileName": "esptool-v5.0.dev1-linux-amd64.tar.gz", + "checksum": "SHA-256:acd0486e96586b99d053a1479acbbbfcae8667227c831cdc53a171f9ccfa27ee", + "size": "100740042" }, { - "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-linux-aarch64.tar.gz", - "archiveFileName": "esptool-v4.9.dev3-linux-aarch64.tar.gz", - "checksum": "SHA-256:5b274bdff2f62e6a07c3c1dfa51b1128924621f661747eca3dbe0f77972f2f06", - "size": "33663882" + "host": "arm-linux-gnueabihf", + "url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-linux-armv7.tar.gz", + "archiveFileName": "esptool-v5.0.dev1-linux-armv7.tar.gz", + "checksum": "SHA-256:ea77a38681506761bbb7b0b39c130811ed565667b67ebbdb4d6dcc6cb6e07368", + "size": "53451939" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-macos-amd64.tar.gz", - "archiveFileName": "esptool-v4.9.dev3-macos-amd64.tar.gz", - "checksum": "SHA-256:c733c83b58fcf5f642fbb2fddb8ff24640c2c785126cba0821fb70c4a5ceea7a", - "size": "32767836" + "url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-macos-amd64.tar.gz", + "archiveFileName": "esptool-v5.0.dev1-macos-amd64.tar.gz", + "checksum": "SHA-256:900a8e90731208bee96647e0e207a43612b9452c2120c4fdc0ff4c6be226257b", + "size": "59631998" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-macos-arm64.tar.gz", - "archiveFileName": "esptool-v4.9.dev3-macos-arm64.tar.gz", - "checksum": "SHA-256:83c195a15981e6a5e7a130db2ccfb21e2d8093912e5b003681f9a5abadd71af7", - "size": "30121441" + "url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-macos-arm64.tar.gz", + "archiveFileName": "esptool-v5.0.dev1-macos-arm64.tar.gz", + "checksum": "SHA-256:3653f4de73cb4fc6a25351eaf663708e91c65ae3265d75bd54ca4315a4350bb4", + "size": "56349992" }, { - "host": "i686-mingw32", - "url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-win64.zip", - "archiveFileName": "esptool-v4.9.dev3-win64.zip", - "checksum": "SHA-256:890051a4fdc684ff6f4af18d0bb27d274ca940ee0eef716a9455f8c64b25b215", - "size": "36072564" + "host": "x86_64-mingw32", + "url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-win64.zip", + "archiveFileName": "esptool-v5.0.dev1-win64.zip", + "checksum": "SHA-256:1e8fd89645daf94f2d4406ec73c9004e617ea921079515f9fd749205eece4d6d", + "size": "59102658" }, { - "host": "x86_64-mingw32", - "url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-win64.zip", - "archiveFileName": "esptool-v4.9.dev3-win64.zip", - "checksum": "SHA-256:890051a4fdc684ff6f4af18d0bb27d274ca940ee0eef716a9455f8c64b25b215", - "size": "36072564" + "host": "i686-mingw32", + "url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-win64.zip", + "archiveFileName": "esptool-v5.0.dev1-win64.zip", + "checksum": "SHA-256:1e8fd89645daf94f2d4406ec73c9004e617ea921079515f9fd749205eece4d6d", + "size": "59102658" } ] }, diff --git a/platform.txt b/platform.txt index 65be05b3bf4..8c7f4432377 100644 --- a/platform.txt +++ b/platform.txt @@ -119,7 +119,7 @@ recipe.hooks.prebuild.2.pattern.windows=cmd /c if not exist "{build.path}\partit recipe.hooks.prebuild.3.pattern.windows=cmd /c if not exist "{build.path}\partitions.csv" COPY "{runtime.platform.path}\tools\partitions\{build.partitions}.csv" "{build.path}\partitions.csv" # Check if custom bootloader exist: source > variant > build.boot -recipe.hooks.prebuild.4.pattern_args=--chip {build.mcu} elf2image --flash_mode {build.flash_mode} --flash_freq {build.img_freq} --flash_size {build.flash_size} -o +recipe.hooks.prebuild.4.pattern_args=--chip {build.mcu} elf2image --flash-mode {build.flash_mode} --flash-freq {build.img_freq} --flash-size {build.flash_size} -o recipe.hooks.prebuild.4.pattern=/usr/bin/env bash -c "[ -f "{build.source.path}"/bootloader.bin ] && cp -f "{build.source.path}"/bootloader.bin "{build.path}"/{build.project_name}.bootloader.bin || ( [ -f "{build.variant.path}"/{build.custom_bootloader}.bin ] && cp "{build.variant.path}"/{build.custom_bootloader}.bin "{build.path}"/{build.project_name}.bootloader.bin || "{tools.esptool_py.path}"/{tools.esptool_py.cmd} {recipe.hooks.prebuild.4.pattern_args} "{build.path}"/{build.project_name}.bootloader.bin "{compiler.sdk.path}"/bin/bootloader_{build.boot}_{build.boot_freq}.elf )" recipe.hooks.prebuild.4.pattern.windows=cmd /c IF EXIST "{build.source.path}\bootloader.bin" ( COPY /y "{build.source.path}\bootloader.bin" "{build.path}\{build.project_name}.bootloader.bin" ) ELSE ( IF EXIST "{build.variant.path}\{build.custom_bootloader}.bin" ( COPY "{build.variant.path}\{build.custom_bootloader}.bin" "{build.path}\{build.project_name}.bootloader.bin" ) ELSE ( "{tools.esptool_py.path}\{tools.esptool_py.cmd}" {recipe.hooks.prebuild.4.pattern_args} "{build.path}\{build.project_name}.bootloader.bin" "{compiler.sdk.path}\bin\bootloader_{build.boot}_{build.boot_freq}.elf" ) ) @@ -163,7 +163,7 @@ recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.f recipe.objcopy.partitions.bin.pattern={tools.gen_esp32part.cmd} -q "{build.path}/partitions.csv" "{build.path}/{build.project_name}.partitions.bin" ## Create bin -recipe.objcopy.bin.pattern_args=--chip {build.mcu} elf2image --flash_mode "{build.flash_mode}" --flash_freq "{build.img_freq}" --flash_size "{build.flash_size}" --elf-sha256-offset 0xb0 -o "{build.path}/{build.project_name}.bin" "{build.path}/{build.project_name}.elf" +recipe.objcopy.bin.pattern_args=--chip {build.mcu} elf2image --flash-mode "{build.flash_mode}" --flash-freq "{build.img_freq}" --flash-size "{build.flash_size}" --elf-sha256-offset 0xb0 -o "{build.path}/{build.project_name}.bin" "{build.path}/{build.project_name}.elf" recipe.objcopy.bin.pattern="{tools.esptool_py.path}/{tools.esptool_py.cmd}" {recipe.objcopy.bin.pattern_args} ## Create Insights Firmware Package @@ -176,7 +176,7 @@ recipe.hooks.objcopy.postobjcopy.2.pattern=/usr/bin/env bash -c "[ ! -d "{build. recipe.hooks.objcopy.postobjcopy.2.pattern.windows=cmd /c if exist "{build.path}\libraries\ESP_SR" if exist "{compiler.sdk.path}\esp_sr\srmodels.bin" COPY /y "{compiler.sdk.path}\esp_sr\srmodels.bin" "{build.path}\srmodels.bin" # Create merged binary -recipe.hooks.objcopy.postobjcopy.3.pattern_args=--chip {build.mcu} merge_bin -o "{build.path}/{build.project_name}.merged.bin" --fill-flash-size {build.flash_size} --flash_mode keep --flash_freq keep --flash_size keep {build.bootloader_addr} "{build.path}/{build.project_name}.bootloader.bin" 0x8000 "{build.path}/{build.project_name}.partitions.bin" 0xe000 "{runtime.platform.path}/tools/partitions/boot_app0.bin" 0x10000 "{build.path}/{build.project_name}.bin" +recipe.hooks.objcopy.postobjcopy.3.pattern_args=--chip {build.mcu} merge-bin -o "{build.path}/{build.project_name}.merged.bin" --pad-to-size {build.flash_size} --flash-mode keep --flash-freq keep --flash-size keep {build.bootloader_addr} "{build.path}/{build.project_name}.bootloader.bin" 0x8000 "{build.path}/{build.project_name}.partitions.bin" 0xe000 "{runtime.platform.path}/tools/partitions/boot_app0.bin" 0x10000 "{build.path}/{build.project_name}.bin" recipe.hooks.objcopy.postobjcopy.3.pattern="{tools.esptool_py.path}/{tools.esptool_py.cmd}" {recipe.hooks.objcopy.postobjcopy.3.pattern_args} ## Save bin @@ -285,14 +285,14 @@ debug.additional_config=debug_config.{build.mcu} tools.esptool_py.upload.protocol=serial tools.esptool_py.upload.params.verbose= tools.esptool_py.upload.params.quiet= -tools.esptool_py.upload.pattern_args=--chip {build.mcu} --port "{serial.port}" --baud {upload.speed} {upload.flags} --before default_reset --after hard_reset write_flash {upload.erase_cmd} -z --flash_mode keep --flash_freq keep --flash_size keep {build.bootloader_addr} "{build.path}/{build.project_name}.bootloader.bin" 0x8000 "{build.path}/{build.project_name}.partitions.bin" 0xe000 "{runtime.platform.path}/tools/partitions/boot_app0.bin" 0x10000 "{build.path}/{build.project_name}.bin" {upload.extra_flags} +tools.esptool_py.upload.pattern_args=--chip {build.mcu} --port "{serial.port}" --baud {upload.speed} {upload.flags} --before default-reset --after hard-reset write-flash {upload.erase_cmd} -z --flash-mode keep --flash-freq keep --flash-size keep {build.bootloader_addr} "{build.path}/{build.project_name}.bootloader.bin" 0x8000 "{build.path}/{build.project_name}.partitions.bin" 0xe000 "{runtime.platform.path}/tools/partitions/boot_app0.bin" 0x10000 "{build.path}/{build.project_name}.bin" {upload.extra_flags} tools.esptool_py.upload.pattern="{path}/{cmd}" {upload.pattern_args} ## Program Application ## ------------------- tools.esptool_py.program.params.verbose= tools.esptool_py.program.params.quiet= -tools.esptool_py.program.pattern_args=--chip {build.mcu} --port "{serial.port}" --baud {upload.speed} {upload.flags} --before default_reset --after hard_reset write_flash -z --flash_mode keep --flash_freq keep --flash_size keep 0x10000 "{build.path}/{build.project_name}.bin" +tools.esptool_py.program.pattern_args=--chip {build.mcu} --port "{serial.port}" --baud {upload.speed} {upload.flags} --before default-reset --after hard-reset write-flash -z --flash-mode keep --flash-freq keep --flash-size keep 0x10000 "{build.path}/{build.project_name}.bin" tools.esptool_py.program.pattern="{path}/{cmd}" {program.pattern_args} ## Erase Chip (before burning the bootloader) @@ -300,7 +300,7 @@ tools.esptool_py.program.pattern="{path}/{cmd}" {program.pattern_args} tools.esptool_py.erase.protocol=serial tools.esptool_py.erase.params.verbose= tools.esptool_py.erase.params.quiet= -tools.esptool_py.erase.pattern_args=--chip {build.mcu} --port "{serial.port}" --baud {upload.speed} {upload.flags} --before default_reset --after hard_reset erase_flash +tools.esptool_py.erase.pattern_args=--chip {build.mcu} --port "{serial.port}" --baud {upload.speed} {upload.flags} --before default-reset --after hard-reset erase-flash tools.esptool_py.erase.pattern="{path}/{cmd}" {erase.pattern_args} ## Burn Bootloader From 016077e2450a29bf94c681e9db23ace55a025e5f Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 20 Jun 2025 11:25:03 +0200 Subject: [PATCH 28/42] changes for updated esptool.py v5 (#11488) --- tools/pioarduino-build.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/pioarduino-build.py b/tools/pioarduino-build.py index 3335a716888..b6a0c0435c5 100644 --- a/tools/pioarduino-build.py +++ b/tools/pioarduino-build.py @@ -99,11 +99,11 @@ def generate_bootloader_image(bootloader_elf): "--chip", build_mcu, "elf2image", - "--flash_mode", + "--flash-mode", "${__get_board_flash_mode(__env__)}", - "--flash_freq", + "--flash-freq", "${__get_board_f_image(__env__)}", - "--flash_size", + "--flash-size", board_config.get("upload.flash_size", "4MB"), "-o", "$TARGET", From 23c6abc1acd26f171827a7459572f9efc216de0e Mon Sep 17 00:00:00 2001 From: Olaf Date: Fri, 20 Jun 2025 11:52:21 +0200 Subject: [PATCH 29/42] Proper EDNS handling and cleaner NOERROR response in DNSSERVER (#11411) * Proper EDNS handling and cleaner NOERROR response * fix: library.properties reverting version number update - as it is done automatically * ci(pre-commit): Apply automatic fixes * Spelling Corrected and minor clarification in comments * Removing commented out code fragments * ci(pre-commit): Apply automatic fixes * fix(pr): Fix typo --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Me No Dev --- libraries/DNSServer/src/DNSServer.cpp | 86 +++++++++++++++++++++++++-- libraries/DNSServer/src/DNSServer.h | 22 +++++++ 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index 28cf89d6ede..1f74c96c733 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -111,16 +111,22 @@ void DNSServer::_handleUDP(AsyncUDPPacket &pkt) { // will reply with IP only to "*" or if domain matches without www. subdomain if (dnsHeader.OPCode == DNS_OPCODE_QUERY && requestIncludesOnlyOneQuestion(dnsHeader) && (_domainName.isEmpty() || getDomainNameWithoutWwwPrefix(static_cast(dnsQuestion.QName), dnsQuestion.QNameLength) == _domainName)) { - replyWithIP(pkt, dnsHeader, dnsQuestion); + + // 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); + } else { + replyWithNoAnsw(pkt, dnsHeader, dnsQuestion); + } return; } - // otherwise reply with custom code replyWithCustomCode(pkt, 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) { @@ -139,7 +145,6 @@ String DNSServer::getDomainNameWithoutWwwPrefix(const unsigned char *start, size void DNSServer::replyWithIP(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion) { AsyncUDPMessage rpl; - // Change the type of message to a response and set the number of answers equal to // the number of questions in the header dnsHeader.QR = DNS_QR_RESPONSE; @@ -187,3 +192,76 @@ void DNSServer::replyWithCustomCode(AsyncUDPPacket &req, DNSHeader &dnsHeader) { rpl.write(reinterpret_cast(&dnsHeader), sizeof(DNSHeader)); _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(&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()); +} diff --git a/libraries/DNSServer/src/DNSServer.h b/libraries/DNSServer/src/DNSServer.h index dfd9a45604d..f2716defc7d 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -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_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 { NoError = 0, FormError = 1, @@ -179,5 +199,7 @@ class DNSServer { inline bool requestIncludesOnlyOneQuestion(DNSHeader &dnsHeader); void replyWithIP(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion); inline void replyWithCustomCode(AsyncUDPPacket &req, DNSHeader &dnsHeader); + inline void replyWithNoAnsw(AsyncUDPPacket &req, DNSHeader &dnsHeader, DNSQuestion &dnsQuestion); + void _handleUDP(AsyncUDPPacket &pkt); }; From ccda9c5f846532972a3f497addba0623935f7cb9 Mon Sep 17 00:00:00 2001 From: Sugar Glider Date: Fri, 20 Jun 2025 15:28:07 -0300 Subject: [PATCH 30/42] feat(matter): Adds Matter Events callback plus example (#11465) * feat(matter): Adds Matter Events callback and related example --- .../examples/MatterEvents/MatterEvents.ino | 168 ++++++++++++++++++ .../Matter/examples/MatterEvents/ci.json | 7 + libraries/Matter/keywords.txt | 39 +++- libraries/Matter/src/Matter.cpp | 37 ++-- libraries/Matter/src/Matter.h | 126 +++++++++++++ libraries/Matter/src/MatterEndPoint.h | 20 +-- 6 files changed, 369 insertions(+), 28 deletions(-) create mode 100644 libraries/Matter/examples/MatterEvents/MatterEvents.ino create mode 100644 libraries/Matter/examples/MatterEvents/ci.json diff --git a/libraries/Matter/examples/MatterEvents/MatterEvents.ino b/libraries/Matter/examples/MatterEvents/MatterEvents.ino new file mode 100644 index 00000000000..dac599bf9fa --- /dev/null +++ b/libraries/Matter/examples/MatterEvents/MatterEvents.ino @@ -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 +#include + +// 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."); +} diff --git a/libraries/Matter/examples/MatterEvents/ci.json b/libraries/Matter/examples/MatterEvents/ci.json new file mode 100644 index 00000000000..556a8a9ee6b --- /dev/null +++ b/libraries/Matter/examples/MatterEvents/ci.json @@ -0,0 +1,7 @@ +{ + "fqbn_append": "PartitionScheme=huge_app", + "requires": [ + "CONFIG_SOC_WIFI_SUPPORTED=y", + "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" + ] +} diff --git a/libraries/Matter/keywords.txt b/libraries/Matter/keywords.txt index a63d9a65acb..68aaebb1d4d 100644 --- a/libraries/Matter/keywords.txt +++ b/libraries/Matter/keywords.txt @@ -36,6 +36,8 @@ EndPointSpeedCB KEYWORD1 EndPointOnOffCB KEYWORD1 EndPointBrightnessCB KEYWORD1 EndPointRGBColorCB KEYWORD1 +matterEvent_t KEYWORD1 +matterEventCB KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -108,6 +110,7 @@ onChangeMode KEYWORD2 onChangeLocalTemperature KEYWORD2 onChangeCoolingSetpoint KEYWORD2 onChangeHeatingSetpoint KEYWORD2 +onEvent KEYWORD2 ####################################### # Constants (LITERAL1) @@ -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 diff --git a/libraries/Matter/src/Matter.cpp b/libraries/Matter/src/Matter.cpp index af7c4c8657e..b16edfd85c1 100644 --- a/libraries/Matter/src/Matter.cpp +++ b/libraries/Matter/src/Matter.cpp @@ -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 @@ -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; @@ -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; } @@ -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()) { @@ -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(event->Type), event); + } } void ArduinoMatter::_init() { diff --git a/libraries/Matter/src/Matter.h b/libraries/Matter/src/Matter.h index e54ceb47e5e..682a0498076 100644 --- a/libraries/Matter/src/Matter.h +++ b/libraries/Matter/src/Matter.h @@ -34,10 +34,136 @@ #include #include +// 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; class ArduinoMatter { public: + // Matter Event Callback type + using matterEventCB = std::function; + // Matter Event Callback + static matterEventCB _matterEventCB; + // set the Matter Event Callback + static void onEvent(matterEventCB cb) { + _matterEventCB = cb; + } + static inline String getManualPairingCode() { // return the pairing code for manual pairing return String("34970112332"); diff --git a/libraries/Matter/src/MatterEndPoint.h b/libraries/Matter/src/MatterEndPoint.h index 5baa4747d18..95d3d3c08df 100644 --- a/libraries/Matter/src/MatterEndPoint.h +++ b/libraries/Matter/src/MatterEndPoint.h @@ -41,22 +41,22 @@ class MatterEndPoint { esp_matter::attribute_t *getAttribute(uint32_t cluster_id, uint32_t attribute_id) { if (endpoint_id == 0) { log_e("Endpoint ID is not set"); - return NULL; + return nullptr; } endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); - if (endpoint == NULL) { + if (endpoint == nullptr) { log_e("Endpoint [%d] not found", endpoint_id); - return NULL; + return nullptr; } cluster_t *cluster = cluster::get(endpoint, cluster_id); - if (cluster == NULL) { + if (cluster == nullptr) { log_e("Cluster [%d] not found", cluster_id); - return NULL; + return nullptr; } esp_matter::attribute_t *attribute = attribute::get(cluster, attribute_id); - if (attribute == NULL) { + if (attribute == nullptr) { log_e("Attribute [%d] not found", attribute_id); - return NULL; + return nullptr; } return attribute; } @@ -64,7 +64,7 @@ class MatterEndPoint { // 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) { esp_matter::attribute_t *attribute = getAttribute(cluster_id, attribute_id); - if (attribute == NULL) { + if (attribute == nullptr) { return false; } if (attribute::get_val(attribute, attrVal) == ESP_OK) { @@ -78,7 +78,7 @@ class MatterEndPoint { // 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) { esp_matter::attribute_t *attribute = getAttribute(cluster_id, attribute_id); - if (attribute == NULL) { + if (attribute == nullptr) { return false; } if (attribute::set_val(attribute, attrVal) == ESP_OK) { @@ -117,6 +117,6 @@ class MatterEndPoint { protected: uint16_t endpoint_id = 0; - EndPointIdentifyCB _onEndPointIdentifyCB = NULL; + EndPointIdentifyCB _onEndPointIdentifyCB = nullptr; }; #endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ From 01e4d5debbbc1b35dd0dfd6853a29b8ebba02f10 Mon Sep 17 00:00:00 2001 From: Sugar Glider Date: Sat, 21 Jun 2025 09:45:26 -0300 Subject: [PATCH 31/42] feat(uart): fixes loopback function after IDF changes (#11492) * feat(uart): fixes loopback function after IDF changes IDF 5.4.1 has added a new function called uart_release_pin() that is called whenever new pins are set or when uart driver is deleted. This has a side effect that causes RX pin to never work again with the loopback function. Other changes also have removed some GPIO setup that was necessary for the GPIO loopback mode work. The PR forces a full RX Pin setup in order to make it work in GPIO Matrix with Loopback TX Signal * feat(uart): adds missing include file * feat(uart): removes not necessary part of the code * fix(uart): commentaries style fix Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- cores/esp32/esp32-hal-uart.c | 46 +++++++++--------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 5311aff4f37..3c9e6bf178b 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -32,10 +32,11 @@ #include "driver/gpio.h" #include "hal/gpio_hal.h" #include "esp_rom_gpio.h" +#include "esp_private/gpio.h" #include "driver/rtc_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" static int s_uart_debug_nr = 0; // UART number for debug output @@ -1383,39 +1384,9 @@ unsigned long uartDetectBaudrate(uart_t *uart) { } /* - These functions are for testing purpose only and can be used in Arduino Sketches - Those are used in the UART examples -*/ - -/* - 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 + * These functions are for testing purposes only and can be used in Arduino Sketches. + * They are utilized in the UART examples and CI. + */ /* This function internally binds defined UARTs TX signal with defined RX pin of any UART (same or different). @@ -1427,7 +1398,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); 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); } /* From 9d84c78cf2d44911639530e54a9dbb9ee9164f6c Mon Sep 17 00:00:00 2001 From: Sugar Glider Date: Sun, 22 Jun 2025 15:02:36 -0300 Subject: [PATCH 32/42] feat(uart): fixes pin attach for any IDF 5.x (#11499) * feat(uart): fixes pin attach for any IDF 5.x * fix(uart): commentary typo error Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix(uart): commentary typo error Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix(uart): commentary typo error Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix(uart): commentary style fix * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- cores/esp32/esp32-hal-uart.c | 131 +++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 5 deletions(-) diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 3c9e6bf178b..2163e9b5f42 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -295,6 +295,125 @@ static bool _uartDetachBus_RTS(void *busptr) { return _uartDetachPins(bus->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, bus->_rtsPin); } +static bool _uartTrySetIomuxPin(uart_port_t uart_num, int io_num, uint32_t idx) { + // Store a pointer to the default pin, to optimize access to its fields. + const uart_periph_sig_t *upin = &uart_periph_signal[uart_num].pins[idx]; + + // In theory, if default_gpio is -1, iomux_func should also be -1, but let's be safe and test both. + if (upin->iomux_func == -1 || upin->default_gpio == -1 || upin->default_gpio != io_num) { + return false; + } + + // Assign the correct function to the GPIO. + assert(upin->iomux_func != -1); + if (uart_num < SOC_UART_HP_NUM) { + gpio_iomux_out(io_num, upin->iomux_func, false); + // If the pin is input, we also have to redirect the signal, in order to bypass the GPIO matrix. + if (upin->input) { + gpio_iomux_in(io_num, upin->signal); + } + } +#if (SOC_UART_LP_NUM >= 1) && (SOC_RTCIO_PIN_COUNT >= 1) + else { + if (upin->input) { + rtc_gpio_set_direction(io_num, RTC_GPIO_MODE_INPUT_ONLY); + } else { + rtc_gpio_set_direction(io_num, RTC_GPIO_MODE_OUTPUT_ONLY); + } + rtc_gpio_init(io_num); + rtc_gpio_iomux_func_sel(io_num, upin->iomux_func); + } +#endif + return true; +} + +static esp_err_t _uartInternalSetPin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) { + // Since an IO cannot route peripheral signals via IOMUX and GPIO matrix at the same time, + // if tx and rx share the same IO, both signals need to be routed to IOs through GPIO matrix + bool tx_rx_same_io = (tx_io_num == rx_io_num); + + // In the following statements, if the io_num is negative, no need to configure anything. + if (tx_io_num >= 0) { +#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO + // In such case, IOs are going to switch to sleep configuration (isolate) when entering sleep for power saving reason + // But TX IO in isolate state could write garbled data to the other end + // Therefore, we should disable the switch of the TX pin to sleep configuration + gpio_sleep_sel_dis(tx_io_num); +#endif + if (tx_rx_same_io || !_uartTrySetIomuxPin(uart_num, tx_io_num, SOC_UART_TX_PIN_IDX)) { + if (uart_num < SOC_UART_HP_NUM) { + gpio_func_sel(tx_io_num, PIN_FUNC_GPIO); + esp_rom_gpio_connect_out_signal(tx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_TX_PIN_IDX), 0, 0); + // output enable is set inside esp_rom_gpio_connect_out_signal func after the signal is connected + // (output enabled too early may cause unnecessary level change at the pad) + } +#if SOC_LP_GPIO_MATRIX_SUPPORTED + else { + rtc_gpio_init(tx_io_num); // set as a LP_GPIO pin + lp_gpio_connect_out_signal(tx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_TX_PIN_IDX), 0, 0); + // output enable is set inside lp_gpio_connect_out_signal func after the signal is connected + } +#endif + } + } + + if (rx_io_num >= 0) { +#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO + // In such case, IOs are going to switch to sleep configuration (isolate) when entering sleep for power saving reason + // But RX IO in isolate state could receive garbled data into FIFO, which is not desired + // Therefore, we should disable the switch of the RX pin to sleep configuration + gpio_sleep_sel_dis(rx_io_num); +#endif + if (tx_rx_same_io || !_uartTrySetIomuxPin(uart_num, rx_io_num, SOC_UART_RX_PIN_IDX)) { + if (uart_num < SOC_UART_HP_NUM) { + gpio_input_enable(rx_io_num); + esp_rom_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0); + } +#if SOC_LP_GPIO_MATRIX_SUPPORTED + else { + rtc_gpio_mode_t mode = (tx_rx_same_io ? RTC_GPIO_MODE_INPUT_OUTPUT : RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_set_direction(rx_io_num, mode); + if (!tx_rx_same_io) { // set the same pin again as a LP_GPIO will overwrite connected out_signal, not desired, so skip + rtc_gpio_init(rx_io_num); // set as a LP_GPIO pin + } + lp_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0); + } +#endif + } + } + + if (rts_io_num >= 0 && !_uartTrySetIomuxPin(uart_num, rts_io_num, SOC_UART_RTS_PIN_IDX)) { + if (uart_num < SOC_UART_HP_NUM) { + gpio_func_sel(rts_io_num, PIN_FUNC_GPIO); + esp_rom_gpio_connect_out_signal(rts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RTS_PIN_IDX), 0, 0); + // output enable is set inside esp_rom_gpio_connect_out_signal func after the signal is connected + } +#if SOC_LP_GPIO_MATRIX_SUPPORTED + else { + rtc_gpio_init(rts_io_num); // set as a LP_GPIO pin + lp_gpio_connect_out_signal(rts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RTS_PIN_IDX), 0, 0); + // output enable is set inside lp_gpio_connect_out_signal func after the signal is connected + } +#endif + } + + if (cts_io_num >= 0 && !_uartTrySetIomuxPin(uart_num, cts_io_num, SOC_UART_CTS_PIN_IDX)) { + if (uart_num < SOC_UART_HP_NUM) { + gpio_pullup_en(cts_io_num); + gpio_input_enable(cts_io_num); + esp_rom_gpio_connect_in_signal(cts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), 0); + } +#if SOC_LP_GPIO_MATRIX_SUPPORTED + else { + rtc_gpio_set_direction(cts_io_num, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_init(cts_io_num); // set as a LP_GPIO pin + lp_gpio_connect_in_signal(cts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), 0); + } +#endif + } + return ESP_OK; +} + // Attach function for UART // connects the IO Pad, set Paripheral Manager and internal UART structure data static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) { @@ -307,7 +426,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t //log_v("attaching UART%d pins: prev,new RX(%d,%d) TX(%d,%d) CTS(%d,%d) RTS(%d,%d)", uart_num, // uart->_rxPin, rxPin, uart->_txPin, txPin, uart->_ctsPin, ctsPin, uart->_rtsPin, rtsPin); vTaskDelay(10); - // IDF uart_set_pin() checks if the pin is used within LP UART and if it is a valid RTC IO pin + // IDF _uartInternalSetPin() checks if the pin is used within LP UART and if it is a valid RTC IO pin // No need for Arduino Layer to check it again bool retCode = true; if (rxPin >= 0) { @@ -316,7 +435,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t perimanClearPinBus(rxPin); } // connect RX Pad - bool ret = ESP_OK == uart_set_pin(uart->num, UART_PIN_NO_CHANGE, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + bool ret = ESP_OK == _uartInternalSetPin(uart->num, UART_PIN_NO_CHANGE, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); #if SOC_UART_LP_NUM >= 1 if (ret && uart_num >= SOC_UART_HP_NUM) { // it is a LP UART NUM ret &= lp_uart_config_io(uart->num, rxPin, RTC_GPIO_MODE_INPUT_ONLY, SOC_UART_RX_PIN_IDX); @@ -339,7 +458,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t perimanClearPinBus(txPin); } // connect TX Pad - bool ret = ESP_OK == uart_set_pin(uart->num, txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + bool ret = ESP_OK == _uartInternalSetPin(uart->num, txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); #if SOC_UART_LP_NUM >= 1 if (ret && uart_num >= SOC_UART_HP_NUM) { // it is a LP UART NUM ret &= lp_uart_config_io(uart->num, txPin, RTC_GPIO_MODE_OUTPUT_ONLY, SOC_UART_TX_PIN_IDX); @@ -362,7 +481,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t perimanClearPinBus(ctsPin); } // connect CTS Pad - bool ret = ESP_OK == uart_set_pin(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, ctsPin); + bool ret = ESP_OK == _uartInternalSetPin(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, ctsPin); #if SOC_UART_LP_NUM >= 1 if (ret && uart_num >= SOC_UART_HP_NUM) { // it is a LP UART NUM ret &= lp_uart_config_io(uart->num, ctsPin, RTC_GPIO_MODE_INPUT_ONLY, SOC_UART_CTS_PIN_IDX); @@ -385,7 +504,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t perimanClearPinBus(rtsPin); } // connect RTS Pad - bool ret = ESP_OK == uart_set_pin(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, rtsPin, UART_PIN_NO_CHANGE); + bool ret = ESP_OK == _uartInternalSetPin(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, rtsPin, UART_PIN_NO_CHANGE); #if SOC_UART_LP_NUM >= 1 if (ret && uart_num >= SOC_UART_HP_NUM) { // it is a LP UART NUM ret &= lp_uart_config_io(uart->num, rtsPin, RTC_GPIO_MODE_OUTPUT_ONLY, SOC_UART_RTS_PIN_IDX); @@ -1398,11 +1517,13 @@ 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); return; } +#if 0 // leave this code here for future reference and need // 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); +#endif esp_rom_gpio_connect_out_signal(rxPin, uart_periph_signal[uartNum].pins[SOC_UART_TX_PIN_IDX].signal, false, false); } From dc82467ba41c7ed589dfb480db742ea14e65b68f Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Mon, 23 Jun 2025 08:48:37 -0300 Subject: [PATCH 33/42] fix(esp_now): Fix broadcast example and use nullptr (#11490) * fix(esp_now): Fix example and use nullptr * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../ESP_NOW_Broadcast_Master.ino | 2 +- .../ESP_NOW_Broadcast_Slave.ino | 28 ++++++++--- .../ESP_NOW_Network/ESP_NOW_Network.ino | 13 +++-- libraries/ESP_NOW/src/ESP32_NOW.cpp | 42 ++++++++-------- libraries/ESP_NOW/src/ESP32_NOW.h | 4 +- libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp | 50 +++++++++---------- libraries/ESP_NOW/src/ESP32_NOW_Serial.h | 4 +- 7 files changed, 81 insertions(+), 62 deletions(-) diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ESP_NOW_Broadcast_Master.ino b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ESP_NOW_Broadcast_Master.ino index 6ee7dc6f846..025a53c913b 100644 --- a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ESP_NOW_Broadcast_Master.ino +++ b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ESP_NOW_Broadcast_Master.ino @@ -58,7 +58,7 @@ public: uint32_t msg_count = 0; // Create a broadcast peer object -ESP_NOW_Broadcast_Peer broadcast_peer(ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL); +ESP_NOW_Broadcast_Peer broadcast_peer(ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, nullptr); /* Main */ diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ESP_NOW_Broadcast_Slave.ino b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ESP_NOW_Broadcast_Slave.ino index 5f1a8bd8807..e61524b64f9 100644 --- a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ESP_NOW_Broadcast_Slave.ino +++ b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ESP_NOW_Broadcast_Slave.ino @@ -52,7 +52,8 @@ public: /* Global Variables */ // List of all the masters. It will be populated when a new master is registered -std::vector masters; +// Note: Using pointers instead of objects to prevent dangling pointers when the vector reallocates +std::vector masters; /* Callbacks */ @@ -62,13 +63,14 @@ void register_new_master(const esp_now_recv_info_t *info, const uint8_t *data, i Serial.printf("Unknown peer " MACSTR " sent a broadcast message\n", MAC2STR(info->src_addr)); Serial.println("Registering the peer as a master"); - ESP_NOW_Peer_Class new_master(info->src_addr, ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL); - - masters.push_back(new_master); - if (!masters.back().add_peer()) { + ESP_NOW_Peer_Class *new_master = new ESP_NOW_Peer_Class(info->src_addr, ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, nullptr); + if (!new_master->add_peer()) { Serial.println("Failed to register the new master"); + delete new_master; return; } + masters.push_back(new_master); + Serial.printf("Successfully registered master " MACSTR " (total masters: %zu)\n", MAC2STR(new_master->addr()), masters.size()); } else { // The slave will only receive broadcast messages log_v("Received a unicast message from " MACSTR, MAC2STR(info->src_addr)); @@ -103,11 +105,23 @@ void setup() { } // Register the new peer callback - ESP_NOW.onNewPeer(register_new_master, NULL); + ESP_NOW.onNewPeer(register_new_master, nullptr); Serial.println("Setup complete. Waiting for a master to broadcast a message..."); } void loop() { - delay(1000); + // Print debug information every 10 seconds + static unsigned long last_debug = 0; + if (millis() - last_debug > 10000) { + last_debug = millis(); + Serial.printf("Registered masters: %zu\n", masters.size()); + for (size_t i = 0; i < masters.size(); i++) { + if (masters[i]) { + Serial.printf(" Master %zu: " MACSTR "\n", i, MAC2STR(masters[i]->addr())); + } + } + } + + delay(100); } diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino b/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino index 48ea6b731ce..6731340c922 100644 --- a/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino +++ b/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino @@ -123,7 +123,7 @@ public: } bool send_message(const uint8_t *data, size_t len) { - if (data == NULL || len == 0) { + if (data == nullptr || len == 0) { log_e("Data to be sent is NULL or has a length of 0"); return false; } @@ -169,9 +169,12 @@ public: /* Peers */ -std::vector peers; // Create a vector to store the peer pointers -ESP_NOW_Network_Peer broadcast_peer(ESP_NOW.BROADCAST_ADDR, 0, NULL); // Register the broadcast peer (no encryption support for the broadcast address) -ESP_NOW_Network_Peer *master_peer = nullptr; // Pointer to peer that is the master +// Create a vector to store the peer pointers +std::vector peers; +// Register the broadcast peer (no encryption support for the broadcast address) +ESP_NOW_Network_Peer broadcast_peer(ESP_NOW.BROADCAST_ADDR, 0, nullptr); +// Pointer to the peer that is the master +ESP_NOW_Network_Peer *master_peer = nullptr; /* Helper functions */ @@ -279,7 +282,7 @@ void setup() { } // Register the callback to be called when a new peer is found - ESP_NOW.onNewPeer(register_new_peer, NULL); + ESP_NOW.onNewPeer(register_new_peer, nullptr); Serial.println("Setup complete. Broadcasting own priority to find the master..."); memset(&new_msg, 0, sizeof(new_msg)); diff --git a/libraries/ESP_NOW/src/ESP32_NOW.cpp b/libraries/ESP_NOW/src/ESP32_NOW.cpp index 6fd3ff0a0b1..25a5609e8db 100644 --- a/libraries/ESP_NOW/src/ESP32_NOW.cpp +++ b/libraries/ESP_NOW/src/ESP32_NOW.cpp @@ -9,12 +9,12 @@ #include "esp32-hal.h" #include "esp_wifi.h" -static void (*new_cb)(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg) = NULL; -static void *new_arg = NULL; // * tx_arg = NULL, * rx_arg = NULL, +static void (*new_cb)(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg) = nullptr; +static void *new_arg = nullptr; // * tx_arg = nullptr, * rx_arg = nullptr, static bool _esp_now_has_begun = false; static ESP_NOW_Peer *_esp_now_peers[ESP_NOW_MAX_TOTAL_PEER_NUM]; -static esp_err_t _esp_now_add_peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk, ESP_NOW_Peer *_peer = NULL) { +static esp_err_t _esp_now_add_peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk, ESP_NOW_Peer *_peer = nullptr) { log_v(MACSTR, MAC2STR(mac_addr)); if (esp_now_is_peer_exist(mac_addr)) { log_e("Peer Already Exists"); @@ -26,16 +26,16 @@ static esp_err_t _esp_now_add_peer(const uint8_t *mac_addr, uint8_t channel, wif memcpy(peer.peer_addr, mac_addr, ESP_NOW_ETH_ALEN); peer.channel = channel; peer.ifidx = iface; - peer.encrypt = lmk != NULL; + peer.encrypt = lmk != nullptr; if (lmk) { memcpy(peer.lmk, lmk, ESP_NOW_KEY_LEN); } esp_err_t result = esp_now_add_peer(&peer); if (result == ESP_OK) { - if (_peer != NULL) { + if (_peer != nullptr) { for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) { - if (_esp_now_peers[i] == NULL) { + if (_esp_now_peers[i] == nullptr) { _esp_now_peers[i] = _peer; return ESP_OK; } @@ -67,8 +67,8 @@ static esp_err_t _esp_now_del_peer(const uint8_t *mac_addr) { } for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) { - if (_esp_now_peers[i] != NULL && memcmp(mac_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0) { - _esp_now_peers[i] = NULL; + if (_esp_now_peers[i] != nullptr && memcmp(mac_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0) { + _esp_now_peers[i] = nullptr; break; } } @@ -87,7 +87,7 @@ static esp_err_t _esp_now_modify_peer(const uint8_t *mac_addr, uint8_t channel, memcpy(peer.peer_addr, mac_addr, ESP_NOW_ETH_ALEN); peer.channel = channel; peer.ifidx = iface; - peer.encrypt = lmk != NULL; + peer.encrypt = lmk != nullptr; if (lmk) { memcpy(peer.lmk, lmk, ESP_NOW_KEY_LEN); } @@ -111,17 +111,17 @@ static void _esp_now_rx_cb(const esp_now_recv_info_t *info, const uint8_t *data, bool broadcast = memcmp(info->des_addr, ESP_NOW.BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0; log_v("%s from " MACSTR ", data length : %u", broadcast ? "Broadcast" : "Unicast", MAC2STR(info->src_addr), len); log_buf_v(data, len); - if (!esp_now_is_peer_exist(info->src_addr) && new_cb != NULL) { + if (!esp_now_is_peer_exist(info->src_addr) && new_cb != nullptr) { log_v("Calling new_cb, peer not found."); new_cb(info, data, len, new_arg); return; } //find the peer and call it's callback for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) { - if (_esp_now_peers[i] != NULL) { + if (_esp_now_peers[i] != nullptr) { log_v("Checking peer " MACSTR, MAC2STR(_esp_now_peers[i]->addr())); } - if (_esp_now_peers[i] != NULL && memcmp(info->src_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0) { + if (_esp_now_peers[i] != nullptr && memcmp(info->src_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0) { log_v("Calling onReceive"); _esp_now_peers[i]->onReceive(data, len, broadcast); return; @@ -133,7 +133,7 @@ static void _esp_now_tx_cb(const uint8_t *mac_addr, esp_now_send_status_t status log_v(MACSTR " : %s", MAC2STR(mac_addr), (status == ESP_NOW_SEND_SUCCESS) ? "SUCCESS" : "FAILED"); //find the peer and call it's callback for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) { - if (_esp_now_peers[i] != NULL && memcmp(mac_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0) { + if (_esp_now_peers[i] != nullptr && memcmp(mac_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0) { _esp_now_peers[i]->onSent(status == ESP_NOW_SEND_SUCCESS); return; } @@ -197,7 +197,7 @@ bool ESP_NOW_Class::end() { } //remove all peers for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) { - if (_esp_now_peers[i] != NULL) { + if (_esp_now_peers[i] != nullptr) { removePeer(*_esp_now_peers[i]); } } @@ -249,7 +249,7 @@ size_t ESP_NOW_Class::write(const uint8_t *data, size_t len) { if (len > ESP_NOW_MAX_DATA_LEN) { len = ESP_NOW_MAX_DATA_LEN; } - esp_err_t result = esp_now_send(NULL, data, len); + esp_err_t result = esp_now_send(nullptr, data, len); if (result == ESP_OK) { return len; } else if (result == ESP_ERR_ESPNOW_NOT_INIT) { @@ -292,7 +292,7 @@ ESP_NOW_Peer::ESP_NOW_Peer(const uint8_t *mac_addr, uint8_t channel, wifi_interf } chan = channel; ifc = iface; - encrypt = lmk != NULL; + encrypt = lmk != nullptr; if (encrypt) { memcpy(key, lmk, 16); } @@ -305,7 +305,7 @@ bool ESP_NOW_Peer::add() { if (added) { return true; } - if (_esp_now_add_peer(mac, chan, ifc, encrypt ? key : NULL, this) != ESP_OK) { + if (_esp_now_add_peer(mac, chan, ifc, encrypt ? key : nullptr, this) != ESP_OK) { return false; } log_v("Peer added - " MACSTR, MAC2STR(mac)); @@ -350,7 +350,7 @@ bool ESP_NOW_Peer::setChannel(uint8_t channel) { if (!_esp_now_has_begun || !added) { return true; } - return _esp_now_modify_peer(mac, chan, ifc, encrypt ? key : NULL) == ESP_OK; + return _esp_now_modify_peer(mac, chan, ifc, encrypt ? key : nullptr) == ESP_OK; } wifi_interface_t ESP_NOW_Peer::getInterface() const { @@ -362,7 +362,7 @@ bool ESP_NOW_Peer::setInterface(wifi_interface_t iface) { if (!_esp_now_has_begun || !added) { return true; } - return _esp_now_modify_peer(mac, chan, ifc, encrypt ? key : NULL) == ESP_OK; + return _esp_now_modify_peer(mac, chan, ifc, encrypt ? key : nullptr) == ESP_OK; } bool ESP_NOW_Peer::isEncrypted() const { @@ -370,14 +370,14 @@ bool ESP_NOW_Peer::isEncrypted() const { } bool ESP_NOW_Peer::setKey(const uint8_t *lmk) { - encrypt = lmk != NULL; + encrypt = lmk != nullptr; if (encrypt) { memcpy(key, lmk, 16); } if (!_esp_now_has_begun || !added) { return true; } - return _esp_now_modify_peer(mac, chan, ifc, encrypt ? key : NULL) == ESP_OK; + return _esp_now_modify_peer(mac, chan, ifc, encrypt ? key : nullptr) == ESP_OK; } size_t ESP_NOW_Peer::send(const uint8_t *data, int len) { diff --git a/libraries/ESP_NOW/src/ESP32_NOW.h b/libraries/ESP_NOW/src/ESP32_NOW.h index efba9243aee..5940cfa2221 100644 --- a/libraries/ESP_NOW/src/ESP32_NOW.h +++ b/libraries/ESP_NOW/src/ESP32_NOW.h @@ -20,7 +20,7 @@ class ESP_NOW_Class : public Print { ESP_NOW_Class(); ~ESP_NOW_Class(); - bool begin(const uint8_t *pmk = NULL /* 16 bytes */); + bool begin(const uint8_t *pmk = nullptr /* 16 bytes */); bool end(); int getTotalPeerCount(); @@ -50,7 +50,7 @@ class ESP_NOW_Peer { bool remove(); size_t send(const uint8_t *data, int len); - ESP_NOW_Peer(const uint8_t *mac_addr, uint8_t channel = 0, wifi_interface_t iface = WIFI_IF_AP, const uint8_t *lmk = NULL); + ESP_NOW_Peer(const uint8_t *mac_addr, uint8_t channel = 0, wifi_interface_t iface = WIFI_IF_AP, const uint8_t *lmk = nullptr); public: virtual ~ESP_NOW_Peer() {} diff --git a/libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp b/libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp index 5603da2ba13..edd6e32aacc 100644 --- a/libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp +++ b/libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp @@ -18,11 +18,11 @@ ESP_NOW_Serial_Class::ESP_NOW_Serial_Class(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk, bool remove_on_fail) : ESP_NOW_Peer(mac_addr, channel, iface, lmk) { - tx_ring_buf = NULL; - rx_queue = NULL; - tx_sem = NULL; + tx_ring_buf = nullptr; + rx_queue = nullptr; + tx_sem = nullptr; queued_size = 0; - queued_buff = NULL; + queued_buff = nullptr; resend_count = 0; _remove_on_fail = remove_on_fail; } @@ -34,7 +34,7 @@ ESP_NOW_Serial_Class::~ESP_NOW_Serial_Class() { size_t ESP_NOW_Serial_Class::setTxBufferSize(size_t tx_queue_len) { if (tx_ring_buf) { vRingbufferDelete(tx_ring_buf); - tx_ring_buf = NULL; + tx_ring_buf = nullptr; } if (!tx_queue_len) { return 0; @@ -49,7 +49,7 @@ size_t ESP_NOW_Serial_Class::setTxBufferSize(size_t tx_queue_len) { size_t ESP_NOW_Serial_Class::setRxBufferSize(size_t rx_queue_len) { if (rx_queue) { vQueueDelete(rx_queue); - rx_queue = NULL; + rx_queue = nullptr; } if (!rx_queue_len) { return 0; @@ -65,7 +65,7 @@ bool ESP_NOW_Serial_Class::begin(unsigned long baud) { if (!ESP_NOW.begin() || !add()) { return false; } - if (tx_sem == NULL) { + if (tx_sem == nullptr) { tx_sem = xSemaphoreCreateBinary(); //xSemaphoreTake(tx_sem, 0); xSemaphoreGive(tx_sem); @@ -79,22 +79,22 @@ void ESP_NOW_Serial_Class::end() { remove(); setRxBufferSize(0); setTxBufferSize(0); - if (tx_sem != NULL) { + if (tx_sem != nullptr) { vSemaphoreDelete(tx_sem); - tx_sem = NULL; + tx_sem = nullptr; } } //Stream int ESP_NOW_Serial_Class::available(void) { - if (rx_queue == NULL) { + if (rx_queue == nullptr) { return 0; } return uxQueueMessagesWaiting(rx_queue); } int ESP_NOW_Serial_Class::peek(void) { - if (rx_queue == NULL) { + if (rx_queue == nullptr) { return -1; } uint8_t c; @@ -105,7 +105,7 @@ int ESP_NOW_Serial_Class::peek(void) { } int ESP_NOW_Serial_Class::read(void) { - if (rx_queue == NULL) { + if (rx_queue == nullptr) { return -1; } uint8_t c = 0; @@ -116,7 +116,7 @@ int ESP_NOW_Serial_Class::read(void) { } size_t ESP_NOW_Serial_Class::read(uint8_t *buffer, size_t size) { - if (rx_queue == NULL) { + if (rx_queue == nullptr) { return -1; } uint8_t c = 0; @@ -128,11 +128,11 @@ size_t ESP_NOW_Serial_Class::read(uint8_t *buffer, size_t size) { } void ESP_NOW_Serial_Class::flush() { - if (tx_ring_buf == NULL) { + if (tx_ring_buf == nullptr) { return; } UBaseType_t uxItemsWaiting = 0; - vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); + vRingbufferGetInfo(tx_ring_buf, nullptr, nullptr, nullptr, nullptr, &uxItemsWaiting); if (uxItemsWaiting) { // Now trigger the ISR to read data from the ring buffer. if (xSemaphoreTake(tx_sem, 0) == pdTRUE) { @@ -141,13 +141,13 @@ void ESP_NOW_Serial_Class::flush() { } while (uxItemsWaiting) { delay(5); - vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); + vRingbufferGetInfo(tx_ring_buf, nullptr, nullptr, nullptr, nullptr, &uxItemsWaiting); } } //RX callback void ESP_NOW_Serial_Class::onReceive(const uint8_t *data, size_t len, bool broadcast) { - if (rx_queue == NULL) { + if (rx_queue == nullptr) { return; } for (uint32_t i = 0; i < len; i++) { @@ -165,7 +165,7 @@ void ESP_NOW_Serial_Class::onReceive(const uint8_t *data, size_t len, bool broad //Print int ESP_NOW_Serial_Class::availableForWrite() { //return ESP_NOW_MAX_DATA_LEN; - if (tx_ring_buf == NULL) { + if (tx_ring_buf == nullptr) { return 0; } return xRingbufferGetCurFreeSize(tx_ring_buf); @@ -178,7 +178,7 @@ size_t ESP_NOW_Serial_Class::tryToSend() { //_onSent will not be called anymore //the data is lost in this case vRingbufferReturnItem(tx_ring_buf, queued_buff); - queued_buff = NULL; + queued_buff = nullptr; xSemaphoreGive(tx_sem); end(); } @@ -188,12 +188,12 @@ size_t ESP_NOW_Serial_Class::tryToSend() { bool ESP_NOW_Serial_Class::checkForTxData() { //do we have something that failed the last time? resend_count = 0; - if (queued_buff == NULL) { + if (queued_buff == nullptr) { queued_buff = (uint8_t *)xRingbufferReceiveUpTo(tx_ring_buf, &queued_size, 0, ESP_NOW_MAX_DATA_LEN); } else { log_d(MACSTR " : PREVIOUS", MAC2STR(addr())); } - if (queued_buff != NULL) { + if (queued_buff != nullptr) { return tryToSend() > 0; } //log_d(MACSTR ": EMPTY", MAC2STR(addr())); @@ -203,7 +203,7 @@ bool ESP_NOW_Serial_Class::checkForTxData() { size_t ESP_NOW_Serial_Class::write(const uint8_t *buffer, size_t size, uint32_t timeout) { log_v(MACSTR ", size %u", MAC2STR(addr()), size); - if (tx_sem == NULL || tx_ring_buf == NULL || !added) { + if (tx_sem == nullptr || tx_ring_buf == nullptr || !added) { return 0; } size_t space = availableForWrite(); @@ -249,12 +249,12 @@ size_t ESP_NOW_Serial_Class::write(const uint8_t *buffer, size_t size, uint32_t //TX Done Callback void ESP_NOW_Serial_Class::onSent(bool success) { log_v(MACSTR " : %s", MAC2STR(addr()), success ? "OK" : "FAIL"); - if (tx_sem == NULL || tx_ring_buf == NULL || !added) { + if (tx_sem == nullptr || tx_ring_buf == nullptr || !added) { return; } if (success) { vRingbufferReturnItem(tx_ring_buf, queued_buff); - queued_buff = NULL; + queued_buff = nullptr; //send next packet? //log_d(MACSTR ": NEXT", MAC2STR(addr())); checkForTxData(); @@ -269,7 +269,7 @@ void ESP_NOW_Serial_Class::onSent(bool success) { //resend limit reached //the data is lost in this case vRingbufferReturnItem(tx_ring_buf, queued_buff); - queued_buff = NULL; + queued_buff = nullptr; log_e(MACSTR " : RE-SEND_MAX[%u]", MAC2STR(addr()), resend_count); //if we are not able to send the data and remove_on_fail is set, remove the peer if (_remove_on_fail) { diff --git a/libraries/ESP_NOW/src/ESP32_NOW_Serial.h b/libraries/ESP_NOW/src/ESP32_NOW_Serial.h index 7cc43d85ef8..5ccb7763ec2 100644 --- a/libraries/ESP_NOW/src/ESP32_NOW_Serial.h +++ b/libraries/ESP_NOW/src/ESP32_NOW_Serial.h @@ -28,7 +28,9 @@ class ESP_NOW_Serial_Class : public Stream, public ESP_NOW_Peer { size_t tryToSend(); public: - ESP_NOW_Serial_Class(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface = WIFI_IF_AP, const uint8_t *lmk = NULL, bool remove_on_fail = false); + ESP_NOW_Serial_Class( + const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface = WIFI_IF_AP, const uint8_t *lmk = nullptr, bool remove_on_fail = false + ); ~ESP_NOW_Serial_Class(); size_t setRxBufferSize(size_t); size_t setTxBufferSize(size_t); From 95ae8cf9c63e3045034cdbc7b642d9bd0c5148b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Andr=C3=BDsek?= Date: Mon, 23 Jun 2025 14:02:47 +0200 Subject: [PATCH 34/42] feat(test): Enhance NVS test (#11481) * feat(test): Enhance NVS test * fix(nvs): Remove unused Unity header and improve Serial wait loop * refactor(nvs): Extract string increment logic into a separate function * refactor(test): Format long strings in expect_exact calls for better readability --- tests/validation/nvs/nvs.ino | 132 +++++++++++++++++++++++++++---- tests/validation/nvs/test_nvs.py | 24 ++++-- 2 files changed, 136 insertions(+), 20 deletions(-) diff --git a/tests/validation/nvs/nvs.ino b/tests/validation/nvs/nvs.ino index 20b5b460098..12c640c65b0 100644 --- a/tests/validation/nvs/nvs.ino +++ b/tests/validation/nvs/nvs.ino @@ -1,35 +1,139 @@ +#include #include +struct TestData { + uint8_t id; + uint16_t value; +}; + Preferences preferences; +void validate_types() { + assert(preferences.getType("char") == PT_I8); + assert(preferences.getType("uchar") == PT_U8); + assert(preferences.getType("short") == PT_I16); + assert(preferences.getType("ushort") == PT_U16); + assert(preferences.getType("int") == PT_I32); + assert(preferences.getType("uint") == PT_U32); + assert(preferences.getType("long") == PT_I32); + assert(preferences.getType("ulong") == PT_U32); + assert(preferences.getType("long64") == PT_I64); + assert(preferences.getType("ulong64") == PT_U64); + assert(preferences.getType("float") == PT_BLOB); + assert(preferences.getType("double") == PT_BLOB); + assert(preferences.getType("bool") == PT_U8); + assert(preferences.getType("str") == PT_STR); + assert(preferences.getType("strLen") == PT_STR); + assert(preferences.getType("struct") == PT_BLOB); +} + +// Function to increment string values +void incrementStringValues(String &val_string, char *val_string_buf, size_t buf_size) { + // Extract the number from string and increment it + val_string = "str" + String(val_string.substring(3).toInt() + 1); + + // Extract the number from strLen and increment it + String strLen_str = String(val_string_buf); + int strLen_num = strLen_str.substring(6).toInt(); + snprintf(val_string_buf, buf_size, "strLen%d", strLen_num + 1); +} + void setup() { Serial.begin(115200); - while (!Serial) { ; } preferences.begin("my-app", false); - // Get the counter value, if the key does not exist, return a default value of 0 - unsigned int counter = preferences.getUInt("counter", 0); + // Get the preferences value and if not exists, use default parameter + char val_char = preferences.getChar("char", 'A'); + unsigned char val_uchar = preferences.getUChar("uchar", 0); + int16_t val_short = preferences.getShort("short", 0); + uint16_t val_ushort = preferences.getUShort("ushort", 0); + int32_t val_int = preferences.getInt("int", 0); + uint32_t val_uint = preferences.getUInt("uint", 0); + int64_t val_long = preferences.getLong("long", 0); + uint32_t val_ulong = preferences.getULong("ulong", 0); + int64_t val_long64 = preferences.getLong64("long64", 0); + uint64_t val_ulong64 = preferences.getULong64("ulong64", 0); + float val_float = preferences.getFloat("float", 0.0f); + double val_double = preferences.getDouble("double", 0.0); + bool val_bool = preferences.getBool("bool", false); - // Print the counter to Serial Monitor - Serial.printf("Current counter value: %u\n", counter); + // Strings + String val_string = preferences.getString("str", "str0"); + char val_string_buf[20] = "strLen0"; + preferences.getString("strLen", val_string_buf, sizeof(val_string_buf)); - // Increase counter by 1 - counter++; + // Structure data + TestData test_data = {0, 0}; - // Store the counter to the Preferences - preferences.putUInt("counter", counter); + size_t struct_size = preferences.getBytes("struct", &test_data, sizeof(test_data)); + if (struct_size == 0) { + // First time - set initial values using parameter names + test_data.id = 1; + test_data.value = 100; + } - // Close the Preferences - preferences.end(); + Serial.printf("Values from Preferences: "); + Serial.printf("char: %c | uchar: %u | short: %d | ushort: %u | int: %ld | uint: %lu | ", val_char, val_uchar, val_short, val_ushort, val_int, val_uint); + Serial.printf("long: %lld | ulong: %lu | long64: %lld | ulong64: %llu | ", val_long, val_ulong, val_long64, val_ulong64); + Serial.printf( + "float: %.2f | double: %.2f | bool: %s | str: %s | strLen: %s | struct: {id:%u,val:%u}\n", val_float, val_double, val_bool ? "true" : "false", + val_string.c_str(), val_string_buf, test_data.id, test_data.value + ); - // Wait 1 second - delay(1000); + // Increment the values + val_char += 1; // Increment char A -> B + val_uchar += 1; + val_short += 1; + val_ushort += 1; + val_int += 1; + val_uint += 1; + val_long += 1; + val_ulong += 1; + val_long64 += 1; + val_ulong64 += 1; + val_float += 1.1f; + val_double += 1.1; + val_bool = !val_bool; // Toggle boolean value + + // Increment string values using function + incrementStringValues(val_string, val_string_buf, sizeof(val_string_buf)); + + test_data.id += 1; + test_data.value += 10; + + // Store the updated values back to Preferences + preferences.putChar("char", val_char); + preferences.putUChar("uchar", val_uchar); + preferences.putShort("short", val_short); + preferences.putUShort("ushort", val_ushort); + preferences.putInt("int", val_int); + preferences.putUInt("uint", val_uint); + preferences.putLong("long", val_long); + preferences.putULong("ulong", val_ulong); + preferences.putLong64("long64", val_long64); + preferences.putULong64("ulong64", val_ulong64); + preferences.putFloat("float", val_float); + preferences.putDouble("double", val_double); + preferences.putBool("bool", val_bool); + preferences.putString("str", val_string); + preferences.putString("strLen", val_string_buf); + preferences.putBytes("struct", &test_data, sizeof(test_data)); - // Restart ESP + // Check if the keys exist + assert(preferences.isKey("char")); + assert(preferences.isKey("struct")); + + // Validate the types of the keys + validate_types(); + + // Close the Preferences, wait and restart + preferences.end(); + Serial.flush(); + delay(1000); ESP.restart(); } diff --git a/tests/validation/nvs/test_nvs.py b/tests/validation/nvs/test_nvs.py index 424095a49ba..7f595b8e93d 100644 --- a/tests/validation/nvs/test_nvs.py +++ b/tests/validation/nvs/test_nvs.py @@ -4,11 +4,23 @@ def test_nvs(dut): LOGGER = logging.getLogger(__name__) - LOGGER.info("Expecting counter value 0") - dut.expect_exact("Current counter value: 0") + LOGGER.info("Expecting default values from Preferences") + dut.expect_exact( + "Values from Preferences: char: A | uchar: 0 | short: 0 | ushort: 0 | int: 0 | uint: 0 | long: 0 | ulong: 0 | " + "long64: 0 | ulong64: 0 | float: 0.00 | double: 0.00 | bool: false | str: str0 | strLen: strLen0 | " + "struct: {id:1,val:100}" + ) - LOGGER.info("Expecting counter value 1") - dut.expect_exact("Current counter value: 1") + LOGGER.info("Expecting updated preferences for the first time") + dut.expect_exact( + "Values from Preferences: char: B | uchar: 1 | short: 1 | ushort: 1 | int: 1 | uint: 1 | long: 1 | ulong: 1 | " + "long64: 1 | ulong64: 1 | float: 1.10 | double: 1.10 | bool: true | str: str1 | strLen: strLen1 | " + "struct: {id:2,val:110}" + ) - LOGGER.info("Expecting counter value 2") - dut.expect_exact("Current counter value: 2") + LOGGER.info("Expecting updated preferences for the second time") + dut.expect_exact( + "Values from Preferences: char: C | uchar: 2 | short: 2 | ushort: 2 | int: 2 | uint: 2 | long: 2 | ulong: 2 | " + "long64: 2 | ulong64: 2 | float: 2.20 | double: 2.20 | bool: false | str: str2 | strLen: strLen2 | " + "struct: {id:3,val:120}" + ) From bad975daa5910b42b48f9f1eda6154c45eb18e6f Mon Sep 17 00:00:00 2001 From: Sugar Glider Date: Tue, 24 Jun 2025 03:37:07 -0300 Subject: [PATCH 35/42] fix(uart): removes assert() to avoid reset (#11508) --- cores/esp32/esp32-hal-uart.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 2163e9b5f42..21fe88b57fb 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -305,7 +305,10 @@ static bool _uartTrySetIomuxPin(uart_port_t uart_num, int io_num, uint32_t idx) } // Assign the correct function to the GPIO. - assert(upin->iomux_func != -1); + if (upin->iomux_func == -1) { + log_e("IO#%d has bad IOMUX internal information. Switching to GPIO Matrix UART function.", io_num); + return false; + } if (uart_num < SOC_UART_HP_NUM) { gpio_iomux_out(io_num, upin->iomux_func, false); // If the pin is input, we also have to redirect the signal, in order to bypass the GPIO matrix. From 882ef25a36399e3c32fc16986f00edddd3324d5a Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 23 Jun 2025 12:11:32 +0300 Subject: [PATCH 36/42] fix(p4): Update hosted and wifi_remote components --- idf_component.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/idf_component.yml b/idf_component.yml index 7f090cb26a0..450929a4067 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -105,11 +105,11 @@ dependencies: rules: - if: "target in [esp32s3]" espressif/esp_hosted: - version: "^0.0.25" + version: "^2.0.12" rules: - if: "target == esp32p4" espressif/esp_wifi_remote: - version: "^0.4.1" + version: "^0.13.0" rules: - if: "target == esp32p4" espressif/libsodium: From b7e5169ea17eabedb82171457f08f43c72bd2c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 24 Jun 2025 13:43:05 +0200 Subject: [PATCH 37/42] fix(spi): Update spi bus for esp32s2 (#11510) --- cores/esp32/esp32-hal-spi.c | 32 ++++++++++++-------------------- cores/esp32/esp32-hal-spi.h | 12 +++--------- libraries/SD/src/SD.cpp | 4 +++- libraries/SPI/src/SPI.cpp | 1 + 4 files changed, 19 insertions(+), 30 deletions(-) diff --git a/cores/esp32/esp32-hal-spi.c b/cores/esp32/esp32-hal-spi.c index 6b8e3f8c013..d39aceb5f8d 100644 --- a/cores/esp32/esp32-hal-spi.c +++ b/cores/esp32/esp32-hal-spi.c @@ -79,16 +79,15 @@ struct spi_struct_t { #if CONFIG_IDF_TARGET_ESP32S2 // ESP32S2 -#define SPI_COUNT (3) +#define SPI_COUNT (2) -#define SPI_CLK_IDX(p) ((p == 0) ? SPICLK_OUT_MUX_IDX : ((p == 1) ? FSPICLK_OUT_MUX_IDX : ((p == 2) ? SPI3_CLK_OUT_MUX_IDX : 0))) -#define SPI_MISO_IDX(p) ((p == 0) ? SPIQ_OUT_IDX : ((p == 1) ? FSPIQ_OUT_IDX : ((p == 2) ? SPI3_Q_OUT_IDX : 0))) -#define SPI_MOSI_IDX(p) ((p == 0) ? SPID_IN_IDX : ((p == 1) ? FSPID_IN_IDX : ((p == 2) ? SPI3_D_IN_IDX : 0))) +#define SPI_CLK_IDX(p) ((p == 0) ? FSPICLK_OUT_MUX_IDX : ((p == 1) ? SPI3_CLK_OUT_MUX_IDX : 0)) +#define SPI_MISO_IDX(p) ((p == 0) ? FSPIQ_OUT_IDX : ((p == 1) ? SPI3_Q_OUT_IDX : 0)) +#define SPI_MOSI_IDX(p) ((p == 0) ? FSPID_IN_IDX : ((p == 1) ? SPI3_D_IN_IDX : 0)) -#define SPI_SPI_SS_IDX(n) ((n == 0) ? SPICS0_OUT_IDX : ((n == 1) ? SPICS1_OUT_IDX : 0)) -#define SPI_HSPI_SS_IDX(n) ((n == 0) ? SPI3_CS0_OUT_IDX : ((n == 1) ? SPI3_CS1_OUT_IDX : ((n == 2) ? SPI3_CS2_OUT_IDX : SPI3_CS0_OUT_IDX))) -#define SPI_FSPI_SS_IDX(n) ((n == 0) ? FSPICS0_OUT_IDX : ((n == 1) ? FSPICS1_OUT_IDX : ((n == 2) ? FSPICS2_OUT_IDX : FSPICS0_OUT_IDX))) -#define SPI_SS_IDX(p, n) ((p == 0) ? SPI_SPI_SS_IDX(n) : ((p == 1) ? SPI_SPI_SS_IDX(n) : ((p == 2) ? SPI_HSPI_SS_IDX(n) : 0))) +#define SPI_HSPI_SS_IDX(n) ((n == 0) ? SPI3_CS0_OUT_IDX : ((n == 1) ? SPI3_CS1_OUT_IDX : ((n == 2) ? SPI3_CS2_OUT_IDX : 0))) +#define SPI_FSPI_SS_IDX(n) ((n == 0) ? FSPICS0_OUT_IDX : ((n == 1) ? FSPICS1_OUT_IDX : ((n == 2) ? FSPICS2_OUT_IDX : 0))) +#define SPI_SS_IDX(p, n) ((p == 0) ? SPI_FSPI_SS_IDX(n) : ((p == 1) ? SPI_HSPI_SS_IDX(n) : 0)) #elif CONFIG_IDF_TARGET_ESP32S3 // ESP32S3 @@ -98,8 +97,8 @@ struct spi_struct_t { #define SPI_MISO_IDX(p) ((p == 0) ? FSPIQ_OUT_IDX : ((p == 1) ? SPI3_Q_OUT_IDX : 0)) #define SPI_MOSI_IDX(p) ((p == 0) ? FSPID_IN_IDX : ((p == 1) ? SPI3_D_IN_IDX : 0)) -#define SPI_HSPI_SS_IDX(n) ((n == 0) ? SPI3_CS0_OUT_IDX : ((n == 1) ? SPI3_CS1_OUT_IDX : 0)) -#define SPI_FSPI_SS_IDX(n) ((n == 0) ? FSPICS0_OUT_IDX : ((n == 1) ? FSPICS1_OUT_IDX : 0)) +#define SPI_HSPI_SS_IDX(n) ((n == 0) ? SPI3_CS0_OUT_IDX : ((n == 1) ? SPI3_CS1_OUT_IDX : ((n == 2) ? SPI3_CS2_OUT_IDX : 0))) +#define SPI_FSPI_SS_IDX(n) ((n == 0) ? FSPICS0_OUT_IDX : ((n == 1) ? FSPICS1_OUT_IDX : ((n == 2) ? FSPICS2_OUT_IDX : 0))) #define SPI_SS_IDX(p, n) ((p == 0) ? SPI_FSPI_SS_IDX(n) : ((p == 1) ? SPI_HSPI_SS_IDX(n) : 0)) #elif CONFIG_IDF_TARGET_ESP32P4 @@ -151,11 +150,7 @@ struct spi_struct_t { #define SPI_MUTEX_UNLOCK() // clang-format off static spi_t _spi_bus_array[] = { -#if CONFIG_IDF_TARGET_ESP32S2 - {(volatile spi_dev_t *)(DR_REG_SPI1_BASE), 0, -1, -1, -1, -1, false}, - {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 1, -1, -1, -1, -1, false}, - {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), 2, -1, -1, -1, -1, false} -#elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4 +#if CONFIG_IDF_TARGET_ESP32S2 ||CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4 {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), 1, -1, -1, -1, -1, false} #elif CONFIG_IDF_TARGET_ESP32C2 @@ -179,11 +174,7 @@ static spi_t _spi_bus_array[] = { #define SPI_MUTEX_UNLOCK() xSemaphoreGive(spi->lock) static spi_t _spi_bus_array[] = { -#if CONFIG_IDF_TARGET_ESP32S2 - {(volatile spi_dev_t *)(DR_REG_SPI1_BASE), NULL, 0, -1, -1, -1, -1, false}, - {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 1, -1, -1, -1, -1, false}, - {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), NULL, 2, -1, -1, -1, -1, false} -#elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4 +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4 {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), NULL, 1, -1, -1, -1, -1, false} #elif CONFIG_IDF_TARGET_ESP32C2 {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false} @@ -621,6 +612,7 @@ void spiStopBus(spi_t *spi) { spi_t *spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder) { if (spi_num >= SPI_COUNT) { + log_e("SPI bus index %d is out of range", spi_num); return NULL; } diff --git a/cores/esp32/esp32-hal-spi.h b/cores/esp32/esp32-hal-spi.h index 565ca43f60d..0284fea8829 100644 --- a/cores/esp32/esp32-hal-spi.h +++ b/cores/esp32/esp32-hal-spi.h @@ -27,19 +27,13 @@ extern "C" { #include #define SPI_HAS_TRANSACTION - -#ifdef CONFIG_IDF_TARGET_ESP32S2 -#define FSPI 1 //SPI 1 bus. ESP32S2: for external memory only (can use the same data lines but different SS) -#define HSPI 2 //SPI 2 bus. ESP32S2: external memory or device - it can be matrixed to any pins -#define SPI2 2 // Another name for ESP32S2 SPI 2 -#define SPI3 3 //SPI 3 bus. ESP32S2: device only - it can be matrixed to any pins -#elif CONFIG_IDF_TARGET_ESP32 +#ifdef CONFIG_IDF_TARGET_ESP32 #define FSPI 1 //SPI 1 bus attached to the flash (can use the same data lines but different SS) #define HSPI 2 //SPI 2 bus normally mapped to pins 12 - 15, but can be matrixed to any pins #define VSPI 3 //SPI 3 bus normally attached to pins 5, 18, 19 and 23, but can be matrixed to any pins #else -#define FSPI 0 // ESP32C2, C3, C6, H2, S3, P4 - SPI 2 bus -#define HSPI 1 // ESP32S3, P4 - SPI 3 bus +#define FSPI 0 // ESP32C2, C3, C6, H2, S2, S3, P4 - SPI 2 bus +#define HSPI 1 // ESP32S2, S3, P4 - SPI 3 bus #endif // This defines are not representing the real Divider of the ESP32 diff --git a/libraries/SD/src/SD.cpp b/libraries/SD/src/SD.cpp index eaec2483053..2d646276d87 100644 --- a/libraries/SD/src/SD.cpp +++ b/libraries/SD/src/SD.cpp @@ -27,7 +27,9 @@ bool SDFS::begin(uint8_t ssPin, SPIClass &spi, uint32_t frequency, const char *m return true; } - spi.begin(); + if (!spi.begin()) { + return false; + } _pdrv = sdcard_init(ssPin, &spi, frequency); if (_pdrv == 0xFF) { diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index c0d7665df03..7d8d44320a6 100644 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -74,6 +74,7 @@ bool SPIClass::begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) { _spi = spiStartBus(_spi_num, _div, SPI_MODE0, SPI_MSBFIRST); if (!_spi) { + log_e("SPI bus %d start failed.", _spi_num); return false; } From 30fb3cbb4f2eb749863f39a040601df3decd87a5 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 24 Jun 2025 08:43:27 -0300 Subject: [PATCH 38/42] fix(docs): Fix links and versions (#11505) * fix(docs): Fix links and versions * fix(docs): Apply suggestions and leftover substitutions --- docs/conf_common.py | 7 +++++++ docs/en/contributing.rst | 4 ++-- docs/en/esp-idf_component.rst | 10 ++++++---- docs/en/guides/docs_contributing.rst | 13 +++++-------- docs/en/index.rst | 1 + docs/en/lib_builder.rst | 16 ++++++++++------ docs/requirements.txt | 2 ++ 7 files changed, 33 insertions(+), 20 deletions(-) diff --git a/docs/conf_common.py b/docs/conf_common.py index 676cca899d5..6945c0d190d 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -2,6 +2,12 @@ from esp_docs.conf_docs import * # noqa: F403,F401 +# Used for substituting variables in the documentation +rst_prolog = """ +.. |version| replace:: 3.2.0 +.. |idf_version| replace:: 5.4 +""" + languages = ["en"] # idf_targets = [ @@ -27,6 +33,7 @@ extensions += [ # noqa: F405 "sphinx_copybutton", "sphinx_tabs.tabs", + "sphinx_substitution_extensions", # For allowing substitutions inside code blocks "esp_docs.esp_extensions.dummy_build_system", ] diff --git a/docs/en/contributing.rst b/docs/en/contributing.rst index 4ebe01cbf5b..7a7b99894eb 100644 --- a/docs/en/contributing.rst +++ b/docs/en/contributing.rst @@ -222,7 +222,7 @@ Documentation ------------- If you are contributing to the documentation, please follow the instructions described in the -`documentation guidelines `_ to properly format and test your changes. +`documentation guidelines `_ to properly format and test your changes. Testing and CI -------------- @@ -435,7 +435,7 @@ Documentation Checks ^^^^^^^^^^^^^^^^^^^^ The CI also checks the documentation for any compilation errors. This is important to ensure that the documentation layout is not broken. -To build the documentation locally, please refer to the `documentation guidelines `_. +To build the documentation locally, please refer to the `documentation guidelines `_. Code Style Checks ^^^^^^^^^^^^^^^^^ diff --git a/docs/en/esp-idf_component.rst b/docs/en/esp-idf_component.rst index f38dc44ec0c..13542cc3c87 100644 --- a/docs/en/esp-idf_component.rst +++ b/docs/en/esp-idf_component.rst @@ -14,9 +14,9 @@ For a simplified method, see `Installing using Boards Manager `_. -.. note:: Latest Arduino Core ESP32 version (3.0.X) is now compatible with `ESP-IDF v5.1 `_. Please consider this compatibility when using Arduino as a component in ESP-IDF. +.. note:: Latest Arduino Core ESP32 version (|version|) is now compatible with ESP-IDF v\ |idf_version|\ . Please consider this compatibility when using Arduino as a component in ESP-IDF. -For easiest use of Arduino framework as a ESP-IDF component, you can use the `IDF Component Manager `_ to add the Arduino component to your project. +For easiest use of Arduino framework as a ESP-IDF component, you can use the `IDF Component Manager `_ to add the Arduino component to your project. This will automatically clone the repository and its submodules. You can find the Arduino component in the `ESP Registry `_ together with dependencies list and examples. Installation @@ -32,14 +32,16 @@ Installing using IDF Component Manager To add the Arduino component to your project using the IDF Component Manager, run the following command in your project directory: .. code-block:: bash + :substitutions: - idf.py add-dependency "espressif/arduino-esp32^3.0.2" + idf.py add-dependency "espressif/arduino-esp32^|version|" Or you can start a new project from a template with the Arduino component: .. code-block:: bash + :substitutions: - idf.py create-project-from-example "espressif/arduino-esp32^3.0.2:hello_world" + idf.py create-project-from-example "espressif/arduino-esp32^|version|:hello_world" Manual installation of Arduino framework **************************************** diff --git a/docs/en/guides/docs_contributing.rst b/docs/en/guides/docs_contributing.rst index 20dc4c84cab..13f08c47618 100644 --- a/docs/en/guides/docs_contributing.rst +++ b/docs/en/guides/docs_contributing.rst @@ -49,11 +49,11 @@ Before starting your collaboration, you need to get the documentation source cod Requirements ************ -To properly work with the documentation, you need to install some packages in your system. +To build the documentation properly, you need to install some packages in your system. Note that depending on +your system, you may need to use a virtual environment to install the packages. .. code-block:: - pip install -U Sphinx pip install -r requirements.txt The requirements file is under the ``docs`` folder. @@ -62,17 +62,14 @@ Using Visual Studio Code ************************ If you are using the Visual Studio Code, you can install some extensions to help you while writing documentation. +For reStructuredText, you can install the `reStructuredText Pack `_ extension. -`reStructuredText Pack `_ - -We also recommend you install to grammar check extension to help you to review English grammar. - -`Grammarly `_ +We also recommend you to install some grammar check extension to help you to review English grammar. Building ******** -To build the documentation and generate the HTML files, you can use the following command inside the ``docs`` folder. After a successful build, you can check the files inside the `_build/en/generic/html` folder. +To build the documentation and generate the HTML files, you can use the following command inside the ``docs`` folder. After a successful build, you can check the files inside the ``_build/en/generic/html`` folder. .. code-block:: diff --git a/docs/en/index.rst b/docs/en/index.rst index 1314a8fc78d..89fc7e3bd6e 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -3,6 +3,7 @@ Welcome to ESP32 Arduino Core's documentation ############################################# Here you will find all the relevant information about the project. +This documentation is valid for the Arduino Core for ESP32 version |version| based on ESP-IDF |idf_version|. .. note:: This is a work in progress documentation and we will appreciate your help! We are looking for contributors! diff --git a/docs/en/lib_builder.rst b/docs/en/lib_builder.rst index e7edb331fd3..a8126c18edc 100644 --- a/docs/en/lib_builder.rst +++ b/docs/en/lib_builder.rst @@ -293,8 +293,9 @@ You have two options to run the Docker image to build the libraries. Manually or To run the Docker image manually, use the following command from the root of the ``arduino-esp32`` repository: .. code-block:: bash + :substitutions: - docker run --rm -it -v $PWD:/arduino-esp32 -e TERM=xterm-256color espressif/esp32-arduino-lib-builder:release-v5.1 + docker run --rm -it -v $PWD:/arduino-esp32 -e TERM=xterm-256color espressif/esp32-arduino-lib-builder:release-v|idf_version| This will start the Lib Builder UI for compiling the libraries. The above command explained: @@ -304,7 +305,7 @@ This will start the Lib Builder UI for compiling the libraries. The above comman - ``-t`` Allocate a pseudo-TTY; - ``-e TERM=xterm-256color``: Optional. Sets the terminal type to ``xterm-256color`` to display colors correctly; - ``-v $PWD:/arduino-esp32``: Optional. Mounts the current folder at ``/arduino-esp32`` inside the container. If not provided, the container will not copy the compiled libraries to the host machine; -- ``espressif/esp32-arduino-lib-builder:release-v5.1``: uses Docker image ``espressif/esp32-arduino-lib-builder`` with tag ``release-v5.1``. +- :substitution-code:`espressif/esp32-arduino-lib-builder:release-v|idf_version|`: uses Docker image ``espressif/esp32-arduino-lib-builder`` with tag :substitution-code:`release-v|idf_version|`. The ``latest`` tag is implicitly added by Docker when no tag is specified. It is recommended to use a specific version tag to ensure reproducibility of the build process. .. warning:: @@ -324,24 +325,27 @@ By default the docker container will run the user interface script. If you want For example, to run a terminal inside the container, you can run: .. code-block:: bash + :substitutions: - docker run -it espressif/esp32-arduino-lib-builder:release-v5.1 /bin/bash + docker run -it espressif/esp32-arduino-lib-builder:release-v|idf_version| /bin/bash Running the Docker image using the provided run script will depend on the host OS. Use the following command from the root of the ``arduino-esp32`` repository to execute the image in a Linux or macOS environment for -the ``release-v5.1`` tag: +the :substitution-code:`release-v|idf_version|` tag: .. code-block:: bash + :substitutions: - curl -LJO https://raw.githubusercontent.com/espressif/esp32-arduino-lib-builder/refs/heads/release/v5.1/tools/docker/run.sh + curl -LJO https://raw.githubusercontent.com/espressif/esp32-arduino-lib-builder/refs/heads/release/v|idf_version|/tools/docker/run.sh chmod +x run.sh ./run.sh $PWD For Windows, use the following command in PowerShell from the root of the ``arduino-esp32`` repository: .. code-block:: powershell + :substitutions: - Invoke-WebRequest -Uri "https://raw.githubusercontent.com/espressif/esp32-arduino-lib-builder/refs/heads/release/v5.1/tools/docker/run.ps1" -OutFile "run.ps1" + Invoke-WebRequest -Uri "https://raw.githubusercontent.com/espressif/esp32-arduino-lib-builder/refs/heads/release/v|idf_version|/tools/docker/run.ps1" -OutFile "run.ps1" .\run.ps1 $pwd As the script is unsigned, you may need to change the execution policy of the current session before running the script. diff --git a/docs/requirements.txt b/docs/requirements.txt index d3017fb5adc..ef2ab88cb65 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,7 @@ +sphinx==4.5.0 esp-docs>=1.4.0 sphinx-copybutton==0.5.0 sphinx-tabs==3.2.0 numpydoc==1.5.0 standard-imghdr==3.13.0 +Sphinx-Substitution-Extensions==2022.2.16 From e9b0930f9dd1fb2e1df28c7156fa7892e4306b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 24 Jun 2025 14:34:17 +0200 Subject: [PATCH 39/42] ci(sizes): Update sizes workflow action --- .github/workflows/publishsizes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publishsizes.yml b/.github/workflows/publishsizes.yml index 8ff591e052b..f3c401d635c 100644 --- a/.github/workflows/publishsizes.yml +++ b/.github/workflows/publishsizes.yml @@ -66,7 +66,7 @@ jobs: path: ./artifacts/sizes-report/pr_num.txt - name: Report results - uses: P-R-O-C-H-Y/report-size-deltas@2043188c68f483a7b50527c4eacf609d05bb67a5 # sizes_v2 + uses: P-R-O-C-H-Y/report-size-deltas@sizes_v2 #2043188c68f483a7b50527c4eacf609d05bb67a5 # sizes_v2 with: sketches-reports-source: ${{ env.SKETCHES_REPORTS_PATH }} github-token: ${{ env.GITHUB_TOKEN }} From 0213200b3d66cae7849623dc6551503346228de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 24 Jun 2025 14:40:53 +0200 Subject: [PATCH 40/42] ci(sizes): Use commit id in report-size-delta action --- .github/workflows/publishsizes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publishsizes.yml b/.github/workflows/publishsizes.yml index f3c401d635c..fad2418668c 100644 --- a/.github/workflows/publishsizes.yml +++ b/.github/workflows/publishsizes.yml @@ -66,7 +66,7 @@ jobs: path: ./artifacts/sizes-report/pr_num.txt - name: Report results - uses: P-R-O-C-H-Y/report-size-deltas@sizes_v2 #2043188c68f483a7b50527c4eacf609d05bb67a5 # sizes_v2 + uses: P-R-O-C-H-Y/report-size-deltas@bea91d2c99ca80c88a883b39b1c4012f00ec3d09 # sizes_v2 with: sketches-reports-source: ${{ env.SKETCHES_REPORTS_PATH }} github-token: ${{ env.GITHUB_TOKEN }} From 9e61fa7e4bce59c05cb17c15b11b53b9bafca077 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 24 Jun 2025 17:23:50 +0300 Subject: [PATCH 41/42] IDF release/v5.4 (#11512) * IDF release/v5.4 f0f2980d * fix(p4): Allow custom pins on P4 for ESP-Hosted --- libraries/WiFi/src/WiFiGeneric.cpp | 12 +++- package/package_esp32_index.template.json | 68 +++++++++++------------ 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp index 3faf34fef34..bddcb5fd448 100644 --- a/libraries/WiFi/src/WiFiGeneric.cpp +++ b/libraries/WiFi/src/WiFiGeneric.cpp @@ -252,13 +252,23 @@ static bool wifiHostedInit() { if (!hosted_initialized) { hosted_initialized = true; struct esp_hosted_sdio_config conf = INIT_DEFAULT_HOST_SDIO_CONFIG(); +#ifdef BOARD_HAS_SDIO_ESP_HOSTED + conf.pin_clk.pin = BOARD_SDIO_ESP_HOSTED_CLK; + conf.pin_cmd.pin = BOARD_SDIO_ESP_HOSTED_CMD; + conf.pin_d0.pin = BOARD_SDIO_ESP_HOSTED_D0; + conf.pin_d1.pin = BOARD_SDIO_ESP_HOSTED_D1; + conf.pin_d2.pin = BOARD_SDIO_ESP_HOSTED_D2; + conf.pin_d3.pin = BOARD_SDIO_ESP_HOSTED_D3; + conf.pin_reset.pin = BOARD_SDIO_ESP_HOSTED_RESET; +#else conf.pin_clk.pin = CONFIG_ESP_SDIO_PIN_CLK; conf.pin_cmd.pin = CONFIG_ESP_SDIO_PIN_CMD; conf.pin_d0.pin = CONFIG_ESP_SDIO_PIN_D0; conf.pin_d1.pin = CONFIG_ESP_SDIO_PIN_D1; conf.pin_d2.pin = CONFIG_ESP_SDIO_PIN_D2; conf.pin_d3.pin = CONFIG_ESP_SDIO_PIN_D3; - //conf.pin_rst.pin = CONFIG_ESP_SDIO_GPIO_RESET_SLAVE; + conf.pin_reset.pin = CONFIG_ESP_SDIO_GPIO_RESET_SLAVE; +#endif // esp_hosted_sdio_set_config() will fail on second attempt but here temporarily to not cause exception on reinit if (esp_hosted_sdio_set_config(&conf) != ESP_OK || esp_hosted_init() != ESP_OK) { log_e("esp_hosted_init failed!"); diff --git a/package/package_esp32_index.template.json b/package/package_esp32_index.template.json index 22d3cc05455..9c1516dbacd 100644 --- a/package/package_esp32_index.template.json +++ b/package/package_esp32_index.template.json @@ -51,7 +51,7 @@ { "packager": "esp32", "name": "esp32-arduino-libs", - "version": "idf-release_v5.4-aed8bdc8-v1" + "version": "idf-release_v5.4-f0f2980d-v1" }, { "packager": "esp32", @@ -104,63 +104,63 @@ "tools": [ { "name": "esp32-arduino-libs", - "version": "idf-release_v5.4-aed8bdc8-v1", + "version": "idf-release_v5.4-f0f2980d-v1", "systems": [ { "host": "i686-mingw32", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "checksum": "SHA-256:448691c3171f79b2136e4ab8006e9c78bd1627156dab1365fff8f8867a6a7e5b", - "size": "353758763" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "checksum": "SHA-256:b1a77c83634111d78a356f1d1756dc815eeeb91605c5d8714a0db7cccbd0bede", + "size": "353978504" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "checksum": "SHA-256:448691c3171f79b2136e4ab8006e9c78bd1627156dab1365fff8f8867a6a7e5b", - "size": "353758763" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "checksum": "SHA-256:b1a77c83634111d78a356f1d1756dc815eeeb91605c5d8714a0db7cccbd0bede", + "size": "353978504" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "checksum": "SHA-256:448691c3171f79b2136e4ab8006e9c78bd1627156dab1365fff8f8867a6a7e5b", - "size": "353758763" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "checksum": "SHA-256:b1a77c83634111d78a356f1d1756dc815eeeb91605c5d8714a0db7cccbd0bede", + "size": "353978504" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "checksum": "SHA-256:448691c3171f79b2136e4ab8006e9c78bd1627156dab1365fff8f8867a6a7e5b", - "size": "353758763" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "checksum": "SHA-256:b1a77c83634111d78a356f1d1756dc815eeeb91605c5d8714a0db7cccbd0bede", + "size": "353978504" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "checksum": "SHA-256:448691c3171f79b2136e4ab8006e9c78bd1627156dab1365fff8f8867a6a7e5b", - "size": "353758763" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "checksum": "SHA-256:b1a77c83634111d78a356f1d1756dc815eeeb91605c5d8714a0db7cccbd0bede", + "size": "353978504" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "checksum": "SHA-256:448691c3171f79b2136e4ab8006e9c78bd1627156dab1365fff8f8867a6a7e5b", - "size": "353758763" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "checksum": "SHA-256:b1a77c83634111d78a356f1d1756dc815eeeb91605c5d8714a0db7cccbd0bede", + "size": "353978504" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "checksum": "SHA-256:448691c3171f79b2136e4ab8006e9c78bd1627156dab1365fff8f8867a6a7e5b", - "size": "353758763" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "checksum": "SHA-256:b1a77c83634111d78a356f1d1756dc815eeeb91605c5d8714a0db7cccbd0bede", + "size": "353978504" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-aed8bdc8-v1.zip", - "checksum": "SHA-256:448691c3171f79b2136e4ab8006e9c78bd1627156dab1365fff8f8867a6a7e5b", - "size": "353758763" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.4-f0f2980d-v1.zip", + "checksum": "SHA-256:b1a77c83634111d78a356f1d1756dc815eeeb91605c5d8714a0db7cccbd0bede", + "size": "353978504" } ] }, From 21640ac82a1bb5efa8cf0b3841be1ac80add6785 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Thu, 26 Jun 2025 12:43:50 +0300 Subject: [PATCH 42/42] fix(webserver): Validate header inputs --- libraries/WebServer/src/WebServer.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/WebServer/src/WebServer.cpp b/libraries/WebServer/src/WebServer.cpp index 652a86f587f..7523e40259b 100644 --- a/libraries/WebServer/src/WebServer.cpp +++ b/libraries/WebServer/src/WebServer.cpp @@ -502,6 +502,16 @@ void WebServer::stop() { } void WebServer::sendHeader(const String &name, const String &value, bool first) { + if (name.indexOf('\r') != -1 || name.indexOf('\n') != -1) { + log_e("Invalid character in HTTP header name"); + return; + } + + if (value.indexOf('\r') != -1 || value.indexOf('\n') != -1) { + log_e("Invalid character in HTTP header value"); + return; + } + RequestArgument *header = new RequestArgument(); header->key = name; header->value = value; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy