7.8 OTA 远程升级组件
7.8 OTA 远程升级组件
📚 本节导读
学习时长:约 30 分钟
难度级别:⭐⭐⭐☆☆
前置知识:第 7.2 节网络协议栈、Flash 分区概念
🎯 学习目标
- 理解 OTA 差分升级的原理和优势
- 掌握 cmiot API 的使用方法
- 了解三种下载方案(general/cms/ctwing)
- 学会配置 OTA 组件和打包固件
- 能够排查常见升级错误
一、OTA 概述
OTA(Over The Air,远程升级) 通过无线网络实现固件升级。OneOS OTA 组件位于 components/ota/cmiot/,README.md 提供了完整的 API 说明和打包命令,Kconfig 配置位于同目录。核心源文件 source/ 下包含 cmiot 升级逻辑和 LUSUN/Wosun 差分算法实现。采用差分升级方式,可极大降低升级包大小。
1.1 系统架构
┌─────────────────────────────────────────┐
│ OTA 云平台 │
│ 版本管理 │ 差分包制作 │ 推送策略 │
└──────────────────┬──────────────────────┘
│ HTTP / CoAP
┌──────────────────┴──────────────────────┐
│ OneOS 设备端 │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ BootLoader │ │ App (固件) │ │
│ │ - 固件还原 │ │ - 更新检测 │ │
│ │ - 固件升级 │ │ - 固件下载 │ │
│ │ - 安全校验 │ │ - 版本校验 │ │
│ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────┘1.2 差分算法支持
| 算法 | RAM 要求 | 适用场景 |
|---|---|---|
| LUSUN | 较小 | 资源受限设备(默认) |
| Wosun LOW | > 200KB | 中等资源设备 |
| Wosun HIGH | > 512KB | 高资源设备 |
| Luckpand | 按需 | 多文件差分升级(单个文件可指定全量) |
关键理解:BootLoader 与 App 需保持差分算法一致(升级时 BootLoader 用同算法还原差分包)。使用 Wosun 算法时还需配置
App part addr from fal's OS_APP_PART_ADDR与Download part size from fal's OS_DL_PART_SIZE(见 4.2)。
1.3 芯片支持情况
STM32F091 / STM32F103 / STM32F407 / STM32F429 / STM32F469 / STM32F767 / STM32F769 / STM32L475 / STM32F496 等均支持 LUSUN 差分算法。
二、核心 API
2.1 API 列表
| API | 说明 | 调用方式 |
|---|---|---|
cmiot_get_network_type() | 获取网络类型 | 无需调用(OTA 自动调用) |
cmiot_get_uniqueid() | 获取设备 MID(最大 30 字节) | 无需调用 |
cmiot_app_name() | 获取 APP 分区名 | 无需调用 |
cmiot_download_name() | 获取 DOWNLOAD 分区名 | 无需调用 |
cmiot_printf() | 日志输出接口 | 无需调用(可重写) |
cmiot_msleep() | 延时接口 | 无需调用(可重写) |
cmiot_reboot_device() | 重启设备 | 无需调用(可重写) |
cmiot_upgrade() | 开始检测并下载固件包 | 用户调用 |
cmiot_report_upgrade() | 上报升级结果 | 用户调用 |
cmiot_upgrade_slave() | 从设备开始检测下载 | 多设备方案 |
cmiot_report_upgrade_slave() | 从设备上报升级结果 | 多设备方案 |
2.2 全部 API 函数原型
以下函数原型来自官方 README.md(components/ota/cmiot/):
/* 获取网络类型(无需调用,可重写) */
cmiot_char *cmiot_get_network_type(void);
/* 获取端侧 MID,最大 30 字节(无需调用) */
void cmiot_get_uniqueid(cmiot_char *uid);
/* 获取 APP 分区名(无需调用) */
cmiot_char *cmiot_app_name(void);
/* 获取 DOWNLOAD 分区名(无需调用) */
cmiot_char *cmiot_download_name(void);
/* 日志输出接口(无需调用,可重写) */
void cmiot_printf(cmiot_char *data, cmiot_uint32 len);
/* 延时接口,单位毫秒(无需调用,可重写) */
void cmiot_msleep(cmiot_uint32 time);
/* 获取当前时间,单位毫秒(无需调用,可重写) */
cmiot_uint32 cmiot_get_now_time(void);
/* 重启设备(无需调用,可重写) */
void cmiot_reboot_device(void);
/* 开始检测下载固件包(单设备 / 多设备-主设备) */
cmiot_int8 cmiot_upgrade(void);
/* 上报升级结果(单设备 / 多设备-主设备) */
cmiot_int8 cmiot_report_upgrade(void);
/* 多设备方案:从设备开始检测下载固件包 */
cmiot_int8 cmiot_upgrade_slave(cmiot_int16 slave_num);
/* 多设备方案:从设备上报升级结果 */
cmiot_int8 cmiot_report_upgrade_slave(cmiot_int16 slave_num);返回值常量:
| 常量 | 含义 |
|---|---|
E_CMIOT_SUCCESS | 有包且下载成功 / 有升级结果且上报成功 |
E_CMIOT_FAILURE | 下载失败 / 有升级结果但上报失败 |
E_CMIOT_NOT_INITTED | OTA 组件初始化失败(一般是内存不够) |
E_CMIOT_LAST_VERSION | 没有新的固件包(仅 upgrade 系) |
E_CMIOT_NO_UPGRADE | 没有升级结果(仅 report 系) |
2.3 核心函数详解
cmiot_upgrade —— 开始检测下载固件包
cmiot_int8 cmiot_upgrade(void);| 返回值 | 含义 |
|---|---|
E_CMIOT_SUCCESS | 有包且下载成功 |
E_CMIOT_FAILURE | 下载失败 |
E_CMIOT_NOT_INITTED | OTA 组件初始化失败(内存不够) |
E_CMIOT_LAST_VERSION | 没有新的固件包 |
分步详解:
第一步:判断返回成功(条件 ret == E_CMIOT_SUCCESS)
ret = cmiot_upgrade();
if (ret == E_CMIOT_SUCCESS) {
cmiot_reboot_device(); /* 下载成功,重启升级 */
}E_CMIOT_SUCCESS 表示固件包已下载到 DOWNLOAD 分区,可重启进入 BootLoader 进行差分还原与升级。
第二步:处理其它返回值(条件分支)
else if (ret == E_CMIOT_LAST_VERSION) {
/* 已是新版本,无需升级 */
} else {
/* 下载失败 / 初始化失败,可重试 */
}cmiot_report_upgrade —— 上报升级结果
cmiot_int8 cmiot_report_upgrade(void);| 返回值 | 含义 |
|---|---|
E_CMIOT_SUCCESS | 有升级结果且上报成功 |
E_CMIOT_FAILURE | 有升级结果但上报失败 |
E_CMIOT_NO_UPGRADE | 没有升级结果 |
E_CMIOT_NOT_INITTED | OTA 组件初始化失败 |
关键理解:cmiot_upgrade/cmiot_report_upgrade(单设备/主设备)无参数;从设备用 cmiot_upgrade_slave(cmiot_int16 slave_num)/cmiot_report_upgrade_slave(slave_num)(带从设备标记)。cmiot_get_now_time 返回毫秒、cmiot_get_network_type 返回网络类型字符串。
三、下载方案
3.1 三种方案对比
| 方案 | 下载方式 | 适用场景 |
|---|---|---|
| general | HTTP / CoAP 直连 OTA 平台 | 直接对接 OTA 平台(多设备方案仅支持此方案) |
| cms | 通过 CMS 中转 | 已使用 CMS 平台的设备(支持 HTTP/CoAP) |
| ctwing | 通过 Ctwing 转发(仅 CoAP) | 使用 Ctwing 平台的设备 |
关键理解:general 和 cms 方案支持 HTTP 与 CoAP 两种下载方式;ctwing 方案仅支持 CoAP;多设备方案目前仅支持 general(详见官方
doc/README_MULTI.md)。
3.2 设备类型
| 类型 | 说明 |
|---|---|
| 单设备 | 独立设备,自行检测和下载升级 |
| 多设备-主设备 | 负责检测下载,协调从设备升级 |
| 多设备-从设备 | 接收主设备指令,执行升级 |
四、Kconfig 配置
4.1 Boot 配置
(Top) → Drivers → Boot → CORTEX-M Boot → Board boot configure
(0x08000000) text section addr # APP 代码起始地址
(0x00080000) text section size # APP 代码区大小
(0x20000000) data section addr # 数据区起始地址
(0x00008000) data section size # 数据区大小4.2 OTA 组件配置
(Top) → Components → OTA → Fota by CMIOT
[*] Enable fota by cmiot
The supported device type (single device) --->
The supported download solution (general) --->
The supported network protocol (Http) --->
The supported algorithm (Lusun) --->
(200) The network timeout # 网络超时(ms)
(3) Segment size index # 分块大小索引配置项说明:
- device type:单设备 / 多设备主设备 / 多设备从设备;
- download solution:general / cms / ctwing(多设备仅 general);
- network protocol:HTTP / CoAP(ctwing 仅 CoAP);
- algorithm:LUSUN / WOSUN LOW / WOSUN HIGH / Luckpand(BootLoader 与 App 需一致);
- network timeout:网络超时(毫秒),易超时需增大;
- segment size index:每次下载的分块大小索引。
关键理解(额外配置):选择 Wosun 算法时,还需配置
App part addr from fal's OS_APP_PART_ADDR(如0x8020000)与Download part size from fal's OS_DL_PART_SIZE(如0x20000)——把 FAL 的OS_APP_PART_ADDR/OS_DL_PART_SIZE算出的值填入,这是平台制作差分包所需信息。选择 Luckpand 算法时,需配置多个文件名与地址(First/Second file name、First/Second file address base、Use full、More file),其中单个文件可指定不用差分(Use full)改用全量。
4.3 平台相关配置
(cmiot_cd) Oem # OEM 标识
(F103RE) Model # 设备型号
(1593394730) Product id # 产品 ID
(e7f48e3ea6c3417d82edfe66b1305ac8) Product secret # 产品密钥
(box) Device type # 设备类型
(STM32F1) Platform # 平台名称
(5.0) Firmware version # 固件版本号注意:
Firmware version改变后编译会生成对应版本的软件包,可在 OTA 平台上传制作差分包。
五、固件打包命令
5.1 打包命令格式
# 单 axf 文件打包
cmiot_pack.bat <fromelf路径> <压缩软件路径> oneos_config.h <axf文件路径>
# 单 bin 文件打包
cmiot_pack.bat <压缩软件路径> oneos_config.h <bin文件路径>
# 多 bin 文件打包
cmiot_pack.bat <压缩软件路径> oneos_config.h <bin文件夹路径>5.2 打包示例
# 使用 fromelf + WinRAR 打包
..\..\components\ota\cmiot\source\pack\cmiot_pack.bat ^
"D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe" ^
"C:\Program Files\WinRAR\WinRAR.exe" ^
oneos_config.h ^
build\keil\Obj\oneos.axf
# 使用 WinRAR 打包 bin 文件
..\..\components\ota\cmiot\source\pack\cmiot_pack.bat ^
"C:\Program Files\WinRAR\WinRAR.exe" ^
oneos_config.h ^
..\..\out\stm32l475-atk-pandora\oneos.bin注意:压缩软件支持 WinRAR.exe 和 7z.exe。路径需替换为个人电脑实际路径。
六、使用示例
#include <cmiot_user.h>
/* 单设备 OTA 升级示例 */
void ota_upgrade_task(void)
{
cmiot_int8 ret;
/* 检测并下载固件包 */
ret = cmiot_upgrade();
if (ret == E_CMIOT_SUCCESS) {
os_kprintf("Firmware downloaded, rebooting...\r\n");
cmiot_reboot_device(); /* 下载成功,重启升级 */
} else if (ret == E_CMIOT_LAST_VERSION) {
os_kprintf("Already the latest version\r\n");
} else {
os_kprintf("Upgrade failed: %d\r\n", ret);
}
/* 上报升级结果 */
ret = cmiot_report_upgrade();
if (ret == E_CMIOT_SUCCESS) {
os_kprintf("Upgrade result reported\r\n");
}
}七、常见错误码
| 错误码 | 含义 | 排查建议 |
|---|---|---|
| 1001 | 项目 ID 与平台不一致 | 检查 Product id 配置 |
| 1002 | 项目不存在 | 检查 Oem/Model/Device type/Platform 配置 |
| 1005 | Flash 操作失败 | 检查 Flash 驱动和硬件 |
| 1008 | MID 长度错误 | MID 需 4-31 个字符 |
| 1010 | 设备访问上限 | 单日访问量达到上限 |
| 1015 | 用户访问上限 | 所属用户单日访问量达到上限 |
| 2001 | 签名不对 | 检查 Product secret,擦除 Flash 下载分区 |
| 2101 | 没有新版本 | 当前已是最新版本 |
| 2103 | 设备未注册 | 擦除 Flash 下载分区 |
八、FAQ
Q:OTA 组件资源占用大吗?
A:FLASH 占用 9KB 以内,RAM 占用 3KB 以内(含 RW、ZI、堆、栈)。
Q:cmiot_upgrade() 该怎么调用?
A:OTA 组件只提供机制,策略由用户决定。可以每天检测一次,也可以每周检测一次;可以下载完就重启升级,也可以等业务完成再重启。
Q:调用超时怎么办?
A:在 menuconfig 中修改 The network timeout,适当增大超时时间。
Q:下载中断后需要重新下载吗?
A:不需要,支持断点续传,会从中断处继续下载。
📝 本节小结
核心要点回顾
- 定位:OTA(远程升级)组件分 BootLoader + App 两部分,BootLoader 负责固件还原/升级/安全校验,App 负责更新检测/固件下载/版本校验;源码位于
components/ota/cmiot/。 - 差分升级:支持 LUSUN(默认,资源受限)、Wosun LOW(>200KB)、Wosun HIGH(>512KB)、Luckpand(多文件)四种算法,大幅减小升级包;BootLoader 与 App 算法需一致。
- 三种方案:general(HTTP/CoAP 直连)、cms(CMS 中转)、ctwing(仅 CoAP);多设备仅支持 general。
- 核心 API:
cmiot_upgrade()(检测下载)/cmiot_report_upgrade()(上报结果),从设备用cmiot_upgrade_slave(slave_num)/cmiot_report_upgrade_slave;其余cmiot_get_network_type/get_uniqueid/app_name/download_name/printf/msleep/get_now_time/reboot_device为可重写接口(无需主动调用)。 - 返回值:
E_CMIOT_SUCCESS/FAILURE/NOT_INITTED/LAST_VERSION(upgrade 系)/NO_UPGRADE(report 系)。 - 配置:Boot 分区(text/data addr+size)、
Components → OTA → Fota by CMIOT(device type/download solution/network protocol/algorithm/network timeout/segment size index)、平台参数(Oem/Model/Product id/Product secret/Device type/Platform/Firmware version);Wosun 需额外配置 APP/DL 分区地址。 - 打包:
cmiot_pack.bat <fromelf> <压缩软件> oneos_config.h <axf/bin/文件夹>,支持 WinRAR/7z。 - 断点续传:下载中断后从断点继续,无需重新下载。
关键易错点速查
| 易错点 | 正确做法 |
|---|---|
| BootLoader 与 App 差分算法不一致 | 升级还原失败 → 两端算法必须一致(LUSUN/Wosun/Luckpand) |
| 用 Wosun 却忘配 APP/DL 分区地址 | 平台做差分包缺信息 → 填 App part addr/Download part size(OS_APP_PART_ADDR/OS_DL_PART_SIZE) |
| 多设备方案用 cms/ctwing | 多设备仅支持 general → 多设备选 general |
| ctwing 方案配 HTTP | ctwing 仅 CoAP → 选 CoAP |
cmiot_upgrade_slave() 不带参数 | 从设备接口带 slave_num → 用 cmiot_upgrade_slave(slave_num) |
改 Firmware version 不重新编译打包 | 版本号不生效 → 改后重编译生成对应版本软件包再上传 |
| 串口下载 buffer 太小 | 升级包出错 → 串口通信 buffer 建议 512 或更大,接收丢失配 RX DMA |
| 超时调用 | The network timeout 太小 → 按网络环境增大 |
| 报 1008 MID 长度错误 | MID 需 4~31 字符 → 检查 cmiot_get_uniqueid 返回长度 |
| 报 2001 签名不对 | Product Id/secret 不一致或 DOWNLOAD 分区脏 → 对比配置,必要时擦除 DOWNLOAD 分区 |
| 忘记 BootLoader 配置 | App 跳转失败 → 先按 Drivers → Boot → Board boot configure 设置 text/data addr+size |
不调用 cmiot_reboot_device | 下载完不重启无法升级 → 下载成功(E_CMIOT_SUCCESS)后调用重启 |
下一步
- 本节已完整覆盖 OTA 远程升级。可回到 7.7 DBoT 分布式物联总线 巩固设备互联;下一章"综合实战"(如"8.1 温湿度采集与 MQTT 上云")将把设备联网、数据上报与 OTA 组合成完整产品实践。