diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bd18f7ad973..eff3343620c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -54,6 +54,7 @@ /libraries/ArduinoOTA/ @me-no-dev /libraries/AsyncUDP/ @me-no-dev /libraries/BLE/ @lucasssvaz @SuGlider +/libraries/ESP_HostedOTA/ @me-no-dev /libraries/ESP_I2S/ @me-no-dev /libraries/ESP_NOW/ @P-R-O-C-H-Y @lucasssvaz /libraries/ESP_SR/ @me-no-dev diff --git a/CMakeLists.txt b/CMakeLists.txt index d9b295dfa70..4732354aa73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,7 @@ set(ARDUINO_ALL_LIBRARIES ESP_I2S ESP_NOW ESP_SR + ESP_HostedOTA ESPmDNS Ethernet FFat @@ -146,6 +147,9 @@ set(ARDUINO_LIBRARY_ESP_SR_SRCS libraries/ESP_SR/src/ESP_SR.cpp libraries/ESP_SR/src/esp32-hal-sr.c) +set(ARDUINO_LIBRARY_ESP_HostedOTA_SRCS + libraries/ESP_HostedOTA/src/ESP_HostedOTA.cpp) + set(ARDUINO_LIBRARY_ESPmDNS_SRCS libraries/ESPmDNS/src/ESPmDNS.cpp) set(ARDUINO_LIBRARY_Ethernet_SRCS libraries/Ethernet/src/ETH.cpp) diff --git a/cores/esp32/esp32-hal-hosted.c b/cores/esp32/esp32-hal-hosted.c index 4fba105457c..b5ab854dbc8 100644 --- a/cores/esp32/esp32-hal-hosted.c +++ b/cores/esp32/esp32-hal-hosted.c @@ -17,6 +17,7 @@ #include "esp32-hal-hosted.h" #include "esp32-hal-log.h" +#include "esp32-hal.h" #include "pins_arduino.h" #include "esp_hosted.h" @@ -53,6 +54,9 @@ static esp_hosted_coprocessor_fwver_t host_version_struct = { .major1 = ESP_HOSTED_VERSION_MAJOR_1, .minor1 = ESP_HOSTED_VERSION_MINOR_1, .patch1 = ESP_HOSTED_VERSION_PATCH_1 }; +static bool hostedInit(); +static bool hostedDeinit(); + void hostedGetHostVersion(uint32_t *major, uint32_t *minor, uint32_t *patch) { *major = host_version_struct.major1; *minor = host_version_struct.minor1; @@ -66,6 +70,11 @@ void hostedGetSlaveVersion(uint32_t *major, uint32_t *minor, uint32_t *patch) { } bool hostedHasUpdate() { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + uint32_t host_version = ESP_HOSTED_VERSION_VAL(host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1); uint32_t slave_version = 0; @@ -106,6 +115,11 @@ char *hostedGetUpdateURL() { } bool hostedBeginUpdate() { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + esp_err_t err = esp_hosted_slave_ota_begin(); if (err != ESP_OK) { log_e("Failed to begin Update: %s", esp_err_to_name(err)); @@ -114,6 +128,11 @@ bool hostedBeginUpdate() { } bool hostedWriteUpdate(uint8_t *buf, uint32_t len) { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + esp_err_t err = esp_hosted_slave_ota_write(buf, len); if (err != ESP_OK) { log_e("Failed to write Update: %s", esp_err_to_name(err)); @@ -122,6 +141,11 @@ bool hostedWriteUpdate(uint8_t *buf, uint32_t len) { } bool hostedEndUpdate() { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + esp_err_t err = esp_hosted_slave_ota_end(); if (err != ESP_OK) { log_e("Failed to end Update: %s", esp_err_to_name(err)); @@ -130,16 +154,31 @@ bool hostedEndUpdate() { } bool hostedActivateUpdate() { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + + // Activate can fail on older firmwares and that is not critical + uint32_t slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1); + uint32_t min_version = ESP_HOSTED_VERSION_VAL(2, 6, 0); + + if (slave_version < min_version) { + // Silence messages caused by earlier versions + esp_log_level_set("rpc_core", ESP_LOG_NONE); + } + esp_err_t err = esp_hosted_slave_ota_activate(); - if (err != ESP_OK) { + + // Any further communication will result in logged errors + esp_log_level_set("sdmmc_io", ESP_LOG_NONE); + esp_log_level_set("H_SDIO_DRV", ESP_LOG_NONE); + + if (err != ESP_OK && slave_version >= min_version) { log_e("Failed to activate Update: %s", esp_err_to_name(err)); + return false; } - // else { - // hostedDeinit(); - // delay(1000); - // hostedInit(); - // } - return err == ESP_OK; + return true; } static bool hostedInit() { @@ -158,15 +197,22 @@ static bool hostedInit() { conf.pin_d2.pin = sdio_pin_config.pin_d2; conf.pin_d3.pin = sdio_pin_config.pin_d3; conf.pin_reset.pin = sdio_pin_config.pin_reset; - // 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!"); + esp_err_t err = esp_hosted_sdio_set_config(&conf); + if (err != ESP_OK) { //&& err != ESP_ERR_NOT_ALLOWED) { // uncomment when second init is fixed + log_e("esp_hosted_sdio_set_config failed: %s", esp_err_to_name(err)); + return false; + } + err = esp_hosted_init(); + if (err != ESP_OK) { + log_e("esp_hosted_init failed: %s", esp_err_to_name(err)); hosted_initialized = false; return false; } log_i("ESP-Hosted initialized!"); - if (esp_hosted_connect_to_slave() != ESP_OK) { - log_e("Failed to connect to slave"); + err = esp_hosted_connect_to_slave(); + if (err != ESP_OK) { + log_e("esp_hosted_connect_to_slave failed: %s", esp_err_to_name(err)); + hosted_initialized = false; return false; } hostedHasUpdate(); diff --git a/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ESP_HostedOTA.ino b/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ESP_HostedOTA.ino new file mode 100644 index 00000000000..01c40d5cebd --- /dev/null +++ b/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ESP_HostedOTA.ino @@ -0,0 +1,66 @@ +/* + * ESP-Hosted OTA Update Example + * + * This example demonstrates how to update the ESP-Hosted co-processor firmware + * over-the-air (OTA). The ESP-Hosted solution allows an ESP32 to act as a WiFi + * co-processor for other microcontrollers. + * + * Prerequisites: + * - ESP32 with ESP-Hosted firmware configured as WiFi co-processor + * - Network connectivity to download firmware updates + * - Valid WiFi credentials + */ + +#include "WiFi.h" // WiFi library for network connectivity +#include "ESP_HostedOTA.h" // ESP-Hosted OTA update functionality + +// WiFi network credentials - CHANGE THESE TO YOUR NETWORK SETTINGS +const char *ssid = "your-ssid"; // Replace with your WiFi network name +const char *password = "your-password"; // Replace with your WiFi password + +void setup() { + // Step 1: Initialize serial communication for debugging output + Serial.begin(115200); + + // Step 2: Initialize the ESP-Hosted WiFi station mode + // This prepares the ESP-Hosted co-processor for WiFi operations + WiFi.STA.begin(); + + // Step 3: Display connection attempt information + Serial.println(); + Serial.println("******************************************************"); + Serial.print("Connecting to "); + Serial.println(ssid); + + // Step 4: Attempt to connect to the specified WiFi network + WiFi.STA.connect(ssid, password); + + // Step 5: Wait for WiFi connection to be established + // Display progress dots while connecting + while (WiFi.STA.status() != WL_CONNECTED) { + delay(500); // Wait 500ms between connection attempts + Serial.print("."); // Show connection progress + } + Serial.println(); + + // Step 6: Display successful connection information + Serial.println("WiFi connected"); + Serial.print("IP address: "); + Serial.println(WiFi.STA.localIP()); + + // Step 7: Attempt to update the ESP-Hosted co-processor firmware + // This function will: + // - Check if ESP-Hosted is initialized + // - Verify if an update is available + // - Download and install the firmware update if needed + if (updateEspHostedSlave()) { + // Step 8: Restart the host ESP32 after successful update + // This is currently required to properly activate the new firmware + // on the ESP-Hosted co-processor + ESP.restart(); + } +} + +void loop() { + delay(1000); +} diff --git a/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ci.yml b/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ci.yml new file mode 100644 index 00000000000..8548a039255 --- /dev/null +++ b/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/ESP_HostedOTA/keywords.txt b/libraries/ESP_HostedOTA/keywords.txt new file mode 100644 index 00000000000..f34de27a296 --- /dev/null +++ b/libraries/ESP_HostedOTA/keywords.txt @@ -0,0 +1,17 @@ +####################################### +# Syntax Coloring Map For ESP_HostedOTA +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +updateEspHostedSlave KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/libraries/ESP_HostedOTA/library.properties b/libraries/ESP_HostedOTA/library.properties new file mode 100644 index 00000000000..a45ab7aa138 --- /dev/null +++ b/libraries/ESP_HostedOTA/library.properties @@ -0,0 +1,9 @@ +name=ESP_HostedOTA +version=3.3.4 +author=me-no-dev +maintainer=me-no-dev +sentence=Library for updating the ESP-Hosted co-processor +paragraph=Supports ESP32 Arduino platforms. +category=Communication +url=https://github.com/espressif/arduino-esp32/ +architectures=esp32 diff --git a/libraries/ESP_HostedOTA/src/ESP_HostedOTA.cpp b/libraries/ESP_HostedOTA/src/ESP_HostedOTA.cpp new file mode 100644 index 00000000000..33b2f957419 --- /dev/null +++ b/libraries/ESP_HostedOTA/src/ESP_HostedOTA.cpp @@ -0,0 +1,159 @@ +// Include necessary headers for SDK configuration, Arduino framework, and networking +#include "sdkconfig.h" +#include +#if CONFIG_ESP_WIFI_REMOTE_ENABLED +#include "Arduino.h" +#include "esp32-hal-hosted.h" // ESP-Hosted specific functions +#include "Network.h" // Network connectivity management +#include "HTTPClient.h" // HTTP client for downloading updates +#include "NetworkClientSecure.h" // Secure network client for HTTPS +#endif + +/** + * Updates the ESP-Hosted co-processor firmware over-the-air (OTA) + * This function downloads and installs firmware updates for the ESP-Hosted slave device + * @return true if update was successful, false otherwise + */ +bool updateEspHostedSlave() { +#if CONFIG_ESP_WIFI_REMOTE_ENABLED + bool updateSuccess = false; + + // Step 1: Verify ESP-Hosted is properly initialized + if (!hostedIsInitialized()) { + Serial.println("ERROR: esp-hosted is not initialized. Did you call WiFi.STA.begin()?"); + return updateSuccess; + } + + // Step 2: Check if an update is actually available + if (!hostedHasUpdate()) { + // esp-hosted is already the latest version - no update needed + return updateSuccess; + } + + // Step 3: Ensure network connectivity is available + if (!Network.isOnline()) { + Serial.println("ERROR: Network is not online! Did you call WiFi.STA.connect(ssid, password)?"); + return updateSuccess; + } + + // Step 4: Begin the update process - display update URL + Serial.print("Updating esp-hosted co-processor from "); + Serial.println(hostedGetUpdateURL()); + + // Step 5: Create a secure network client for HTTPS communication + NetworkClientSecure *client = new NetworkClientSecure(); + if (!client) { + Serial.println("ERROR: Could not allocate client!"); + return updateSuccess; + } + + // Step 6: Configure client to skip certificate verification (insecure mode) + client->setInsecure(); + + // Step 7: Initialize HTTP client and attempt to connect to update server + HTTPClient https; + int httpCode = 0; + if (!https.begin(*client, hostedGetUpdateURL())) { + Serial.println("ERROR: HTTP begin failed!"); + goto finish_ota; + } + + // Step 8: Send HTTP GET request to download the firmware + httpCode = https.GET(); + if (httpCode == HTTP_CODE_OK) { + // Step 9: Get the size of the firmware file to download + int len = https.getSize(); + if (len < 0) { + Serial.println("ERROR: Update size not received!"); + https.end(); + goto finish_ota; + } + + // Step 10: Get stream pointer for reading firmware data + NetworkClient *stream = https.getStreamPtr(); + + // Step 11: Initialize the ESP-Hosted update process + if (!hostedBeginUpdate()) { + Serial.println("ERROR: esp-hosted update start failed!"); + https.end(); + goto finish_ota; + } + +// Step 12: Allocate buffer for firmware data transfer (2KB chunks) +#define HOSTED_OTA_BUF_SIZE 2048 + uint8_t *buff = (uint8_t *)malloc(HOSTED_OTA_BUF_SIZE); + if (!buff) { + Serial.println("ERROR: Could not allocate OTA buffer!"); + https.end(); + goto finish_ota; + } + + // Step 13: Download and write firmware data in chunks + while (https.connected() && len > 0) { + size_t size = stream->available(); + if (size > 0) { + // Show progress indicator + Serial.print("."); + + // Limit chunk size to buffer capacity + if (size > HOSTED_OTA_BUF_SIZE) { + size = HOSTED_OTA_BUF_SIZE; + } + + // Prevent reading more data than expected + if (size > len) { + Serial.printf("\nERROR: Update received extra bytes: %u!", size - len); + break; + } + + // Read firmware data chunk into buffer + int readLen = stream->readBytes(buff, size); + len -= readLen; + + // Write the chunk to ESP-Hosted co-processor + if (!hostedWriteUpdate(buff, readLen)) { + Serial.println("\nERROR: esp-hosted update write failed!"); + break; + } + + // Step 14: Check if entire firmware has been downloaded + if (len == 0) { + // Finalize the update process + if (!hostedEndUpdate()) { + Serial.println("\nERROR: esp-hosted update end failed!"); + break; + } + + // Activate the new firmware + if (!hostedActivateUpdate()) { + Serial.println("\nERROR: esp-hosted update activate failed!"); + break; + } + + // Update completed successfully + updateSuccess = true; + Serial.println("\nSUCCESS: esp-hosted co-processor updated!"); + break; + } + } + // Small delay to prevent overwhelming the system + delay(1); + } + + // Step 15: Clean up allocated buffer + free(buff); + Serial.println(); + } + + // Step 16: Close HTTP connection + https.end(); + +finish_ota: + // Step 17: Clean up network client + delete client; + return updateSuccess; +#else + // ESP-Hosted functionality is not enabled in SDK configuration + return false; +#endif +} diff --git a/libraries/ESP_HostedOTA/src/ESP_HostedOTA.h b/libraries/ESP_HostedOTA/src/ESP_HostedOTA.h new file mode 100644 index 00000000000..56c35ec2b07 --- /dev/null +++ b/libraries/ESP_HostedOTA/src/ESP_HostedOTA.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +/** + * Updates the ESP-Hosted co-processor firmware over-the-air (OTA) + * This function downloads and installs firmware updates for the ESP-Hosted slave device + * @return true if update was successful, false otherwise + */ +bool updateEspHostedSlave();