From 32d9404b34dd0ff5bf048e7711076c284cd463a0 Mon Sep 17 00:00:00 2001 From: Michael Ehrenreich Date: Wed, 18 Mar 2026 09:38:24 +0100 Subject: [PATCH] OTA stuff --- main/ota.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++ main/ota.h | 4 ++ 2 files changed, 212 insertions(+) create mode 100644 main/ota.c create mode 100644 main/ota.h diff --git a/main/ota.c b/main/ota.c new file mode 100644 index 0000000..12790d2 --- /dev/null +++ b/main/ota.c @@ -0,0 +1,208 @@ +#include "sdkconfig.h" + +#include + +#include "esp_console.h" +#include "esp_err.h" +#include "esp_event.h" +#include "esp_http_client.h" +#include "esp_https_ota.h" +#include "esp_log.h" +#include "esp_ota_ops.h" +#include "esp_system.h" +#include "argtable3/argtable3.h" + +#define OTA_TASK_STACK_SIZE 2048 +#define OTA_TASK_PRIORITY 20 + +static const char TAG[] = "OTA"; + +static struct { + struct arg_str *url; + struct arg_lit *reboot; + struct arg_lit *confirm; + struct arg_lit *rollback; + struct arg_lit *switch_part; + struct arg_end *end; +} ota_args; + +static bool ota_task_running; +static TaskHandle_t ota_task_handle; + +static char https_url[1024]; +static bool reboot_after_update; + +esp_http_client_config_t http_client_config = { + .url = https_url, +}; + +esp_https_ota_config_t ota_config = { + .http_config = &http_client_config, + .bulk_flash_erase = true, + .buffer_caps = MALLOC_CAP_SPIRAM +}; + +static void ota_task(void *) +{ + esp_err_t res = esp_https_ota(&ota_config); + if (res == ESP_OK) + { + if (reboot_after_update) + esp_restart(); + } + else + { + ESP_LOGE(TAG, "esp_https_ota failed: %s", esp_err_to_name(res)); + } + + ota_task_running = false; + + vTaskDelete(NULL); +} + +static void ota_event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + if (event_base == ESP_HTTPS_OTA_EVENT) { + switch (event_id) { + case ESP_HTTPS_OTA_START: + ESP_LOGI(TAG, "OTA started"); + break; + case ESP_HTTPS_OTA_CONNECTED: + ESP_LOGI(TAG, "Connected to server"); + break; + case ESP_HTTPS_OTA_GET_IMG_DESC: + ESP_LOGI(TAG, "Reading Image Description"); + break; + case ESP_HTTPS_OTA_VERIFY_CHIP_ID: + ESP_LOGI(TAG, "Verifying chip id of new image: %d", *(esp_chip_id_t *)event_data); + break; + case ESP_HTTPS_OTA_VERIFY_CHIP_REVISION: + ESP_LOGI(TAG, "Verifying chip revision of new image: %d", *(uint16_t *)event_data); + break; + case ESP_HTTPS_OTA_DECRYPT_CB: + ESP_LOGI(TAG, "Callback to decrypt function"); + break; + case ESP_HTTPS_OTA_WRITE_FLASH: + ESP_LOGD(TAG, "Writing to flash: %d written", *(int *)event_data); + break; + case ESP_HTTPS_OTA_UPDATE_BOOT_PARTITION: + ESP_LOGI(TAG, "Boot partition updated. Next Partition: %d", *(esp_partition_subtype_t *)event_data); + break; + case ESP_HTTPS_OTA_FINISH: + ESP_LOGI(TAG, "OTA finish"); + break; + case ESP_HTTPS_OTA_ABORT: + ESP_LOGI(TAG, "OTA abort"); + break; + } + } +} + +static int ota_cmd(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **)&ota_args); + if (nerrors != 0) { + arg_print_errors(stderr, ota_args.end, argv[0]); + return 1; + } + + if (ota_args.url->count + ota_args.confirm->count + ota_args.rollback->count + ota_args.switch_part->count != 1) + { + ESP_LOGE(TAG, "Please specify exactly one operation to perform"); + return 1; + } + + if (ota_args.reboot->count && !ota_args.url->count) + { + ESP_LOGE(TAG, "reboot is only valid when performing update"); + return 1; + } + + if (ota_args.confirm->count) + { + int res = esp_ota_mark_app_valid_cancel_rollback(); + if (res != ESP_OK) + { + ESP_LOGE(TAG, "esp_ota_mark_app_valid_cancel_rollback failed: %s", esp_err_to_name(res)); + return 1; + } + } + + if (ota_args.rollback->count) + { + esp_err_t res = esp_ota_mark_app_invalid_rollback_and_reboot(); + if (res != ESP_OK) + { + ESP_LOGE(TAG, "esp_ota_mark_app_invalid_rollback_and_reboot failed: %s", esp_err_to_name(res)); + return 1; + } + } + + if (ota_args.switch_part->count) + { + const esp_partition_t* other_app = esp_ota_get_next_update_partition(NULL); + if (other_app == NULL) + { + ESP_LOGE(TAG, "Could not get non-running partition"); + return 1; + } + + esp_err_t res = esp_ota_set_boot_partition(other_app); + if (res != ESP_OK) + { + ESP_LOGE(TAG, "esp_ota_set_boot_partition failed: %s", esp_err_to_name(res)); + return 1; + } + } + + if (ota_args.url->count) + { + if (strlen(ota_args.url->sval[0]) >= sizeof(https_url)) + { + ESP_LOGE(TAG, "URL too long"); + return 1; + } + + strcpy(https_url, ota_args.url->sval[0]); + reboot_after_update = ota_args.reboot->count; + + const esp_partition_t* other_app = esp_ota_get_next_update_partition(NULL); + if (other_app == NULL) + { + ESP_LOGE(TAG, "Could not get non-running partition"); + return 1; + } + + ota_config.partition.staging = other_app; + + xTaskCreate(ota_task, "ota", OTA_TASK_STACK_SIZE, + NULL, CONFIG_SNIFFER_TASK_PRIORITY, &ota_task_handle); + } + + return 0; +} + + +void register_ota_cmd(void) +{ + ota_args.url = arg_str0("u", NULL, "", "update over HTTPS URL"); + ota_args.reboot = arg_lit0("r", NULL, "reboot into new app after update"); + ota_args.confirm = arg_lit0("C", NULL, "confirm app partition after update"); + ota_args.rollback = arg_lit0("R", NULL, "rollback to old app and reboot after update"); + ota_args.switch_part = arg_lit0("S", NULL, "switch to other app partition and reboot"); + ota_args.end = arg_end(1); + const esp_console_cmd_t cmd = { + .command = "ota", + .help = "perform OTA operation", + .hint = NULL, + .func = &ota_cmd, + .argtable = &ota_args + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); +} + +void ota_init(void) +{ + ESP_ERROR_CHECK(esp_event_handler_register(ESP_HTTPS_OTA_EVENT, ESP_EVENT_ANY_ID, ota_event_handler, NULL)); +} diff --git a/main/ota.h b/main/ota.h new file mode 100644 index 0000000..4603ba3 --- /dev/null +++ b/main/ota.h @@ -0,0 +1,4 @@ +#pragma once + +void register_ota_cmd(void); +void ota_init(void);