/** * @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 #include #include #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 */