feat: implement OTA updates over HTTP(S), initiated over MQTT
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "src/ota_manager.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp_https_ota app_update mqtt_publisher esp_system
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* Begin an OTA update in a background task.
|
||||
* Applies a MAC-derived jitter delay before downloading so a fleet-wide
|
||||
* trigger staggers reboots instead of dropping all coverage at once.
|
||||
* Reports status via mqtt_publisher_send_ota_status().
|
||||
* On success the device reboots; on failure it stays on the current firmware.
|
||||
*/
|
||||
esp_err_t ota_manager_start(const char *url, const char *version);
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "ota_manager.h"
|
||||
#include "mqtt_publisher.h"
|
||||
#include "esp_https_ota.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define TAG "ota_manager"
|
||||
|
||||
/* Prevent concurrent OTA attempts */
|
||||
static volatile bool s_ota_running = false;
|
||||
|
||||
typedef struct {
|
||||
char url[256];
|
||||
char version[32];
|
||||
} ota_task_arg_t;
|
||||
|
||||
static void ota_task(void *arg)
|
||||
{
|
||||
ota_task_arg_t *params = (ota_task_arg_t *)arg;
|
||||
|
||||
/* Spread reboots across up to 60 s using last 3 MAC bytes as seed */
|
||||
uint8_t mac[6];
|
||||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||
uint32_t jitter_ms = ((uint32_t)(mac[3] ^ mac[4] ^ mac[5])) % 60000;
|
||||
if (jitter_ms > 0) {
|
||||
ESP_LOGI(TAG, "OTA jitter: %lu ms", jitter_ms);
|
||||
mqtt_publisher_send_ota_status("pending", params->version);
|
||||
vTaskDelay(pdMS_TO_TICKS(jitter_ms));
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Starting OTA from %s", params->url);
|
||||
mqtt_publisher_send_ota_status("downloading", params->version);
|
||||
|
||||
esp_http_client_config_t http_cfg = {
|
||||
.url = params->url,
|
||||
.timeout_ms = 30000,
|
||||
.keep_alive_enable = true,
|
||||
};
|
||||
esp_https_ota_config_t ota_cfg = {
|
||||
.http_config = &http_cfg,
|
||||
};
|
||||
|
||||
esp_err_t err = esp_https_ota(&ota_cfg);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "OTA succeeded, rebooting");
|
||||
mqtt_publisher_send_ota_status("rebooting", params->version);
|
||||
vTaskDelay(pdMS_TO_TICKS(500)); /* let MQTT publish flush */
|
||||
esp_restart();
|
||||
} else {
|
||||
ESP_LOGE(TAG, "OTA failed: %s", esp_err_to_name(err));
|
||||
mqtt_publisher_send_ota_status("failed", params->version);
|
||||
}
|
||||
|
||||
free(params);
|
||||
s_ota_running = false;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
esp_err_t ota_manager_start(const char *url, const char *version)
|
||||
{
|
||||
if (s_ota_running) {
|
||||
ESP_LOGW(TAG, "OTA already in progress, ignoring");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
ota_task_arg_t *params = malloc(sizeof(ota_task_arg_t));
|
||||
if (!params) return ESP_ERR_NO_MEM;
|
||||
|
||||
strncpy(params->url, url, sizeof(params->url) - 1);
|
||||
strncpy(params->version, version ? version : "", sizeof(params->version) - 1);
|
||||
|
||||
s_ota_running = true;
|
||||
if (xTaskCreate(ota_task, "ota_task", 8192, params, 5, NULL) != pdPASS) {
|
||||
free(params);
|
||||
s_ota_running = false;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user