Files
2026-06-12 17:03:10 +08:00

210 lines
7.2 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file ota_manager.h
* @brief 固件 OTAOver-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 完成百分比 (0100)
* @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 */