210 lines
7.2 KiB
C
210 lines
7.2 KiB
C
/**
|
||
* @file ota_manager.h
|
||
* @brief 固件 OTA(Over-The-Air)底层管理器
|
||
*
|
||
* 提供与传输层无关的固件更新基础设施。
|
||
* 上层(USB / 串口 / 蓝牙 / SD 卡 / HTTP)只需:
|
||
* 1. 调用 ota_begin(total_size) 开始更新
|
||
* 2. 循环调用 ota_write(chunk, len) 写入固件数据
|
||
* 3. 调用 ota_end() 完成更新(验证 + 切换启动分区)
|
||
* 4. 异常时调用 ota_abort() 放弃更新
|
||
*
|
||
* 回调机制:
|
||
* 注册回调后,在 OTA 各阶段自动触发,用于 UI 更新 / 日志 / 错误处理。
|
||
*
|
||
* 框架: ESP-IDF 5.1.x
|
||
* 依赖: hw_led_set()(状态 LED 反馈)
|
||
*/
|
||
|
||
#ifndef OTA_MANAGER_H
|
||
#define OTA_MANAGER_H
|
||
|
||
#include <stdint.h>
|
||
#include <stdbool.h>
|
||
#include <stddef.h>
|
||
#include "esp_err.h"
|
||
#include "esp_partition.h"
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* ================================================================
|
||
* 类型定义
|
||
* ================================================================ */
|
||
|
||
/** OTA 状态机 */
|
||
typedef enum {
|
||
OTA_STATE_IDLE, /**< 空闲,等待开始 */
|
||
OTA_STATE_IN_PROGRESS, /**< 固件写入中 */
|
||
OTA_STATE_VERIFYING, /**< 固件校验中(end 阶段) */
|
||
OTA_STATE_COMPLETE, /**< 更新成功,准备重启 */
|
||
OTA_STATE_ERROR, /**< 发生错误,需 abort 或重试 */
|
||
} ota_state_t;
|
||
|
||
/** OTA 错误码(扩展 esp_err_t,负值为 ESP-IDF 错误,正值为自定义错误) */
|
||
typedef enum {
|
||
OTA_ERR_NONE = 0, /**< 无错误 */
|
||
OTA_ERR_NOT_INITIALIZED = 1, /**< 未调用 begin */
|
||
OTA_ERR_ALREADY_IN_PROGRESS = 2, /**< 已有进行中的 OTA */
|
||
OTA_ERR_BEGIN_FAILED = 3, /**< begin 阶段失败 */
|
||
OTA_ERR_WRITE_FAILED = 4, /**< 写入 flash 失败 */
|
||
OTA_ERR_END_FAILED = 5, /**< end 阶段失败 */
|
||
OTA_ERR_ABORT_FAILED = 6, /**< abort 失败 */
|
||
OTA_ERR_SIZE_MISMATCH = 7, /**< 写入长度与声明的 total 不符 */
|
||
OTA_ERR_NO_OTA_PARTITION = 8, /**< 无可用 OTA 分区 */
|
||
OTA_ERR_STATE_ERROR = 9, /**< 当前状态不允许该操作 */
|
||
OTA_ERR_VERIFY_FAILED = 10, /**< 固件校验失败 */
|
||
} ota_error_t;
|
||
|
||
/* ================================================================
|
||
* 回调函数类型
|
||
* ================================================================ */
|
||
|
||
/**
|
||
* @brief 进度回调
|
||
* @param percent 完成百分比 (0–100)
|
||
* @param bytes_written 已写入字节数
|
||
* @param total_size 固件总字节数
|
||
*
|
||
* 调用频率: 每完成一个 ota_write() 调用后触发一次(约 1–4KB 一次)。
|
||
* 上层可在此回调中更新进度条或打印日志。
|
||
*/
|
||
typedef void (*ota_progress_cb_t)(int percent, size_t bytes_written, size_t total_size);
|
||
|
||
/**
|
||
* @brief 完成回调
|
||
* @param success true=更新成功, false=更新失败
|
||
* @param error_code 失败时的错误码(success 为 true 时无效)
|
||
*
|
||
* 调用时机: ota_end() 处理完毕后触发。
|
||
* 若 success=true,设备应在适当时机重启以加载新固件。
|
||
*/
|
||
typedef void (*ota_complete_cb_t)(bool success, ota_error_t error_code);
|
||
|
||
/**
|
||
* @brief 状态变化回调
|
||
* @param old_state 变化前的状态
|
||
* @param new_state 变化后的状态
|
||
*
|
||
* 每次 OTA 状态机发生转换时触发。
|
||
* 上层可在 OTA_STATE_COMPLETE 时自动重启,或在 OTA_STATE_ERROR 时告警。
|
||
*/
|
||
typedef void (*ota_state_cb_t)(ota_state_t old_state, ota_state_t new_state);
|
||
|
||
/**
|
||
* @brief 错误回调(颗粒度更细)
|
||
* @param error_code 错误码
|
||
* @param message 可读错误描述
|
||
*
|
||
* 在 ota_write 失败、end 失败等场景触发,先于状态变化回调。
|
||
* 可用于日志记录、错误提示等。
|
||
*/
|
||
typedef void (*ota_error_cb_t)(ota_error_t error_code, const char *message);
|
||
|
||
/* ================================================================
|
||
* 回调注册
|
||
* ================================================================ */
|
||
|
||
/** 注册进度回调(最多一个,后注册的覆盖前面的) */
|
||
void ota_set_progress_callback(ota_progress_cb_t cb);
|
||
|
||
/** 注册完成回调 */
|
||
void ota_set_complete_callback(ota_complete_cb_t cb);
|
||
|
||
/** 注册状态变化回调 */
|
||
void ota_set_state_callback(ota_state_cb_t cb);
|
||
|
||
/** 注册错误回调 */
|
||
void ota_set_error_callback(ota_error_cb_t cb);
|
||
|
||
/** 一次性注销所有回调 */
|
||
void ota_clear_callbacks(void);
|
||
|
||
/* ================================================================
|
||
* 底层 OTA API
|
||
*
|
||
* 调用顺序:
|
||
* 正常流程: ota_begin() → ota_write()×N → ota_end() → 重启设备
|
||
* 错误处理: ota_begin() → ota_write()×N → ota_abort()
|
||
* ================================================================ */
|
||
|
||
/**
|
||
* @brief 开始 OTA 更新
|
||
*
|
||
* 内部自动选择下一个可用的 OTA 分区,擦除分区空间。
|
||
*
|
||
* @param firmware_size 固件总大小(字节)。
|
||
* 传入 0 表示大小未知:跳过长度校验,progress 百分比始终为 0。
|
||
* @return
|
||
* - ESP_OK: 开始成功,可调用 ota_write
|
||
* - OTA_ERR_ALREADY_IN_PROGRESS: 上次 OTA 未结束
|
||
* - OTA_ERR_NO_OTA_PARTITION: 分区表无可用 OTA 分区
|
||
* - OTA_ERR_BEGIN_FAILED: ESP-IDF OTA begin 失败
|
||
*/
|
||
esp_err_t ota_begin(size_t firmware_size);
|
||
|
||
/**
|
||
* @brief 写入固件数据块
|
||
*
|
||
* 阻塞调用,写入 flash 前先进行必要的擦除。
|
||
* 建议每次写入 1024–4096 字节以获得最佳吞吐量。
|
||
*
|
||
* @param data 数据指针
|
||
* @param len 数据长度(字节)
|
||
* @return
|
||
* - ESP_OK: 写入成功
|
||
* - OTA_ERR_NOT_INITIALIZED: 未调用 ota_begin
|
||
* - OTA_ERR_STATE_ERROR: 当前状态不允许写入
|
||
* - OTA_ERR_WRITE_FAILED: 写入 flash 失败
|
||
*/
|
||
esp_err_t ota_write(const uint8_t *data, size_t len);
|
||
|
||
/**
|
||
* @brief 完成 OTA 更新
|
||
*
|
||
* 校验已写入数据的完整性,验证固件镜像,切换启动分区。
|
||
* 成功返回后,设备应在适当时机调用 esp_restart() 重启。
|
||
*
|
||
* @return
|
||
* - ESP_OK: 更新完成,可重启
|
||
* - OTA_ERR_END_FAILED: 校验或切换失败
|
||
* - OTA_ERR_SIZE_MISMATCH: 实写大小与声明不符
|
||
*/
|
||
esp_err_t ota_end(void);
|
||
|
||
/**
|
||
* @brief 中止 OTA 更新
|
||
*
|
||
* 放弃当前写入的固件,恢复状态机到 IDLE。
|
||
* 调用后目标 OTA 分区数据不可靠,下次 begin 会重新擦除。
|
||
*
|
||
* @return ESP_OK 或错误码
|
||
*/
|
||
esp_err_t ota_abort(void);
|
||
|
||
/* ================================================================
|
||
* 状态查询
|
||
* ================================================================ */
|
||
|
||
/** 获取当前 OTA 状态 */
|
||
ota_state_t ota_get_state(void);
|
||
|
||
/** 获取当前运行的固件分区(const 指针,不可修改) */
|
||
const esp_partition_t *ota_get_running_partition(void);
|
||
|
||
/** 获取正在更新的目标分区(仅在 IN_PROGRESS 或 VERIFYING 时有效) */
|
||
const esp_partition_t *ota_get_update_partition(void);
|
||
|
||
/** 获取已写入字节数 */
|
||
size_t ota_get_bytes_written(void);
|
||
|
||
/** 获取声明的固件总大小 */
|
||
size_t ota_get_firmware_size(void);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* OTA_MANAGER_H */
|