Files
ESP32_dass2/docs/ota_callbacks.md
T
2026-06-12 17:03:10 +08:00

12 KiB
Raw Blame History

OTA Manager 回调文档

版本: 1.0
目标芯片: ESP32-S3
框架: ESP-IDF 5.1.x
模块: ota_manager.h / ota_manager.c


1. 架构概览

┌──────────────────────────────────────────────────────────┐
│  传输层(USB / 串口 / 蓝牙 / SD / HTTP                  │
│    ↓                                                      │
│  ota_begin(size) → ota_write(chunk)×N → ota_end()        │
│    │                    │                   │              │
│    ▼                    ▼                   ▼              │
│  ┌─────────────────────────────────────────────────┐     │
│  │            OTA Manager(本模块)                  │     │
│  │  · 状态机管理(IDLE → PROGRESS → VERIFY → DONE)│     │
│  │  · esp_ota_ops 封装(分区选择 / 擦除 / 写入)    │     │
│  │  · 回调触发(进度 / 状态 / 完成 / 错误)         │     │
│  │  · LED 反馈                                      │     │
│  └──────────────┬──────────────────────────────────┘     │
│                 │ 回调(可注册 4 种)                      │
│     ┌───────────┼───────────┬──────────────┐              │
│     ▼           ▼           ▼              ▼              │
│  progress   state      complete        error             │
│  (进度条)   (状态灯)   (重启逻辑)    (告警/日志)         │
└──────────────────────────────────────────────────────────┘

设计原则OTA Manager 只负责底层固件写入与分区切换,不关心固件数据从哪里来。传输层代码只需顺序调用三个 API,并通过回调感知进度。


2. 状态机

                    ota_begin()
  IDLE ──────────────────────────→ IN_PROGRESS
    ↑                                  │
    │                                  │ ota_write()
    │                                  ▼ (循环)
    │                              IN_PROGRESS
    │                                  │
    │                    ┌─────────────┼──────────────┐
    │                    │ ota_end()   │ (写入失败)    │
    │                    ▼             ▼               │
    │                VERIFYING      ERROR             │
    │                    │             │               │
    │        ┌───────────┤             │ ota_abort()   │
    │        │ 成功      │ 失败        ▼               │
    │        ▼           ▼           IDLE              │
    │     COMPLETE      ERROR                          │
    │        │                                           │
    │        │ esp_restart()                             │
    │        ▼                                           │
    │      (重启)                                        │
    └────────────────────────────────────────────────────┘

状态枚举 (ota_state_t)

状态 含义 允许的操作
OTA_STATE_IDLE 空闲,就绪 ota_begin()
OTA_STATE_IN_PROGRESS 正在接收固件 ota_write(), ota_end(), ota_abort()
OTA_STATE_VERIFYING 正在校验固件 无(内部过程)
OTA_STATE_COMPLETE 更新成功 重启设备
OTA_STATE_ERROR 发生错误 ota_abort()

3. 回调详解

3.1 进度回调 — ota_progress_cb_t

typedef void (*ota_progress_cb_t)(int percent, size_t bytes_written, size_t total_size);

触发时机:每次 ota_write() 成功返回后。

参数

参数 类型 说明
percent int 完成百分比 (0100)。若 ota_begin() 传入 total_size=0,则始终为 0
bytes_written size_t 累计已写入字节数
total_size size_t 声明的固件总字节数(即 ota_begin 的参数,0=未知)

用途:驱动进度条 UI、串口日志输出。

示例

static void on_progress(int percent, size_t written, size_t total)
{
    printf("\rOTA: %d%% (%zu/%zu bytes)", percent, written, total);
    // 或通过 LVGL 更新进度条:
    // lv_bar_set_value(bar, percent, LV_ANIM_OFF);
}

3.2 状态变化回调 — ota_state_cb_t

typedef void (*ota_state_cb_t)(ota_state_t old_state, ota_state_t new_state);

触发时机:状态机每次发生转换时。

参数

参数 类型 说明
old_state ota_state_t 变化前的状态
new_state ota_state_t 变化后的状态

典型用法

场景 检查条件 操作
更新成功 new_state == OTA_STATE_COMPLETE 自动重启 esp_restart()
更新失败 new_state == OTA_STATE_ERROR 显示错误提示、记录日志
开始更新 new_state == OTA_STATE_IN_PROGRESS 禁用用户交互、显示进度 UI
恢复空闲 new_state == OTA_STATE_IDLE 恢复用户交互

示例

static void on_state_change(ota_state_t old, ota_state_t new_state)
{
    ESP_LOGI("app", "OTA state: %d → %d", old, new_state);

    if (new_state == OTA_STATE_COMPLETE) {
        ESP_LOGI("app", "Update OK, restarting in 3s...");
        vTaskDelay(pdMS_TO_TICKS(3000));
        esp_restart();
    } else if (new_state == OTA_STATE_ERROR) {
        ESP_LOGE("app", "OTA failed, back to idle");
    }
}

3.3 完成回调 — ota_complete_cb_t

typedef void (*ota_complete_cb_t)(bool success, ota_error_t error_code);

触发时机

  • ota_end() 处理完毕后
  • ota_abort() 处理完毕后

参数

参数 类型 说明
success bool true = 更新成功,false = 失败或被中止
error_code ota_error_t 失败时的错误码(success=true 时始终为 OTA_ERR_NONE

与状态变化回调的区别

  • ota_state_cb_t 关注「状态发生了什么变化」
  • ota_complete_cb_t 关注「这次 OTA 最终成功了还是失败了」+ 失败原因

两者可同时使用,但建议至少注册一个来处理重启逻辑。

示例

static void on_complete(bool success, ota_error_t err)
{
    if (success) {
        ESP_LOGI("app", "OTA complete, rebooting...");
        esp_restart();
    } else {
        ESP_LOGW("app", "OTA failed, err=%d", err);
        // 返回主界面
    }
}

3.4 错误回调 — ota_error_cb_t

typedef void (*ota_error_cb_t)(ota_error_t error_code, const char *message);

触发时机:每当发生可恢复或不致命的错误时(写入失败、校验失败等)。

参数

参数 类型 说明
error_code ota_error_t 错误码枚举值
message const char * 人类可读的错误描述(静态字符串,无需释放)

错误码一览

错误码 含义
OTA_ERR_NONE 0 无错误
OTA_ERR_NOT_INITIALIZED 1 未调用 ota_begin()
OTA_ERR_ALREADY_IN_PROGRESS 2 已有进行中的 OTA
OTA_ERR_BEGIN_FAILED 3 ota_begin() 失败(分区擦除失败等)
OTA_ERR_WRITE_FAILED 4 写入 flash 失败
OTA_ERR_END_FAILED 5 ota_end() 失败
OTA_ERR_ABORT_FAILED 6 ota_abort() 失败
OTA_ERR_SIZE_MISMATCH 7 实写大小与声明不符
OTA_ERR_NO_OTA_PARTITION 8 分区表无可用 OTA 分区
OTA_ERR_STATE_ERROR 9 当前状态不允许该操作
OTA_ERR_VERIFY_FAILED 10 固件校验失败

4. 完整注册示例

#include "ota_manager.h"
#include "esp_system.h"

/* ---- 回调实现 ---- */

static void on_progress(int pct, size_t written, size_t total)
{
    printf("\rOTA %d%% (%zu/%zu)", pct, written, total);
}

static void on_state_change(ota_state_t old, ota_state_t new_state)
{
    if (new_state == OTA_STATE_COMPLETE) {
        printf("\nUpdate OK. Rebooting...\n");
        esp_restart();
    }
}

static void on_complete(bool ok, ota_error_t err)
{
    if (!ok) printf("\nOTA failed: %d\n", err);
}

static void on_error(ota_error_t code, const char *msg)
{
    ESP_LOGE("app", "OTA error %d: %s", code, msg);
}

/* ---- 注册(在 app_main 初始化阶段调用一次) ---- */

void ota_callbacks_init(void)
{
    ota_set_progress_callback(on_progress);
    ota_set_state_callback(on_state_change);
    ota_set_complete_callback(on_complete);
    ota_set_error_callback(on_error);
}

5. 与 LVGL UI 集成要点

5.1 FreeRTOS 线程安全

LVGL 对象操作必须在 UI Task 中进行。OTA 的调用方通常运行在另一个 Task 中,回调在调用线程中触发。因此:

// ❌ 错误:在进度回调中直接操作 LVGL
static void on_progress(int pct, ...) {
    lv_bar_set_value(ui_bar, pct, LV_ANIM_OFF);  // 非 UI 线程!
}

// ✅ 正确:通过全局变量 + LVGL Timer 轮询
static volatile int g_ota_pct = 0;

static void on_progress(int pct, ...) {
    g_ota_pct = pct;
}

// 在 LVGL Timer10Hz)中:
static void lv_timer_update_ota(lv_timer_t *timer) {
    lv_bar_set_value(ui_bar, g_ota_pct, LV_ANIM_ON);
}

5.2 建议的 UI 状态映射

OTA 状态 UI 行为
IDLE 显示「固件升级」入口
IN_PROGRESS 显示进度条 + 「正在更新…」
VERIFYING 进度条 100%,文字改为「正在校验…」
COMPLETE 「更新成功,3 秒后重启」+ 倒计时
ERROR 红色警告 + 错误描述 + «重试»按钮

6. LED 反馈模式

OTA Manager 内置了 LED 反馈(通过 hw_led_set()),无需额外配置。

阶段 LED 行为 含义
begin 调用后 常亮 OTA 进行中
每次 write 短暂熄灭→亮起 数据写入闪烁(约 2ms
end 成功 常亮 更新完成,等待重启
end 失败 / abort 熄灭 回到空闲

7. 错误处理建议

ota_begin() 返回非 ESP_OK
  ├─ OTA_ERR_ALREADY_IN_PROGRESS → 先调 ota_abort(),再重试 begin
  ├─ OTA_ERR_NO_OTA_PARTITION  → 检查分区表是否正确烧录
  └─ OTA_ERR_BEGIN_FAILED      → 检查 flash 是否正常

ota_write() 返回非 ESP_OK
  └─ 调 ota_abort(),提示用户重试

ota_end() 返回非 ESP_OK
  └─ 调 ota_abort(),固件未切换,设备仍运行旧版本

8. 平台兼容性

特性 要求
ESP-IDF 版本 ≥ 5.0(本模块使用 v5.1.x API
分区表 必须含 factory + 至少一个 ota_0 类型分区
Flash 大小 ≥ 2MB(推荐 8MB,双 OTA 槽位)
hw_led_set() 需由项目提供(当前在 hw_init.h 中定义)

9. API 快速参考

函数 返回值 副作用
ota_begin(size) esp_err_t 擦除 OTA 分区,LED 亮
ota_write(data, len) esp_err_t 写入 flash,触发进度回调,LED 闪烁
ota_end() esp_err_t 校验固件,切换启动分区,触发完成回调
ota_abort() esp_err_t 放弃更新,LED 灭,回到 IDLE
ota_get_state() ota_state_t
ota_get_bytes_written() size_t
ota_get_firmware_size() size_t
ota_get_running_partition() const esp_partition_t *
ota_get_update_partition() const esp_partition_t *
ota_set_progress_callback(cb) void 覆盖之前的回调
ota_set_complete_callback(cb) void 覆盖之前的回调
ota_set_state_callback(cb) void 覆盖之前的回调
ota_set_error_callback(cb) void 覆盖之前的回调
ota_clear_callbacks() void 注销所有回调