/* * cmd_ota.c * OTA firmware update commands (HTTPS + cert bundle) * Compiled as empty when CONFIG_ESPILON_OTA_ENABLED is not set. */ #include "sdkconfig.h" #ifdef CONFIG_ESPILON_OTA_ENABLED #include #include #include "esp_log.h" #include "esp_system.h" #include "esp_ota_ops.h" #include "esp_https_ota.h" #include "esp_http_client.h" #include "esp_crt_bundle.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "utils.h" #define TAG "OTA" /* ============================================================ * COMMAND: ota_update (async) * ============================================================ */ static esp_err_t cmd_ota_update( int argc, char **argv, const char *req, void *ctx ) { (void)ctx; const char *url = argv[0]; char buf[256]; snprintf(buf, sizeof(buf), "url=%s", url); msg_info(TAG, buf, req); esp_http_client_config_t http_config = { .url = url, #ifdef CONFIG_ESPILON_OTA_ALLOW_HTTP .skip_cert_common_name_check = true, #else .crt_bundle_attach = esp_crt_bundle_attach, #endif .timeout_ms = 30000, .keep_alive_enable = true, }; esp_https_ota_config_t ota_config = { .http_config = &http_config, }; esp_https_ota_handle_t ota_handle = NULL; esp_err_t err = esp_https_ota_begin(&ota_config, &ota_handle); if (err != ESP_OK) { snprintf(buf, sizeof(buf), "begin_failed=%s", esp_err_to_name(err)); msg_error(TAG, buf, req); return err; } int total_size = esp_https_ota_get_image_size(ota_handle); int last_pct = -1; while (1) { err = esp_https_ota_perform(ota_handle); if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) break; if (total_size > 0) { int bytes_read = esp_https_ota_get_image_len_read(ota_handle); int pct = (bytes_read * 100) / total_size; if (pct / 10 != last_pct / 10) { last_pct = pct; snprintf(buf, sizeof(buf), "progress=%d%%", pct); msg_info(TAG, buf, req); } } } if (err != ESP_OK) { snprintf(buf, sizeof(buf), "download_failed=%s", esp_err_to_name(err)); msg_error(TAG, buf, req); esp_https_ota_abort(ota_handle); return err; } err = esp_https_ota_finish(ota_handle); if (err != ESP_OK) { if (err == ESP_ERR_OTA_VALIDATE_FAILED) { msg_error(TAG, "validate_failed=image_corrupted", req); } else { snprintf(buf, sizeof(buf), "finish_failed=%s", esp_err_to_name(err)); msg_error(TAG, buf, req); } return err; } msg_info(TAG, "status=success rebooting=true", req); vTaskDelay(pdMS_TO_TICKS(500)); esp_restart(); return ESP_OK; } /* ============================================================ * COMMAND: ota_status * ============================================================ */ static esp_err_t cmd_ota_status( int argc, char **argv, const char *req, void *ctx ) { (void)argc; (void)argv; (void)ctx; const esp_partition_t *running = esp_ota_get_running_partition(); const esp_partition_t *boot = esp_ota_get_boot_partition(); esp_app_desc_t app_desc; esp_ota_get_partition_description(running, &app_desc); char buf[256]; snprintf(buf, sizeof(buf), "partition=%s boot=%s version=%s idf=%s", running ? running->label : "?", boot ? boot->label : "?", app_desc.version, app_desc.idf_ver ); msg_info(TAG, buf, req); return ESP_OK; } /* ============================================================ * COMMAND REGISTRATION * ============================================================ */ static const command_t ota_cmds[] = { { "ota_update", NULL, "OTA update from HTTPS URL", 1, 1, cmd_ota_update, NULL, true }, { "ota_status", NULL, "Current firmware info", 0, 0, cmd_ota_status, NULL, false }, }; void mod_ota_register_commands(void) { ESPILON_LOGI_PURPLE(TAG, "Registering OTA commands"); for (size_t i = 0; i < sizeof(ota_cmds) / sizeof(ota_cmds[0]); i++) { command_register(&ota_cmds[i]); } } #endif /* CONFIG_ESPILON_OTA_ENABLED */