WS63 LiteOS OTA 如何实现
0. LiteOS OTA 实现方式概述
在 LiteOS 生态中,常见的 OTA 实现方式主要有以下三种:
| 方式 | 适用场景 | 核心机制 |
|---|---|---|
| ① UPG 模块模式 | 轻量级 MCU/SoC(如 WS63) | 芯片厂商封装 UPG 接口,应用层直接调用 API 写入 Flash,由 Bootloader 完成分区切换 |
| ② IoT Link SDK 模式 | 物联网设备(NB-IoT 等) | 基于 LwM2M/PCP 协议与云平台交互,采用 Loader + App 双镜像架构,支持差分升级 |
| ③ OpenHarmony 标准系统模式 | 富设备(RK3568、手机等) | 独立的 updater 分区与 misc 分区,uboot 引导进入 updater 模式,有完整 Linux 用户态升级流程 |
本文实现的是方式 ①:基于 WS63 芯片提供的 UPG 模块,在 LiteOS 应用层构建 TCP 局域网服务端,通过流式接收固件并调用 uapi_upg_* 系列接口完成升级。相比其他两种方式,这种方式最轻量、最直接,适合局域网快速烧录和调试场景。
无论哪种实现方式,OTA 的整体流程都可以概括为以下四个步骤:
本文按实际操作的先后顺序组织,共分为以下几个部分:
- 了解 UPG 接口:理解底层升级模块的工作原理和 API
- 环境准备:生成签名密钥、全量编译并烧录底包(必须先完成,否则后续 OTA 校验会失败)
- 代码实现:编写 OTA 服务代码
- OTA 升级与推送:生成升级包并远程推送到设备
- 问题排查:常见问题及解决方法
- 方案对比:LiteOS OTA 与 OpenHarmony 标准系统 OTA 的差异
1. 了解 LiteOS OTA 相关接口
WS63 的 OTA 升级依赖于底层提供的 UPG (Upgrade) 模块。应用层只需要把接收到的固件包按顺序调用接口写入即可,底层的签名校验和 Flash 分区操作由 UPG 模块完成。
UPG 内部工作流程
UPG 内部 Flash 分区与数据流向
WS63 采用 AB 双分区 方案,Flash 整体布局如下图所示(总大小4MB):
上图中与 UPG 相关的区域划分定义在 build/config/target_config/ws63/param_sector/param_sector.json 中,分区 ID 枚举见 middleware/chips/ws63/partition/include/partition_resource_id.h:
| 区域 | 分区名 | 作用 |
|---|---|---|
| A 区(运行区) | PARTITION_APP_IMAGE |
当前正在运行的固件,起始地址 0x030000,大小 0x240000(约 2.25MB) |
| B 区(FOTA 区) | PARTITION_FOTA_DATA |
起始地址 0x270000,大小 0x183000(约 1.512MB)。前半部分存放接收到的升级包数据;后半部分为升级标记区(记录升级状态、包长度、镜像数量等);最后 4KB 为 AB 配置区(记录当前从哪个分区启动) |
物理分区与 A/B 逻辑分区的映射关系如下图所示:
物理上的 imageA (2.25M)+ fota data (1.5M)被从中问强行切分,变成了等大的两个1.93MB 区域,这也解释了为什么 fota_data 不仅仅是暂存区,它的前半部分其实是 B 区代码的运行空间。
下载阶段(App 中)的数据流向:
应用层调用 UPG 存储层接口(实现在 middleware/utils/update/storage/upg_storage.c),将网络接收到的固件写入 Flash:
uapi_upg_prepare()—— 擦除PARTITION_FOTA_DATA分区,并在升级标记区写入head_magic和package_lengthuapi_upg_write_package_sync()—— 将网络接收到的固件数据按 offset 顺序写入 FOTA 区起始地址 + offset。实际的 Flash 读写由middleware/chips/ws63/update/common/upg_common_porting.c中的upg_flash_write()完成uapi_upg_verify_file()—— 对 FOTA 暂存区中的固件进行 ECDSA 签名 + SHA256 哈希校验uapi_upg_request_upgrade(true)—— 在升级标记区写入head_end_magic和firmware_num,标记”升级包已就绪”,然后重启
升级阶段(Bootloader 中)的数据流向:
Bootloader 启动后(入口在 bootloader/flashboot_ws63/startup/main.c 的 start_fastboot()),调用 ws63_upg_check() 检测升级标记。若标记完整,自动调用 uapi_upg_start()(实现在 middleware/utils/update/local_update/upg_process.c):
- 从 FOTA 暂存区 读取升级包,逐个镜像处理(
upg_process_update()→upg_perform_image_task()) - 对于全镜像升级,调用
middleware/utils/update/local_update/upg_upgrade.c中的uapi_upg_full_image_update(),将 FOTA 区的数据读出,写入目标镜像分区(B 区或对应分区) - 如果是 AB 模式,写入完成后调用
middleware/chips/ws63/update/ab_upg/upg_ab.c中的upg_set_run_region()切换启动分区 - 设置
complete_flag,再次重启,此时从新的分区启动新固件
OTA 动作与数据流向的整体过程如下图所示:
注意图片内容是假设当前系统正在A区运行
回滚机制:AB 模式下,如果新固件连续验签失败达到阈值(3 次),Bootloader 会自动切换回另一个分区启动,实现失败回滚。
核心头文件为 include/middleware/utils/upg.h,下面是此头文件里的相关接口说明,你也可以直接打开头文件查看内容:
| API 接口 | 功能说明 | 调用时机 |
|---|---|---|
uapi_upg_init(const upg_func_t *funcs) |
初始化 UPG 模块。需要传入内存分配和串口打印的回调函数。 | 系统启动后,OTA 服务启动前。 |
uapi_upg_reset_upgrade_flag(void) |
清除残留标志。防止上次失败的升级状态干扰本次 OTA。 | UPG 初始化后紧接着调用。 |
uapi_upg_prepare(upg_prepare_info_t *info) |
准备升级。告知底层即将接收的固件总大小,底层会做 Flash 空间检查。 | 接收到网络传输的固件 Header 时调用。 |
uapi_upg_write_package_sync(offset, buf, len) |
同步写入数据块。将接收到的固件分块写入 UPG 暂存区。 | 网络接收循环中,每收到一块数据时调用。 |
uapi_upg_verify_file(upg_package_header_t *hdr) |
固件校验。对写入完成的暂存区固件进行 ECDSA 签名和 SHA256 哈希校验。 | 整个固件包接收完成后调用。 |
uapi_upg_request_upgrade(bool reboot) |
触发升级。通知 Bootloader 切换分区,并可选择是否立即重启。 | verify 校验通过(返回成功)后调用。 |
2. 环境准备
在开始编写 OTA 代码之前,必须先完成以下两项环境准备工作。否则后续生成的 OTA 升级包将无法通过设备端的签名校验。
2.1 用脚本生成签名密钥
OTA 固件包必须经过签名,Bootloader 和 UPG 模块才会允许刷入。第一次开发或更换工程时,需要生成一套专属密钥。
脚本位置: build/config/target_config/ws63/sign_encry/gen_all_key.sh
- 进入 SDK 的加密配置目录:
1
cd build/config/target_config/ws63/sign_encry
- 执行一键生成密钥脚本:注意:执行过程中提示旧密钥会被覆盖,输入
1
bash gen_all_key.sh
Y确认。 - 输出结果:脚本会在
build/config/target_config/ws63/sign_config/目录下生成一系列.pem私钥文件(包含 Root、App、Bootloader 私钥等),后续打包脚本会自动调用这些文件。
2.2 全量编译与本地烧录底包(极度重要)
⚠️ 为什么必须有这一步?
刚才生成的只是私钥。对应的公钥会被编译进 Bootloader 和设备的 efuse 中。如果只生成密钥而不做本地全量烧录,设备里的旧公钥将永远无法校验通过你用新私钥打包的 OTA 固件(表现为
uapi_upg_verify_file返回0x01或错误码)
编译全量工程(回到 SDK 根目录):
1
python build.py ws63-liteos-app
**打包全量烧录包 (.fwpkg)**:
(如果 build.py 没有自动生成,手动执行以下命令)
脚本位置:tools/pkg/packet.py1
python tools/pkg/packet.py ws63 ws63-liteos-app ""
产物路径:
output/ws63/fwpkg/ws63-liteos-app/ws63-liteos-app_all.fwpkg本地物理烧录:
使用官方烧录工具(如 HiBurn 或 JLink),通过串口/USB线将ws63-liteos-app_all.fwpkg完整烧录到开发板中。
(完成这一步后,你的开发板就具备了接收后续 OTA 的最新公钥基础。)
3. 代码实现
我们可以用Liteos的这套接口,在UPG外部实现完整的 OTA 相关代码
本工程中的 OTA 服务实现位于以下文件:
- 头文件:
application/samples/peripheral/smart_car/apps/robot_demo/services/ota_service.h - 实现文件:
application/samples/peripheral/smart_car/apps/robot_demo/services/ota_service.c - 主程序初始化:
application/samples/peripheral/smart_car/apps/robot_demo/core/robot_mgr.c
3.1 OTA 服务接口定义
文件位置: application/samples/peripheral/smart_car/apps/robot_demo/services/ota_service.h
1 |
|
3.2 OTA 服务核心实现
文件位置: application/samples/peripheral/smart_car/apps/robot_demo/services/ota_service.c
核心逻辑分为 7 个阶段:
阶段 A:UPG 初始化
1 | void ota_service_init(void) |
阶段 B:TCP 服务端启动
1 | bool ota_service_start(uint32_t expected_size) |
阶段 C:接收 8 字节 Header
这个Header由 魔术字+包大小 组成
1 | [4 bytes] Magic: "OTAx" (0x4F 0x54 0x41 0x78) |
阶段 D:UPG 预准备
1 | errcode_t ret = uapi_upg_prepare(&prepare_info); |
阶段 E:流式接收并写入 UPG
1 | while (offset < total_size) { |
阶段 F:UPG 校验
1 | ret = uapi_upg_read_package(0, (uint8_t *)&hdr, sizeof(hdr)); |
阶段 G:请求升级并重启
1 | ret = uapi_upg_request_upgrade(true); // true = 立即重启 |
3.3 主程序初始化
文件位置: application/samples/peripheral/smart_car/apps/robot_demo/core/robot_mgr.c
在 robot_mgr_init() 中 udp_service_init() 之后添加:
1 |
|
4. OTA 升级与推送
完成环境准备和代码编写后,即可开始制作 OTA 升级包并推送到设备进行验证。
4.1 生成 OTA 升级包 (Pkg)
现在你的设备已经跑起来了,假设你需要修复一个 Bug 并通过 OTA 推送。
修改代码:
在应用代码中做一些改动,比如修改版本号打印:printf("OTA Version V2.0\r\n");重新编译代码:
1
python build.py ws63-liteos-app
获取 OTA 产物:
产物路径:output/ws63/upgrade/update.fwpkg注意区分,这个包通常只有 1MB 左右,比全量包小很多。
执行 OTA 升级包打包脚本:
脚本位置:build/config/target_config/ws63/build_ws63_update.py1
python build/config/target_config/ws63/build_ws63_update.py --pkt app
该脚本会提取上一步编译出的
.bin文件,使用【2.1】生成的私钥进行 ECDSA 签名,并打包成 UPG 格式。
验证方法: 可以在代码中加入明显的版本标记(如 printf("[FIRMWARE] OTA_TEST_BUILD_V2\r\n")),OTA 重启后观察串口是否出现该标记,即可确认固件已更新。
4.2 远程 OTA 推送
最后一步,通过网络将 update.fwpkg 发送给处于局域网内的开发板。
这个脚本是我自己写的,可以根据我的代码实现一个自己的脚本
在我的工程里,脚本位置是: tools/ota_sender.py
- 确保开发板已连接 Wi-Fi,且你编写的网络 OTA 服务端(第3节的代码)处于监听状态。
- 使用配套的 Python 发送端脚本推送固件:
1
2# 格式:python ota_sender.py <目标开发板IP> <OTA包路径>
python tools/ota_sender.py 192.168.111.20 output/ws63/upgrade/update.fwpkg - 观察设备端反应:
- 设备端开始接收数据,串口打印接收进度。
- 接收完成后,触发
uapi_upg_verify_file校验。 - 串口输出类似
[UPG] verify success!。 - 设备自动重启,此时底层 Bootloader 会接管,将暂存区的新固件覆盖到主分区。
- 再次开机,看到你修改的
OTA Version V2.0打印,说明完整 OTA 流程大功告成。
4.3 日常操作流程
1 | # Step 1: 修改代码(如 robot_mgr.c 等) |
5. 可能遇到的问题
| 现象 | 原因 | 解决 |
|---|---|---|
| TCP 连接失败 | OTA 任务未启动 / 防火墙 / 网络不通 | 检查设备是否正常运行,确认 IP 地址和端口 |
| Header ACK 错误 | 固件大小为 0 / Magic 不匹配 | 检查 update.fwpkg 是否完整 |
| UPG write 失败 | 空间不足 / Flash 错误 | 检查 UPG 分区大小,确认 Flash 正常 |
| 校验失败 (0x01) | 签名密钥不匹配 | 重新运行 gen_all_key.sh,重新全量烧录 .fwpkg |
| 设备未重启 | UPG request_upgrade 失败 | 检查 uapi_upg_verify_file() 返回值 |
| OTA 成功但固件没变化 | 修改代码后没有重新编译 | 打包脚本只读取已编译的 .bin,不会自动编译。必须先执行 python build.py ws63-liteos-app |
校验失败详细排查
如果最终 ACK 返回 0x01(校验失败),串口日志会打印类似:
1 | [OTA] uapi_upg_verify_file failed, ret=0x800030xx (UPG_XXX) |
常见错误码:
0x80003040: UPG_NOT_INIT — UPG 未初始化0x80003044: UPG_INVALID_IMAGE_ID — 镜像 ID 不匹配0x80003048: UPG_FLASH_ERASE_ERROR — Flash 擦除错误0x80003051: UPG_NO_ENOUGH_SPACE — 空间不足
最可能的原因:Bootloader 公钥与新私钥不匹配。
确认步骤:
- 检查
fota.cfg中的RootKeyFile和SubKeyFile路径是否正确 - 确认
sign_config/下的.pem文件是最新生成的 - 重新全量烧录
ws63-liteos-app_all.fwpkg(这是最关键的步骤) - 重新生成
update.fwpkg - 再次 OTA 推送
6. LiteOS OTA 与 OpenHarmony OTA 的对比
6.1 相同点
| 相同点 | 说明 |
|---|---|
| 签名校验 | 都需要使用公私钥对升级包进行签名,设备端用公钥校验,防止刷入非法固件 |
| 分区备份机制 | 都有分区/镜像备份的概念,升级失败时可以回滚到旧版本 |
| 四步基本流程 | 都遵循”制作升级包 → 下载/推送 → 校验 → 重启生效”的基本流程 |
| 全量升级支持 | 都支持全量升级,这是最基础的升级方式 |
6.2 不同点
| 对比项 | LiteOS OTA(本文实现) | OpenHarmony 标准系统 OTA |
|---|---|---|
| 操作系统 | LiteOS(轻量级实时操作系统) | OpenHarmony 标准系统(类 Linux 多进程系统) |
| 系统架构 | 单进程/单任务,应用直接操作硬件 | 多进程、多服务,有 init、SA、IPC 等机制 |
| 升级入口 | C 语言直接调用 UPG 接口 | JS API → update_service SA → 系统安装服务 |
| 分区设计 | Flash 划分为 Running / Backup / Download / Flag 等区域 | 独立的 updater 分区、misc 分区,与主系统隔离 |
| 启动流程 | Bootloader 直接切换分区并跳转 | uboot 读取 misc 分区指令,加载 updater.img 进入升级模式 |
| 网络协议 | 由应用层自行实现(本文为方便采用 TCP 局域网推送) | 内置 HTTPS 下载,对接厂商 OTA 服务器 |
| 差分升级 | 本文实现不支持(UPG 模块本身不处理差分) | 支持(bsdiff/imgdiff 差分算法) |
| UI 界面 | 无 | 有升级进度 UI 界面 |
| 服务器依赖 | 局域网 Python 推送脚本即可 | 需要搭建搜包服务器 + 下载服务器 |
| 适用设备 | 资源受限的 MCU、模组 | 富设备、开发板、手机等 |
下图展示了 OpenHarmony 标准系统 updater 模式的启动流程,可以看到其需要 uboot 读取 misc 分区指令、加载 updater.img、启动 init 服务等一系列步骤:
下图则是 OpenHarmony 标准系统升级子系统的整体架构,包含了 updater_service、updater、公共库、三方库等多个层级,远比本文的 UPG 轻量接口复杂:
参考文章
| 文章 | 链接 | 说明 |
|---|---|---|
| [经验分享] OTA升级开发指导 | https://forums.openharmony.cn/forum.php?mod=viewthread&tid=900&extra=page%3D1&mobile=no | OpenHarmony 标准系统 updater 模式适配指南 |
| OTA升级 - openharmony官方文档 | https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-ota-guide.md#%E7%94%9F%E6%88%90%E5%85%AC%E7%A7%81%E9%92%A5%E5%AF%B9 | 官方文档,覆盖标准系统与轻量/小型系统 |
| 如何实现OpenHarmony的OTA升级? | https://cloud.tencent.com/developer/article/2516696 | 基于 RK3568 的标准系统 OTA 实践 |
| 漫谈LiteOS之OTA 方案概述 | https://bbs.huaweicloud.com/blogs/180966 | LiteOS IoT Link SDK 的 OTA 方案(HDIFFPATCH + LZMA) |
| 漫谈LiteOS-Huawei_IoT_Link_SDK_OTA 开发指导 | https://www.cnblogs.com/hwiot/p/12745773.html | LiteOS 基于 LwM2M 的 FOTA/SOTA 开发指南 |








