FOTA 开发指南
本文档介绍 HiDiTingV100 的 FOTA(Firmware Over-The-Air,固件空中升级)能力,并以 diting-community 应用镜像升级为例,说明升级包制作、传输、校验、升级和结果确认的完整流程。
本文档还介绍 FOTA 模块的工作原理、关键配置和验证方法,帮助开发者基于 SDK 提供的接口构建产品升级业务。
FOTA 背景知识
FOTA 工作原理
FOTA 用于在不重新烧录整机固件的情况下更新设备软件。升级系统通常由升级包制作端、升级包传输端和目标设备三部分组成:
| 组成部分 | 主要职责 | HiDiTingV100 对应实现 |
|---|---|---|
| 升级包制作端 | 选择新旧镜像,完成签名、压缩或差分处理,生成升级包 | build_3322_update.py、fota.cfg、build_upg_pkg.py |
| 升级包传输端 | 将 update.fwpkg 传输到设备 |
DebugKits、产品自定义网络或有线传输模块 |
| 目标设备 | 保存升级包、校验合法性、设置升级标记、更新镜像并记录结果 | Upgrade UAPI、文件系统、Recovery |
HiDiTingV100 支持以下升级模式:
| 升级模式 | 配置值 | 说明 | 适用场景 |
|---|---|---|---|
| 全量升级 | DecompressFlag=0 |
将完整的新镜像写入目标分区 | 初次集成、镜像变化较大或需要简化升级包制作流程 |
| 压缩升级 | DecompressFlag=0x3C7896E1 |
传输压缩后的新镜像,设备侧解压后更新 | 希望减少升级包体积,设备存储和耗时允许 |
| 差分升级 | DecompressFlag=0x44494646 |
根据旧镜像与新镜像生成差分数据,设备侧还原新镜像 | 版本演进路径明确、需要进一步减小升级包 |
注意事项:
- NV 镜像仅支持全量升级。
- 资源文件支持全量升级和压缩升级,不支持差分升级。
- 3322 平台配置支持差分升级,但未启用升级镜像解密能力。
ReRncFlag必须保持为0。
端到端升级流程
FOTA 的完整链路如下图所示。构建服务器生成升级包后,可由升级服务器或本地计算机下发到设备。设备接收完整升级包并校验通过后进入本地升级流程,升级结束后重新启动到新版本。

图 1 FOTA 端到端升级流程
设备侧的标准操作顺序如下:
- 初始化升级模块:系统启动时调用 uapi_upg_init 注册内存管理和日志输出函数。
- 获取存储能力:调用 uapi_upg_get_storage_size 获取升级包可用空间。
- 准备升级存储:在传输开始前调用 uapi_upg_prepare,传入完整升级包长度。
- 保存升级包:将升级包直接上传到固定路径,或按连续偏移分包调用写入接口。
- 申请升级:调用 uapi_upg_request_upgrade 校验升级包并写入升级标记。
- 执行本地升级:设备重启进入 Recovery,由 Recovery 再次初始化升级模块并调用 uapi_upg_start。
- 确认结果:升级完成后读取升级状态和结果,并检查应用版本、关键业务与设备日志。
diting-community 应用已在系统启动阶段初始化 Upgrade 模块。因此,应用 Demo 调用 uapi_upg_init() 时,应同时将 ERRCODE_SUCC 和 ERRCODE_UPG_ALREADY_INIT 视为可继续状态。将 Demo 移植到未集成系统初始化流程的目标时,必须注册有效的 malloc 和 free 函数。
升级包制作原理
升级包由 Key Area、FOTA Info Area、镜像哈希表和一个或多个镜像数据区组成。制作工具根据 fota.cfg 中的签名、版本、镜像 ID 和升级模式配置处理镜像,再生成统一的 update.fwpkg。

图 2 升级包制作流程
| 数据区域 | 主要内容 | 作用 |
|---|---|---|
| FOTA Key Area | 密钥算法、密钥版本、二级公钥和签名 | 建立升级包验签所需的信任信息 |
| FOTA Info Area | 升级包版本、硬件 ID、镜像数量和哈希表摘要 | 描述升级包整体属性 |
| 镜像哈希表 | 镜像 ID、位置、长度和镜像头哈希 | 定位并校验升级包中的各镜像 |
| 镜像数据区 | 镜像头、完整镜像、压缩数据或差分数据 | 提供实际写入目标分区的数据 |
参数与配置说明
FOTA 相关的主要配置文件如下:
| 配置文件 | 说明 | 修改建议 |
|---|---|---|
| src/build/config/target_config/3322/build_3322_update.py | 配置新旧镜像路径、参与升级的镜像列表和输出目录 | 根据构建目标配置对应的镜像输出路径 |
| src/build/config/target_config/3322/fota/fota.cfg | 配置签名方式、升级包版本、镜像 ID、升级模式和防回滚版本 | 修改前备份;镜像配置必须与目标设备一致 |
| src/middleware/chips/3322/include/shared/upg_config.h | 配置设备侧升级能力 | 应与升级包制作配置匹配 |
| src/middleware/chips/3322/include/shared/upg_common_porting.h | 定义板端升级包和资源索引路径 | 默认升级包路径为 /user/update/update.fwpkg |
diting-community 目标通过 CONFIG_OTA_UPDATE_SUPPORT 启用 Upgrade 模块,并通过 CONFIG_ENABLE_FOTA_SAMPLE 编译本文配套 Demo。移植到其他目标时,应同时检查 Upgrade 组件、Recovery 镜像、分区配置和 FOTA Demo 组件是否参与构建。
常用升级包参数如下:
| 参数 | 默认值或可选值 | 说明 |
|---|---|---|
SignSuite |
4 |
默认使用 SHA-256 非安全校验格式;量产产品应根据安全方案选择签名配置和密钥 |
KeyAlg |
0x2A13C856 |
与默认 SignSuite=4 对应的 SHA-256 ECC256 格式 |
Version |
0x00000000 |
FOTA 信息区版本号 |
version_ext |
0x00000000 |
单镜像防回滚版本号 |
version_mask |
0x00000000 |
单镜像防回滚版本掩码 |
DecompressFlag |
0、0x3C7896E1、0x44494646 |
分别表示全量、压缩和差分升级 |
ReRncFlag |
0 |
升级镜像加密标志;当前平台保持为 0 |
当前 fota.cfg 中常用镜像 ID 如下:
| 镜像名称 | 镜像 ID | 支持的升级模式 |
|---|---|---|
ssb |
0x4B1E3C2D |
全量、压缩;不建议加入应用侧常规 FOTA 升级包 |
seliteos |
0x4BE10F2D |
全量、压缩 |
recovery |
0x4B69872D |
全量、压缩、差分 |
application |
0x4B0F2D2D |
全量、压缩、差分 |
bt |
0x4BF01E2D |
全量、压缩、差分 |
dsp_main |
0x5A87A52D |
全量、压缩、差分 |
dsp_overlay |
0x5A87A54B |
全量、压缩、差分 |
nv |
0xCB9E063C |
仅全量;当前 3322 产品配置未开启 NV 升级能力 |
res_index |
0xCB9E0826 |
全量、压缩 |
res_data |
0xCB9E0832 |
全量、压缩 |
注意事项:当前非 Recovery 镜像启用了防回滚校验。制作升级包时,不应将镜像版本号配置为低于设备允许的版本;量产版本的版本号和版本掩码应由产品安全策略统一管理。
API 接口列表
本文档示例流程使用的接口如下:
| 接口函数 | 说明 |
|---|---|
| uapi_upg_init | 初始化升级模块并注册内存管理函数 |
| uapi_upg_get_storage_size | 获取升级包可用存储空间 |
| uapi_upg_prepare | 根据升级包总长度准备本地存储 |
| uapi_upg_write_package_sync | 按偏移同步写入升级包分片 |
| uapi_upg_write_package_async | 按偏移异步写入升级包分片 |
| uapi_upg_read_package | 从本地存储读取升级包数据 |
| uapi_upg_request_upgrade | 校验升级包、设置升级标记并按需重启 |
| uapi_upg_start | 执行本地升级 |
| uapi_upg_register_progress_callback | 注册升级进度回调 |
| uapi_upg_get_status | 获取当前升级状态 |
| uapi_upg_get_result | 获取升级结果和最后处理的镜像序号 |
| uapi_upg_reset_upgrade_flag | 清除升级标记,为下一次升级恢复状态 |
完整 API 列表
更多 FOTA 接口请参考:Upgrade API 参考
快速跑通 FOTA Demo
功能说明
本 Demo 使用 diting-community 的 application_signed.bin 制作单应用全量升级包,再通过 DebugKits 将升级包上传到设备。Demo 提供升级状态查询、存储准备、升级申请、升级标记清理和传输层分包写入接口。应用侧申请升级后,设备重启进入 Recovery 完成镜像更新。
| 验证项 | Demo 行为 |
|---|---|
| 升级对象 | application 镜像 |
| 升级模式 | 全量升级 |
| 升级包名称 | update.fwpkg |
| 板端路径 | /user/update/update.fwpkg |
| 完整性校验 | 申请升级时执行整包校验,升级时再次校验镜像 |
| 预期结果 | Recovery 升级成功,设备重启运行新应用版本 |
配套 AT 命令如下:
| 命令 | 说明 |
|---|---|
AT+FOTASTATUS |
查询 Demo 状态、存储空间、升级状态和上次升级结果 |
AT+FOTABEGIN=<package_len> |
使用升级包完整长度准备本地存储 |
AT+FOTAREQUEST=<reset> |
校验升级包并申请升级;reset=1 时申请成功后立即重启 |
AT+FOTARESETFLAG |
清除升级标记并恢复 Demo 上下文 |
准备工作
说明: 本文示例命令以一站式 CLI 为主。实际开发可从以下三种环境中选择,推荐使用一站式 CLI。
| 开发环境 | 适用场景 | 使用指南 |
|---|---|---|
| 一站式 CLI(推荐) | 快速完成目标选择、构建、烧录和串口监视 | 一站式 CLI 开发环境使用指南 |
| HiSpark Studio for VS Code | 图形化编辑、编译、烧录和调试 | HiSpark Studio for VS Code 开发环境使用指南 |
| WSL 与 Docker | 在 Windows 上使用一致的 Linux 容器构建环境 | WSL 与 Docker 环境使用指南 |
- 按照一站式CLI开发环境使用指南完成环境安装,并确认
fbb doctor检查通过。 - 准备一块已烧录基线版本的 HiDiTing 开发板,并记录基线应用的版本信息或可观察行为。
- 确认目标配置已包含
CONFIG_OTA_UPDATE_SUPPORT和CONFIG_ENABLE_FOTA_SAMPLE,Recovery 镜像和分区表已正确烧录。 - 准备 DebugKits,并通过调试串口连接开发板。
- 开发验证阶段可使用 SDK 默认测试密钥;量产环境必须替换为产品密钥,并按安全规范保护私钥。
编译
在工程目录中设置 diting-community 打包目标并执行编译:
编译成功后,完整固件位于:
用于制作 FOTA 升级包的应用镜像位于:
首次验证 FOTA 前,先使用一站式 CLI 将该构建对应的完整基线固件烧写到开发板,并打开 UART2 日志串口;将 COM3 替换为实际端口:
fbb flash -f "$env:FBB_SDK_DIR\output\3322\fwpkg\diting-community.fwpkg" --chip 3322 -d --timeout 180
fbb monitor --port COM3 --baud 750000
后续 FOTA 验证传输的是升级包,不需要为每次升级重复整包烧写。
制作升级包
-
检查 src/build/config/target_config/3322/build_3322_update.py 中的
self.app_bin。diting-community应使用以下输出路径:如果产品使用其他构建目标,应将该路径调整为对应目标的实际输出目录。
-
保持
get_new_image()中仅启用application: -
确认 src/build/config/target_config/3322/fota/fota.cfg 中
[application]使用全量模式: -
在 SDK 的
src目录执行升级包制作脚本:Linux:
Windows:
说明:SDK 当前提供 Linux 和 Windows 版本的签名、压缩工具。macOS 主机请在受支持的 Linux 构建环境中执行升级包制作流程。
-
确认生成以下文件:
-
在 Linux 构建环境中获取升级包的完整字节数,后续调用
AT+FOTABEGIN时必须使用该值:
使用方式
- 执行
AT+FOTASTATUS,确认命令可用且storage不小于升级包长度。 - 根据
update.fwpkg的完整字节数执行AT+FOTABEGIN=<package_len>。命令返回OK后再上传文件。 - 在 DebugKits 的 System 插件中选择“上传文件”。
- 本地文件选择
output/3322/upgrade/update.fwpkg。 -
板端文件路径填写
/user/update/update.fwpkg,单击“Upload”并等待进度达到 100%。
图 3 使用 DebugKits 上传升级包
-
文件接收完成后执行
AT+FOTAREQUEST=1。命令校验升级包并写入升级标记,申请成功后设备立即重启;串口可能在输出最终OK前断开。 - Recovery 初始化升级模块后自动检查升级标记,并调用
uapi_upg_start()完成本地升级。
说明:调试阶段可先执行
AT+FOTAREQUEST=0完成校验和升级申请,再通过受控方式手动复位。需要放弃尚未执行的升级请求时,执行AT+FOTARESETFLAG清除升级标记。说明:SDK 中的 DFX 更新测试还提供分包写入验证路径。该测试先将源文件放到
/user/test/update.fwpkg,再按连续偏移调用uapi_upg_write_package_sync()写入升级存储。该路径仅用于验证分包保存流程,不应与正式升级包固定路径/user/update/update.fwpkg混用。
预期结果
- 升级包制作脚本执行成功,在
output/3322/upgrade生成update.fwpkg。 - DebugKits 上传进度达到 100%,板端可访问
/user/update/update.fwpkg。 uapi_upg_request_upgrade()返回ERRCODE_SUCC,设备重启进入 Recovery。- Recovery 日志显示检测到升级请求并开始处理
application镜像。 - 升级完成后设备再次重启,运行新版本应用。
uapi_upg_get_status()返回升级成功状态,或uapi_upg_get_result()返回UPG_RESULT_UPDATE_SUCCESS。
文件结构与代码走读
文件结构
FOTA Demo、打包工具、接口、设备适配和运行实现位于以下路径:
samples/native_samples/fota/
├── CMakeLists.txt # fota_sample 组件配置
├── fota_demo.h # 状态、信息结构和传输层接口
├── fota_demo.c # FOTA 状态机与 Upgrade UAPI 调用
└── fota_demo_at.c # FOTA AT 命令注册与处理
src/
├── build/config/target_config/3322/
│ ├── build_3322_update.py # 3322 升级包制作入口
│ └── fota/fota.cfg # 升级包格式、签名和镜像配置
├── build/script/
│ └── build_upg_pkg.py # 通用升级包制作逻辑
├── include/middleware/utils/
│ └── upg.h # FOTA 对外接口与数据结构
└── middleware/
├── chips/3322/
│ ├── include/shared/
│ │ ├── upg_config.h # 3322 升级能力配置
│ │ └── upg_common_porting.h # 板端文件路径等适配配置
│ └── update/ # 3322 存储、备份和公共适配
└── utils/
├── dfx/diag_dfx_cmd/diag_update/
│ └── diag_update.c # DFX 升级命令与分包写入示例
└── update/
├── common/ # 升级公共逻辑与校验
├── storage/ # 升级包存储和升级申请
└── local_update/ # 全量、压缩、差分和资源升级
各文件职责
| 文件 | 职责 | 关键内容 |
|---|---|---|
| /samples/native_samples/fota/fota_demo.c | 实现传输无关的 FOTA 状态机 | 初始化、准备、连续写入、升级申请、状态查询和标记清理 |
| /samples/native_samples/fota/fota_demo_at.c | 提供开发板调试入口 | AT+FOTASTATUS、AT+FOTABEGIN、AT+FOTAREQUEST、AT+FOTARESETFLAG |
| /samples/native_samples/fota/fota_demo.h | 定义 Demo 对外接口 | 状态枚举、查询结构和传输层调用接口 |
| /samples/native_samples/fota/CMakeLists.txt | 定义 fota_sample 构建组件 |
源文件、头文件路径、AT 接入宏和整库链接 |
build_3322_update.py |
选择升级镜像并生成 update.fwpkg |
upg_base_info、get_new_image()、get_old_image() |
fota.cfg |
定义升级包和各镜像属性 | SIGN_CFG、FOTA_KEY_AREA、FOTA_INFO_AREA、DecompressFlag |
upg.h |
提供应用和 Recovery 可调用的 FOTA UAPI | 初始化、保存、申请、启动、状态和校验接口 |
upg_config.h |
定义当前产品支持的升级能力 | 验签、差分、防回滚、文件系统和资源文件支持 |
upg_common_porting.h |
提供板级路径定义 | UPG_FILE_NAME、UPG_RES_INDEX_PATH |
upg_storage.c |
管理升级包保存、升级标记和升级申请 | uapi_upg_prepare()、uapi_upg_write_package_sync()、uapi_upg_request_upgrade() |
upg_process.c |
执行升级包校验和镜像更新 | uapi_upg_start() |
upg_verify.c |
校验包头、哈希表和镜像 | 包头校验、签名校验、镜像校验 |
diag_update.c |
展示分包写入和升级命令接入方式 | 连续偏移写入、升级请求和本地升级 |
代码走读
升级包制作入口
build_3322_update.py 通过 get_new_image() 选择升级包包含的新镜像;仅在制作差分升级包时,才需要在 get_old_image() 中提供严格匹配的旧版本镜像。脚本最终调用通用打包逻辑,在 output/3322/upgrade 目录生成 update.fwpkg。
conf = get_parameters()
conf.app_name = "update"
conf.upg_format_path = info.fota_format_path
conf.base = info.fota_cfg
conf.new_images = get_new_image(info)
conf.old_images = get_old_image(info)
conf.output_dir = info.upg_output
begin(conf)
升级模块初始化
标准应用在 CONFIG_OTA_UPDATE_SUPPORT 打开时初始化升级模块。malloc 和 free 为必选函数,serial_putc 为可选日志输出函数。
upg_func_t upg_func = {0};
upg_func.malloc = upg_malloc;
upg_func.free = upg_free;
upg_func.serial_putc = upg_putc;
errcode_t ret = uapi_upg_init(&upg_func);
if (ret != ERRCODE_SUCC && ret != ERRCODE_UPG_ALREADY_INIT) {
/* 记录初始化失败日志并退出升级流程 */
}
应用和 Recovery 属于不同程序,均需要在各自启动流程中初始化升级模块。
准备并写入升级包
分包传输时,应先传入完整升级包长度完成准备,再按从 0 开始的连续偏移写入。下一包偏移必须等于上一包的 offset + len。根据 upg.h 的接口约束,写入偏移和分片长度均应按 4 字节对齐。
errcode_t fota_begin(uint32_t package_len)
{
if (package_len == 0 || package_len > uapi_upg_get_storage_size()) {
return ERRCODE_UPG_INVALID_PARAMETER;
}
upg_prepare_info_t info = {
.package_len = package_len,
};
return uapi_upg_prepare(&info);
}
errcode_t fota_write(uint32_t offset, const uint8_t *data, uint16_t len)
{
if (data == NULL || len == 0 || (offset % 4) != 0 || (len % 4) != 0) {
return ERRCODE_UPG_INVALID_PARAMETER;
}
return uapi_upg_write_package_sync(offset, data, len);
}
申请并执行升级
升级包完整保存后,在应用侧调用 uapi_upg_request_upgrade()。当 reset 为 true 时,申请成功后设备重启;Recovery 检测到升级标记后调用 uapi_upg_start()。
errcode_t fota_request(void)
{
errcode_t ret = uapi_upg_request_upgrade(true);
if (ret != ERRCODE_SUCC) {
/* 整包校验或升级标记设置失败,记录 ret */
return ret;
}
/* 申请成功后进入重启流程,正常情况下不会继续执行业务代码 */
return ERRCODE_SUCC;
}
uapi_upg_start() 是阻塞接口,升级期间不应在同一执行上下文中运行其他耗时业务。标准产品通常在 Recovery 或专用升级线程中执行该接口。
查询升级状态和结果
升级完成并重新启动应用后,可读取升级状态和结果,用于日志记录或界面提示:
void fota_print_result(void)
{
upg_result_t result = UPG_RESULT_MAX;
uint32_t last_image_index = 0;
if (uapi_upg_get_result(&result, &last_image_index) == ERRCODE_SUCC) {
/* 输出 result 和 last_image_index,或上报到设备管理平台 */
}
}
基于 FOTA Demo 开发应用
代码清单
配套 Demo 已按以下结构接入 samples/native_samples/fota:
samples/native_samples/fota/
├── CMakeLists.txt # fota_sample 组件构建配置
├── fota_demo.c # 升级状态机和 Upgrade UAPI 调用
├── fota_demo.h # 对外接口与状态定义
└── fota_demo_at.c # 可选的 AT 串口回归入口
| 文件 | 职责 |
|---|---|
fota_demo.c |
初始化升级模块,检查空间,准备存储,接收数据,申请升级、清除标记和记录结果 |
fota_demo.h |
定义 fota_demo_init()、fota_demo_begin()、fota_demo_write()、fota_demo_request() 等接口 |
fota_demo_at.c |
可选地将状态查询、存储准备、升级申请和标记清理封装为 AT 命令 |
CMakeLists.txt |
声明核心源文件、头文件路径和组件链接配置;需要串口回归时再纳入 AT 适配文件和注册宏 |
升级状态机设计
产品 FOTA 模块应使用显式状态机,避免重复准备、乱序写入或在升级过程中再次触发升级:
| 状态 | 允许操作 | 成功后的下一状态 |
|---|---|---|
IDLE |
查询空间并调用 fota_demo_begin() |
PREPARED |
PREPARED |
通过 DebugKits 上传固定路径文件,或提交首个分片 | PREPARED、RECEIVING 或 READY |
RECEIVING |
按连续且 4 字节对齐的偏移写入升级包分片 | RECEIVING 或 READY |
READY |
校验累计接收长度并申请升级 | REQUESTED |
REQUESTED |
等待重启进入 Recovery | 设备重启后由 uapi_upg_get_status() 和 uapi_upg_get_result() 反映结果 |
FAILED |
保存错误码并执行 fota_demo_reset() |
IDLE |
CMakeLists.txt 示例
FOTA Demo 的组件配置如下:
set(COMPONENT_NAME "fota_sample")
set(SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/fota_demo.c
)
set(PUBLIC_HEADER
${CMAKE_CURRENT_SOURCE_DIR}
)
set(PRIVATE_HEADER
${ROOT_DIR}/src/include
${ROOT_DIR}/src/include/middleware/utils
)
set(PUBLIC_DEFINES "")
set(WHOLE_LINK true)
set(MAIN_COMPONENT false)
build_component()
产品业务应在下载任务或升级服务中依次调用 fota_demo_init()、fota_demo_begin()、fota_demo_write() 和 fota_demo_request()。只有需要复用当前 Demo 的串口回归方式时,才把 fota_demo_at.c 加入 SOURCES、补充 AT 私有头文件目录并定义 AT_DITING_EXAMPLE_FOTA;AT 入口不是升级状态机的一部分。
关键代码片段
产品传输层应将接收到的数据转换为连续的 offset + data + len 分片,并调用 Demo 接口:
errcode_t product_fota_receive(uint32_t offset, const uint8_t *data, uint16_t len)
{
return fota_demo_write(offset, data, len);
}
fota_demo_write() 内部检查 Demo 状态、连续偏移、4 字节对齐和累计长度。全部分片写入后,调用 fota_demo_request(true) 申请升级并立即重启。
如果使用异步写入接口,必须等待写完成回调返回成功后再提交下一包:
static void fota_write_done(errcode_t result)
{
if (result == ERRCODE_SUCC) {
/* 通知传输任务发送或提交下一包 */
} else {
/* 停止接收并记录失败结果 */
}
}
测试验证
- 正常升级:从基线版本升级到更高版本,确认版本和业务功能正确。
- 重复升级:重复下发相同升级包,确认产品策略能够拒绝或安全处理。
- 降级测试:下发低版本升级包,确认防回滚策略生效。
- 断点测试:分别在下载、校验和镜像更新阶段模拟断电,确认设备可恢复或重新进入升级。
- 损坏包测试:修改升级包任意字节,确认整包校验失败且不会写入目标镜像。
- 空间边界:测试等于、接近和超过可用空间的升级包。
- 乱序数据:故意提交错误偏移,确认 Demo 拒绝写入并保留错误日志。
- 多镜像测试:对应用、Recovery、资源文件等组合包逐项确认升级结果。
注意事项
- 升级包路径固定:文件系统模式下,正式升级包使用
/user/update/update.fwpkg。文件名或路径不一致会导致升级模块无法读取升级包。 - 准备操作必须在传输前完成:调用
uapi_upg_prepare()时必须传入完整且非 0 的升级包长度。 - 分包必须连续写入:写入偏移应从 0 开始连续递增,不得乱序、重复或跳过数据。
- 分包必须满足对齐要求:调用同步或异步写入接口时,分片偏移和长度应按 4 字节对齐;升级包制作和传输协议应保证最后一个分片同样满足该要求。
- 异步写入不得并发:只有收到上一次异步写完成回调且结果成功后,才能写入下一包。
- 校验失败不得强制升级:
uapi_upg_request_upgrade()返回失败时,应删除或覆盖错误升级包,不得绕过校验设置升级标记。 - 升级过程中保持供电稳定:进入本地升级后不支持业务侧取消。虽然平台支持升级进度恢复,仍应避免断电和看门狗异常复位。
- 进度回调保持轻量:进度回调中只更新状态或发送事件,不得执行阻塞 I/O、动态加载或长时间日志输出。
- 差分包严格绑定旧版本:差分包只能用于制作时指定的旧镜像版本。旧镜像不匹配会导致校验或差分恢复失败。
- 版本号单调递增:启用防回滚后,新包的镜像版本不得低于设备允许版本。版本掩码应由产品统一管理。
- 私钥不得随产品发布:SDK 内的默认私钥仅用于开发验证。量产密钥应离线保存,并在受控环境中完成签名。
- 镜像能力必须匹配:NV 仅支持全量升级;资源文件不支持差分升级;当前平台未开启升级镜像解密能力。
- 不得升级分区表:普通 FOTA 包不应包含分区表。升级前应确认现有分区布局能够容纳新镜像和升级包。
- 谨慎升级启动链镜像:SSB 等启动链镜像失败可能影响设备启动,普通应用升级不建议包含此类镜像。
- 资源升级预留索引空间:资源索引文件与升级包共享存储空间时,需要为
res_index.bin留出空间;资源文件数量较多时应提前做容量和性能验证。 - 先验证再批量发布:升级包应在目标硬件、目标基线版本和量产分区配置上完成灰度验证后再发布。
常见编译错误
| 现象或错误码 | 常见原因 | 处理方法 |
|---|---|---|
build_3322_update.py 提示镜像不存在 |
self.app_bin 与当前构建目标不一致,或目标固件尚未编译 |
确认路径为 output/3322/acore/diting-community/application_signed.bin,并先执行 fbb build |
| 打包工具或 LZMA 工具不存在 | SDK 子模块或工具链未完整下载 | 更新 SDK 子模块,并执行 fbb doctor 检查环境 |
| Python 模块导入失败 | 未在 SDK src 目录执行,或 Python 环境不完整 |
回到 src 目录执行脚本,并使用一站式 CLI 环境提供的 Python |
CMake 找不到 upg.h |
Demo 的私有头文件路径或组件依赖未配置 | 增加 ${ROOT_DIR}/src/include/middleware/utils,并确认升级组件加入当前目标 |
链接阶段提示 uapi_upg_* 未定义 |
目标未启用 OTA 更新组件 | 确认目标配置包含 CONFIG_OTA_UPDATE_SUPPORT 和 Upgrade 相关组件后重新编译 |
AT+FOTASTATUS 等命令不存在 |
FOTA Demo 未参与构建或 AT 注册入口未执行 | 检查 CONFIG_ENABLE_FOTA_SAMPLE、fota_sample 和 AT_DITING_EXAMPLE_FOTA |
CMake 提示 Function missing ending ")" |
CMakeLists.txt 括号不完整或混用了异常行结束符 |
检查报错行附近的 set()/if(),并统一文件行结束符后重新配置 |
ERRCODE_UPG_NOT_INIT (0x80003040) |
调用其他升级接口前未执行 uapi_upg_init() |
在应用和 Recovery 各自的初始化阶段调用 uapi_upg_init() |
ERRCODE_UPG_ALREADY_INIT (0x80003041) |
同一程序重复初始化升级模块 | 统一初始化入口;将该返回值作为已初始化状态处理 |
ERRCODE_UPG_INVALID_PARAMETER (0x80003042) |
包长为 0、参数为空、长度越界或状态不匹配 | 检查 package_len、数据指针、分片长度和当前状态 |
ERRCODE_UPG_NOT_PREPARED (0x80003054) |
未完成存储准备,或 Demo 状态不允许写入和申请升级 | 先执行 AT+FOTABEGIN=<package_len>,再传输完整升级包 |
ERRCODE_UPG_INVALID_BUFF_LEN (0x80003055) |
分片长度为 0 或不符合传输约束 | 确保数据非空,并按 4 字节对齐分片长度和偏移 |
ERRCODE_PARTITION_CONFIG_NOT_FOUND (0x80003003) |
分区表未烧录或缺少 FOTA 相关分区 | 重新烧录与当前固件匹配的分区表,并核对分区 ID |
| 升级包长度未超过分区容量,但仍提示空间不足 | 设备残留旧升级包或资源索引占用空间 | 删除旧升级包,重新查询可用空间,并为资源索引预留容量 |
ERRCODE_UPG_FILE_OPEN_FAIL (0x80003057) |
文件不存在、路径错误或文件系统未挂载 | 检查 /user/update/update.fwpkg、目录权限和文件系统状态 |
ERRCODE_UPG_FILE_WRITE_FAIL (0x80003058) |
空间不足、写入中断或文件系统异常 | 检查剩余空间,删除旧文件并重新传输升级包 |
ERRCODE_UPG_FILE_SEEK_FAIL (0x80003059) |
分包偏移错误或文件损坏 | 确保分包连续写入并从偏移 0 重新开始 |
ERRCODE_UPG_FILE_READ_FAIL (0x80003060) |
升级包未完整上传或文件读取异常 | 比对文件长度,重新上传并再次申请升级 |
ERRCODE_UPG_NOT_NEED_TO_UPDATE (0x80003047) |
未检测到有效升级标记或升级包不完整 | 确认 uapi_upg_request_upgrade() 已成功,检查升级标记和包完整性 |
| 升级包校验失败 | 传输损坏、密钥或算法不匹配、镜像 ID 不匹配、包不属于当前设备 | 重新传输,核对 SignSuite、KeyAlg、硬件 ID、镜像 ID 和目标版本 |
| 防回滚校验失败 | version_ext 低于设备允许版本,或版本掩码配置错误 |
使用更高版本重新制作升级包,核对产品版本策略 |
| 差分恢复失败 | 设备旧镜像与打包时使用的旧镜像不一致 | 使用正确基线镜像重新制作差分包,或改用全量升级包 |
| 资源索引创建失败 | 升级包占满可用空间,没有为 res_index.bin 预留容量 |
减小升级包或调整存储布局,确保索引和资源数据均可落盘 |
调试建议:
- 优先记录每个 Upgrade UAPI 的十六进制返回值、升级包长度、当前偏移和已接收长度。
- 打包失败时检查
output/3322/upgrade/temp_dir中的中间文件和脚本输出。 - 申请升级前比对计算机端与设备端的文件大小;必要时增加摘要校验。
- 升级失败后调用
uapi_upg_get_result(),结合last_image_index定位失败镜像。 - 对差分、压缩和多镜像升级,先分别验证单镜像全量包,再逐步增加复杂度。
- 使用
fota_demo_write()时同时记录期望偏移和实际偏移,优先排查乱序、重复和未对齐分片。