ota: add ota update capabilities

This commit is contained in:
2026-03-12 10:59:35 +01:00
parent b3485fcb10
commit ee3ec3ac4f
10 changed files with 1118 additions and 3 deletions

350
main/ota_handler.c Normal file
View File

@@ -0,0 +1,350 @@
#include "ota_handler.h"
#include "config.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_https_ota.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include <string.h>
static const char *TAG = "OTA";
// OTA state
static bool ota_initialized = false;
static bool ota_updating = false;
static int ota_progress = 0;
static SemaphoreHandle_t ota_mutex = NULL;
static TaskHandle_t ota_task_handle = NULL;
static esp_ota_handle_t ota_upload_handle = 0;
static const esp_partition_t *ota_update_partition = NULL;
static size_t ota_bytes_written = 0;
// OTA configuration
#define OTA_TIMEOUT_MS 30000
#define OTA_BUFFER_SIZE 4096
// ============================================================================
// OTA Update Task
// ============================================================================
typedef struct {
char url[256];
} ota_update_params_t;
static void ota_update_task(void *arg) {
ota_update_params_t *params = (ota_update_params_t *)arg;
if (!params) {
ESP_LOGE(TAG, "Invalid OTA parameters");
ota_updating = false;
vTaskDelete(NULL);
return;
}
ESP_LOGI(TAG, "Starting OTA update from: %s", params->url);
esp_http_client_config_t http_config = {
.url = params->url,
.timeout_ms = OTA_TIMEOUT_MS,
.buffer_size = OTA_BUFFER_SIZE,
.skip_cert_common_name_check = true,
};
esp_https_ota_config_t ota_config = {
.http_config = &http_config,
};
esp_https_ota_handle_t ota_handle = NULL;
esp_err_t ret = esp_https_ota_begin(&ota_config, &ota_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "OTA begin failed: %s", esp_err_to_name(ret));
free(params);
ota_updating = false;
vTaskDelete(NULL);
return;
}
int last_progress = -1;
while (1) {
ret = esp_https_ota_perform(ota_handle);
if (ret == ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
// Update progress
int downloaded = esp_https_ota_get_image_len_read(ota_handle);
int total = esp_https_ota_get_image_size(ota_handle);
if (total > 0) {
ota_progress = (downloaded * 100) / total;
if (ota_progress != last_progress) {
ESP_LOGI(TAG, "OTA progress: %d%% (%d/%d bytes)", ota_progress, downloaded, total);
last_progress = ota_progress;
}
}
} else if (ret == ESP_OK) {
// OTA complete
ESP_LOGI(TAG, "OTA download complete");
break;
} else {
ESP_LOGE(TAG, "OTA perform failed: %s", esp_err_to_name(ret));
esp_https_ota_abort(ota_handle);
free(params);
ota_updating = false;
ota_progress = 0;
vTaskDelete(NULL);
return;
}
}
ret = esp_https_ota_finish(ota_handle);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "OTA update successful, restarting...");
free(params);
ota_progress = 100;
vTaskDelay(pdMS_TO_TICKS(1000));
esp_restart();
} else {
ESP_LOGE(TAG, "OTA finish failed: %s", esp_err_to_name(ret));
free(params);
ota_updating = false;
ota_progress = 0;
}
vTaskDelete(NULL);
}
// ============================================================================
// Public Functions
// ============================================================================
esp_err_t ota_handler_init(void) {
if (ota_initialized) {
ESP_LOGW(TAG, "OTA handler already initialized");
return ESP_OK;
}
ESP_LOGI(TAG, "Initializing OTA handler");
ota_mutex = xSemaphoreCreateMutex();
if (!ota_mutex) {
ESP_LOGE(TAG, "Failed to create OTA mutex");
return ESP_ERR_NO_MEM;
}
ota_initialized = true;
ota_updating = false;
ota_progress = 0;
// Print current partition info
const esp_partition_t *running = esp_ota_get_running_partition();
if (running) {
ESP_LOGI(TAG, "Running from partition: %s (offset: 0x%lx)", running->label, running->address);
}
return ESP_OK;
}
esp_err_t ota_handler_deinit(void) {
if (!ota_initialized) {
return ESP_OK;
}
ESP_LOGI(TAG, "Deinitializing OTA handler");
if (ota_mutex) {
vSemaphoreDelete(ota_mutex);
ota_mutex = NULL;
}
ota_initialized = false;
return ESP_OK;
}
const char *ota_get_running_partition(void) {
const esp_partition_t *running = esp_ota_get_running_partition();
if (running) {
return running->label;
}
return "unknown";
}
bool ota_is_updating(void) {
return ota_updating;
}
int ota_get_progress(void) {
if (ota_updating) {
return ota_progress;
}
return -1;
}
// ============================================================================
// OTA Start Function (called from web server)
// ============================================================================
esp_err_t ota_start_update(const char *url) {
if (!ota_initialized) {
ESP_LOGE(TAG, "OTA handler not initialized");
return ESP_ERR_INVALID_STATE;
}
if (ota_updating) {
ESP_LOGW(TAG, "OTA update already in progress");
return ESP_ERR_INVALID_STATE;
}
if (!url || strlen(url) == 0) {
ESP_LOGE(TAG, "Invalid URL");
return ESP_ERR_INVALID_ARG;
}
// Allocate parameters
ota_update_params_t *params = calloc(1, sizeof(ota_update_params_t));
if (!params) {
ESP_LOGE(TAG, "Failed to allocate OTA params");
return ESP_ERR_NO_MEM;
}
strncpy(params->url, url, sizeof(params->url) - 1);
params->url[sizeof(params->url) - 1] = '\0';
ota_updating = true;
ota_progress = 0;
// Create OTA task
BaseType_t ret = xTaskCreate(ota_update_task, "ota_update", 8192, params, 5, &ota_task_handle);
if (ret != pdPASS) {
ESP_LOGE(TAG, "Failed to create OTA task");
free(params);
ota_updating = false;
ota_progress = 0;
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
// ============================================================================
// OTA Upload Functions (for direct file upload)
// ============================================================================
esp_err_t ota_start_upload(void) {
if (!ota_initialized) {
ESP_LOGE(TAG, "OTA handler not initialized");
return ESP_ERR_INVALID_STATE;
}
if (ota_updating) {
ESP_LOGW(TAG, "OTA update already in progress");
return ESP_ERR_INVALID_STATE;
}
// Get next update partition
ota_update_partition = esp_ota_get_next_update_partition(NULL);
if (!ota_update_partition) {
ESP_LOGE(TAG, "No OTA partition found");
return ESP_ERR_NOT_FOUND;
}
ESP_LOGI(TAG, "Starting OTA upload to partition: %s", ota_update_partition->label);
// Begin OTA
esp_err_t ret = esp_ota_begin(ota_update_partition, OTA_SIZE_UNKNOWN, &ota_upload_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin failed: %s", esp_err_to_name(ret));
ota_update_partition = NULL;
return ret;
}
ota_updating = true;
ota_progress = 0;
ota_bytes_written = 0;
return ESP_OK;
}
esp_err_t ota_write_data(const uint8_t *data, size_t len) {
if (!ota_updating || !ota_update_partition) {
ESP_LOGE(TAG, "OTA upload not started");
return ESP_ERR_INVALID_STATE;
}
esp_err_t ret = esp_ota_write(ota_upload_handle, data, len);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_write failed: %s", esp_err_to_name(ret));
return ret;
}
ota_bytes_written += len;
// Update progress (we don't know total size, so show bytes written)
ESP_LOGD(TAG, "Written %zu bytes", ota_bytes_written);
return ESP_OK;
}
esp_err_t ota_finish_upload(void) {
if (!ota_updating || !ota_update_partition) {
ESP_LOGE(TAG, "OTA upload not started");
return ESP_ERR_INVALID_STATE;
}
ESP_LOGI(TAG, "Finishing OTA upload, total bytes: %zu", ota_bytes_written);
esp_err_t ret = esp_ota_end(ota_upload_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed: %s", esp_err_to_name(ret));
ota_updating = false;
ota_upload_handle = 0;
ota_update_partition = NULL;
ota_bytes_written = 0;
return ret;
}
// Set boot partition
ret = esp_ota_set_boot_partition(ota_update_partition);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed: %s", esp_err_to_name(ret));
ota_updating = false;
ota_upload_handle = 0;
ota_update_partition = NULL;
ota_bytes_written = 0;
return ret;
}
ESP_LOGI(TAG, "OTA upload complete, restarting...");
ota_progress = 100;
ota_updating = false;
ota_upload_handle = 0;
ota_update_partition = NULL;
ota_bytes_written = 0;
// Restart after short delay
vTaskDelay(pdMS_TO_TICKS(1000));
esp_restart();
return ESP_OK;
}
esp_err_t ota_abort_upload(void) {
if (!ota_updating) {
return ESP_OK;
}
ESP_LOGI(TAG, "Aborting OTA upload");
if (ota_upload_handle) {
esp_ota_abort(ota_upload_handle);
ota_upload_handle = 0;
}
ota_updating = false;
ota_progress = 0;
ota_update_partition = NULL;
ota_bytes_written = 0;
return ESP_OK;
}