当前位置:首页 > 财经百科 > LuatOS FOTA升级:新手也能秒懂的操作指南

LuatOS FOTA升级:新手也能秒懂的操作指南

面对成千上万的终端设备,如何低成本完成版本迭代?FOTA是答案。本文聚焦LuatOS平台的远程升级能力,以极简语言和清晰步骤,带你走过从创建项目、获取校验码到设备重启升级的全过程,真正实现“极速上手”。

一、Flash 分区与 FOTA 分区简介

大家好,在开始动手操作 FOTA 之前,我们必须先了解它的“工作场地”——模组内部的 Flash 存储器。这就像装修房子前,一定要先看懂户型图。

不同型号的模组,Flash 分区就像不同的“户型”,格局各异,但都有一个核心设计:必须有一个独立的“系统升级专用间”,也就是 FOTA 分区。

今天,我们就以 Air780EHM 为例,来详细解读这份关键的“Flash 户型图”。

wKgZPGmCvx2AfBGVAAKdaf0OKsw654.png

首先,我们看全局。Air780EHM 的 Flash 是一栋总容量 8MB 的“大楼”。但这里有个精妙的设计:这栋楼有两个门牌号系统。

物理地址(0x0 – 8MB):这是 Flash 芯片真实的“砖块地址”,用于存储所有数据。

XIP 映射地址(8MB – 16MB):CPU 执行代码时“看到”的地址。系统固件被“映射”到这个区域,CPU 可以直接读取执行,这叫 XIP(就地执行)。

简单来说,固件实体存放在物理地址的低 8MB,而当 CPU 要运行时,它“看到”的是从 8MB 开始的那段地址。

大家可以看到,flash 中分区很多,但作为 LuatOS 开发者,我们主要关注其中四个“房间”:ap+cp image 分区(包含脚本代码区)、LFS 分区、KV 分区和 FOTA 分区

1.1 ap+cp image 分区– “主系统套房”:

作用 :这是设备的操作系统和核心应用所在,相当于电脑的 C 盘。它包含了底层 C 固件、Lua 虚拟机,以及最重要的——脚本区。

脚本区:这是我们 Lua 代码的“家”。代码在这里拥有持久化特性,只有固件升级时才会被整体更新。它的大小在编译时就已经固定,不同固件版本会有差异,

脚本区大小会根据模组型号不同,固件编号不同也会有差别,大小在编译时固定

1.2 LFS 分区– “用户资料室” :

作用:提供一个小型文件系统,用于存储用户数据和配置。

特点:具有持久化存储,断电不丢失 、可动态写入和读取、支持文件系统 API 访问、可随时创建和删除的特性。可以用来储存用户配置文件、设备运行日志、网络配置信息、临时数据和缓存、用户生成的文件。

文件系统空间大小会根据模组型号不同,固件编号不同也会有差别,实际大小= 基础 lfs 空间 + 附件空间。

附加空间通常是去掉某些功能所节省出来的空间,所以有了不同编号的固件,来实现不同功能 + 大内存的需求。

1.3 KV 分区– “配置存储间”:

作用:KV(Key-Value)分区是一个小型的键值对存储区域,用于保存设备的配置信息和状态数据。

特点:

持久化存储:断电后数据不丢失

高效访问:支持快速的键值对读写操作

固定大小:通常为 64KB

轻量级:适合存储少量关键配置,如网络参数、设备 ID、运行状态等

KV 分区的典型用途:

存储设备唯一标识符

保存网络连接参数(APN、服务器地址等)

记录设备运行状态和统计信息

存储用户自定义配置

保存升级状态和版本信息

1.4 FOTA 分区“系统升级专用间”:

1.4.1 分区介绍

作用 :这是今天的主角,用于临时存放固件升级包,实现安全、隔离的远程升级。

fota 分区具有以下特性:

1、独立空间 :与其他分区严格分离,确保升级安全,从网络或其他渠道下载的完整固件包或差分包先存储到 FOTA 分区

2、安全机制 :支持升级包完整性校验和签名验证,升级包写入 FOTA 分区后会进行 MD5 校验,确保传输未损坏

FOTA 升级的核心安全规则:为什么“户型”要对齐?

这里有一个至关重要的安全限制,可以通过下面这个表格来了解:

通过上面表格可以看出,虽然两个固件的 FOTA 分区大小完全一样,但它们的 fota 分区、ap+cp 分区和 LFS 分区的起始、结束地址发生了偏移。

结论:使用错误编号的固件包升级时,其设计的分区地址与设备当前分区布局不匹配。FOTA 机制会在升级包写入前校验升级包,将会检查出完整性异常,并返回升级失败。

所以会有一个铁律:不同编号的固件之间不能进行 fota 升级,FOTA 差分升级只能在相同编号的固件版本之间进行。 既编号 1 固件只能差分升级为新版本的编号 1 固件,编号 2 固件只能差分升级为新版本的编号 2 固件。在制作升级包时,务必首先确认这个编号匹配关系。

如果不同编号固件间升级 :需要使用完整包重新烧录,不能使用 fota 升级

虽然这个房间总大小 1048KB,但是 fota 升级包大小并不等于 fota 分区空间大小。

在不同编号的固件版本规划中,为了容纳不同的功能,其整个 Flash 的分区表布局(包括 FOTA、AP、CP、LFS 等分区的起始地址和大小)是预先定义好且固定的。因此,不同编号的固件,其 FOTA 分区大小可能相同也可能不同,但更关键的是其在整个 Flash 中的"位置"(地址)不同。所以具体 fota 分区大小可以看 1.5 章节具体表格。

1.4.2 升级流程

初始化 FOTA → 下载升级包 → 写入 FOTA 分区 → MD5 校验 → 设置升级标志 → 重启

重启后:Bootloader 检查升级标志 → 验证升级包 → 写入目标分区 → 启动新固件 → 验证新固件

wKgZPGmC0L2AWtPdAAErH0G7Ffs017.pngwKgZPGmC0QeAKD7eAACAk_Lp5VA726.png

1、升级包写入流程:

初始化 FOTA 模块: 创建升级上下文结构体,分配必要的缓冲区和资源,为升级流程做准备。

下载升级包:

通过 HTTP、MQTT 或 UART通信协议从服务器或本地下载升级包数据。支持下载到文件系统,或者直接分包写入 fota 分区。

写入 FOTA 分区:

如果是下载到文件系统中暂存,待整个升级包在文件系统中下载完成后,再一次性从文件系统读取并写入到专用的 FOTA 分区。

也可以实时分包接收升级包数据并写入到文件系统中。

完成写入,验证升级包:

执行升级包的完整性校验(MD5)

若校验通过,则在特定位置中设置升级标志,并重启系统。Bootloader 下次启动时执行升级。

若校验失败,则报告错误并终止升级流程。

2、重启后升级流程:

设备上电,执行 Bootloader

设备重启后,Bootloader 首先检查是否存在有效的升级标志。

验证与执行升级 若无升级标志:Bootloader 跳过升级流程,直接启动现有固件。 若存在升级标志:

1.5 各模组 fota 分区空间

在实际使用中,不同的固件版本或型号为了支持不同的功能,会调整 FOTA 分区内的大小,因此用户实际可用的升级空间可查看下表。

Air7xx 系列模组 LuatOS 多固件版本

wKgZPGmC0bGATy1QAACPPX52ZKs938.pngwKgZPGmC0dKAUxDqAACju2s0nJY452.png

Air8000 系列固件版本

wKgZO2mC0kSAYGvLAACfdUR5qQs614.png

二、soc 软件包文件分析

“理解了 FOTA 的‘工作场地’(Flash 分区),接下来一个很自然的问题是:我们要通过网络下载并写入这个场地的‘升级包’,它到底是什么?

这个核心的升级包就是 .soc 文件。它不是一个普通的固件,而是合宙定义的标准化容器。在 FOTA 流程中,它扮演着三个关键角色:

它是差分的蓝本:我们下一章要讲的‘差分升级’,其核心就是对比新旧两个版本的.soc 文件,生成差异部分。

它携带了分区地图:.soc 包内的info.json配置文件,明确写明了本固件的编号、分区地址等信息,这是 FOTA 进行安全校验(防止变砖)的核心依据。

它统一了操作对象:无论底层是移芯还是展锐芯片,最终都封装成统一的.soc 格式,让升级工具和流程得以标准化。

所以,拆解.soc 文件,就是理解 FOTA 操作对象和实现原理的基础。明白了它里面有什么,你才能彻底搞懂后续的差分生成和升级执行究竟在操作什么。”

下面讲解一下模组所用固件包.soc 文件的组成以及格式相关内容。

2.1 soc 简介

.soc 文件,是我们在 2021 年自定义的一个文件,该文件, 用于用一种通用的格式,保存不同型号模组的固件。

该文件格式的优点是可以屏蔽模组差异, 对用户通用, 对Luatools 通用;

缺点是,当前的 IOT 升级后台尚且无法支持这个格式。

对于远程升级来说, 必须用 Luatools 内置的差分包制作工具, 输入两个不同版本的 soc文件, 生成一个.bin 结尾的差分包,才能上传到 IOT 后台进行远程升级。

LuatOS 将来会适配非常多的 SoC/MCU 芯片来设计模组, 而各种模组的刷机格式各不相同,有必要定义一个统一的对外格式。

这里称之为 soc 格式, 后缀选定为 soc, 实际内容为 info.json 及多个固件文件的压缩包。

2.2 SoC 组成部分

[必选]包含的文件 info.json

[可选]脚本数据存储文件 script.bin, 使用 LuaDB v2 格式存储

[必选]原始固件,以不同 SoC 芯片为准

2.2.1 info.json 的内容

info.json 是固件配置文件,包含固件的详细参数和配置信息。以 780EHM 的 2018 版本 1 号固件解压后打开 info.json,具体包含如下参数:

wKgZPGmC06KAAio-AAdSrFf6fjw243.png

2.2.2 脚本数据文件 script.bin

在给出的底层固件中并不包含此文件,此文件是在 luatools 工具点击生成量产文件后所生成的。是 Lua 脚本的二进制文件,包含了用户编写的应用程序代码。文件格式为 LuaDB 格式存储。

LuaDB 并非数据库, 而是一种用于 LuatOS 固件的文件打包格式.

其作用相当于一个只读文件系统.

2.2.3 原始固件

原始固件是构成 .soc 文件的核心数据部分,指由芯片原厂(如展锐、移芯)提供的底层系统映像文件。其格式依芯片平台而定,常见后缀包括 .pac、.binpkg、.fls、.img 等。该固件包含了操作系统内核、基础驱动、通信协议栈等核心代码,是设备功能运行的基石。

在 .soc 文件体系中,info.json 配置文件内的 "rom" -> "file" 字段即指明了所包含的原始固件文件名(例如 "luatos.binpkg")。该文件与 info.json、可选的 script.bin 一同被打包压缩,最终形成统一的 .soc 分发文件。

展锐平台:

展锐平台的 4G 模组,当前我们用的是 8910 平台,典型模组是Air724UG,编译之后的文件后缀为 PAC, 可以用展锐提供的upgrade 固件烧录工具烧录,也可以用 Luatools 烧录。

移芯平台:

移芯平台的 4G 模组,我们用的有 EC718、EC618、716 等。典型模组是 Air780EXX 系列、Air8000 系列。

移芯平台编译出来的固件,都是 binpkg 后缀结尾的固件,可以用移芯提供的 flashtool 工具烧录,也可以用 Luatools 烧录。

对于最终用户和开发者而言,无论底层原始固件格式如何,均推荐使用 Luatools 配合 .soc 文件进行下载和升级。

所以,.soc文件的核心就在于,用一个外层的标准化包装,封装了内部不同平台的原生固件,让用户和工具都能用同一种方式处理它们。

2.3 soc 实际文件分析

soc 解压后实际文件在不同的模组中也不尽相同,但是都是 info.json+ 原始固件 + 辅助文件 + 脚本数据文件组成。

下面以移芯系列固件为例:将 .soc 压后会得到 7 个核心文件: comdb.txt 、 info.json 、 luat_conf_bsp.h 、 luatos.binpkg 、 luatos.elf 、 luatos_debug.map 、 mem_map.txt 。而使用 Luatools 生成量产文件后,还会多出两个文件: core.binpkg 和 script.bin 。

这些文件各司其职,共同构成了一个完整的固件系统。接下来,我们逐个分析它们的作用。

还是以 780ehm 为例,看下它的固件 soc 解压后都有哪些文件:

wKgZPGmC1DuAU--nAACL8AXBHxw161.png

info.json

第一个要介绍的是 info.json ,这是固件的核心配置文件,相当于固件的"身份证"。

这个文件主要包含有:

芯片类型(

ROM 文件信息及下载地址

脚本文件信息及下载地址

分区表配置

下载参数(波特率、强制波特率等)

luatos.binpkg

LuatOS 的核心二进制固件包,包含了系统运行所需的所有核心代码。

.binpkg 为移芯系列芯片所生成的原始固件内容,是移芯芯片特有的格式,不同的芯片会有不同的原始固件格式

其他辅助文件

mem_map.txt:这是设备内存的"地图",内存映射配置文件,定义了设备内存的分区结构和地址范围。

luat_conf_bsp.h :支持功能配置头文件,定义了一些功能相关的配置宏,比如引脚定义、外设配置等。

luatos.elf :包含完整调试信息的可执行文件,开发通常用不到,出现 ramdump 死机的时候用于解析死机日志时使用。

luatos_debug.map :调试映射文件,记录了函数和变量在内存中的地址。

comdb.txt :组件数据库文件,记录了固件中包含的各种组件信息,使用 EPAT 抓取日志时需要。

在 luatools 上点击生成量产文件后,量产文件也是一个 soc 文件,解压后除了上述内容外还会多出两个文件:core.binpkg、script.bin

core.binpkg :核心固件包,包含了系统的核心功能。

script.bin :Lua 脚本的二进制文件,包含了用户编写的应用程序代码。这是一个非常重要的文件,它支持单独更新脚本,无需重新下载完整固件。

总的来说,.soc 文件就是一个集配置文件、核心固件、用户脚本于一体的标准化容器。它解决了多平台统一管理的问题,并通过 info.json 实现精细控制,实现固件烧录、差分升级等功能。为后续的差分升级打下了坚实的基础。那么,如何利用两个不同版本的.soc 文件,生成一个体积小巧的升级包呢?这就是我们接下来要揭秘的——差分升级的奥秘

三、差分包生成原理以及差分升级原理

3.1 差分升级的基本概念

在 FOTA(无线固件升级)中,升级方式主要分为两种:整包升级和差分升级。

整包升级:指将完整的新版本固件包全部下载至设备,并完全覆盖设备上现有的旧版本固件。其过程直接,但数据量大。

差分升级:是一种增量更新技术。其核心在于,设备无需下载完整的新固件包,而是仅下载新旧两个固件版本之间的差异部分(即“差分包”)。设备在本地利用此差分包与自身已有的旧版本固件进行合并,从而生成完整的新版本固件。

一个生动的比喻是:你手中有一本旧版的书籍,出版社发布了修订版,但实际内容只更改了其中的 30 页。此时,你无需购买整本新书,只需获取这 30 页的修订页,并替换旧书中对应的页面即可。差分升级正是这一高效思路在固件更新上的体现。

传统整包升级的痛点:

升级包体积大:消耗大量网络带宽与设备存储空间。

升级耗时长:下载时间长,升级过程慢,影响用户体验。

流量成本高:对于部署量庞大的物联网设备群,升级产生的总流量成本非常显著。

差分升级的显著优势:

体积极小:差分包通常仅为完整新固件包的 10%-30%,甚至更低。

速度极快:下载时间大幅缩短,升级效率显著提升。

可靠性更高:传输数据量小,在弱网环境下传输失败或出错的概率降低。

成本大幅节省:在海量设备升级场景下,能节省可观的流量费用与服务器带宽成本。

3.2 差分升级原理:只传"差异",不传"全部"

差分升级的技术本质可概括为 “计算差异、传输差异、应用差异”。其核心在于通过算法比对,仅处理和传输发生变化的数据块,而非整个文件。

具体来说,在升级前,会使用专门的算法工具(luatools),对旧固件(V1)和新固件(V2)的二进制内容进行深度比对,精确找出所有被修改、新增或删除的数据块。然后,只将这些“差异”信息,打包成一个结构化的差分包(Δ)。设备获取这个小包后,在本地执行反向操作,根据包内的指引,将差异应用到自身的 V1 版本上,从而重构出 V2。

1. 差分包生成原理

差分升级的核心是 差异比较算法 ,常用的有:

BSDiff :基于后缀排序的高效差异算法

HDiffPatch :高性能的差异比较库

Rsync 算法 :用于网络传输的差异算法

我们的 Luatools 等工具就集成了这类算法,能够智能地比较两个.soc 文件或原始固件,生成最优的差分包。

2. 差分包的组成

一个标准的差分包(通常为.bin 文件)是一个精心设计的数据包,通常包含::

差异数据 :新旧固件的二进制差异

元信息 :版本号、校验值、生成时间

合并指令 :指导设备如何合并生成新固件

校验机制 :确保差分包完整性和安全性

3. 差分升级的工作流程

下面看一下差分升级的工作流程,具体流程如下:

wKgZO2mC1XyACP5GAACuaMsgijA663.png

流程详解:

1、生成阶段:在 luatools 上,指定新旧版本 V1 和 V2,生成一个包含合并指令和差异数据的差分包(Δ)。

2、传输阶段:这个极小的差分包通过蜂窝网络、以太网蓝牙Wi-Fi 或本地串口等渠道,高效地下发到设备。

3、应用阶段:设备端在设备的固件升级功能模块控制下,完成差分包校验、新固件合并写入与最终验证后,重启切换至新版本固件完成升级的过程。

四、单脚本升级以及 core+ 脚本升级 升级包制作

4.1 LuatOS 开发结构

LuatOS 二次开发由两部分组成:

Core 部分:既底层固件,底层 C 代码编译的二进制固件,包含操作系统内核、驱动、基础库,文件较大,更新频率较低。

Script 部分:上层 Lua 应用脚本,包含业务逻辑、基础配置、应用功能等,文件较小,更新频率较高。。

在设备使用过程中,升级的时候通常会碰到三种情况,一种是单脚本需要升级,一种是脚本 + 固件都需要升级。

在 4G 相关的模组中,比如 Air780Exx 系列、Air8000 系列模组中

单脚本升级时为全量升级,含 core 升级时为差分升级。

在 wifi 模组 Air8101 和 Air8101A 中

单脚本升级和含 core 升级都为全量升级

4.2 单脚本升级

4.2.1 为什么脚本升级使用全量模式?

单脚本升级采用全量升级模式,原因有三:

大小因素:脚本文件本身就很小,一般几十到几百 KB,即使全量传输消耗的流量和带宽也很有限,差分计算带来的收益不明显。

变更模式:脚本更新频繁,且可能完全重写逻辑,相邻版本之间可能没有明显的"差异",而是完全不同的实现,这种情况下差分效果差。

实现复杂度:全量升级实现简单,直接覆盖文件即可,无需复杂的差分生成和合并算法,开发成本低。

4.2.2 单脚本升级升级包制作

在 luatools 中点击生成量产文件,在生成的量产文件夹中,对应的.bin 后缀的就是单脚本升级的升级包。

4.2.3 实际应用场景

场景 1:快速迭代开发

在开发阶段,业务逻辑频繁调整,每次修改后直接全量更新脚本,简单快捷。

场景 2:配置文件更新

当需要修改服务器地址、端口号、超时时间等配置参数时,直接替换整个配置文件即可。

4.2.4 工作流程

开发者编写新脚本 → 打包为升级文件 → 通过 FOTA 平台下发 → 设备接收并覆盖旧脚本 → 重启后生效。

整个过程简单直接,适合高频次的业务逻辑更新。

4.3 含 core 升级

4.3.1 为什么必须用差分升级?

含 core 升级必须使用差分模式,主要原因如下:

1、文件大小因素:Core 固件通常很大(512KB~2MB+),全量传输消耗大量流量和时间。差分可以显著减少传输数据量,通常减少 90% 以上。

2、变更模式特点:Core 固件更新频率低,相邻版本变化小,主要是 bug 修复和功能增强,大部分代码不变,适合差分算法。

3、技术必要性:嵌入式设备存储空间有限,无法同时存储两个完整固件;fota 分区通常比较小。

4.3.2 含 core 升级升级包制作

对于含 core 升级的话需要制作差分包,原始版本生成一次量产文件,新版本生成一次量产文件。

针对这两个量产文件,制作一个差分文件,点击到 luatools 的主界面,依次点击图中蓝框所示意的地方(注:必须使用 luatools_3.0.9 及其以上版本,要不差分包升级的时候可能会出问题)

wKgZPGmC1sqAdc8XAADmKbPMHg4229.png

按下图所示选择低版本以及高版本的固件,然后点击开始执行即可,如果不想输出的差分包在 luatools 根目录下,可以自行选择一个输出路径

wKgZO2mC1ueAMWvMAAET05Tjxk4520.png

在你选择的目录下看到如下所示,.bin 文件就是升级差分包。

wKgZPGmC1wWAetOKAAAscJy0qq0887.png

4.3.3 Core 固件的变更特点

Core 固件的更新通常是增量式的:修复一个 bug、优化某个驱动、增加一个小功能。比如 V1.0.0 到 V1.0.1,可能只是修复了网络连接中的一个空指针异常,99% 的代码都没有变化。这种场景下,差分升级只需传输那 1% 的变化部分,效率极高。

4.3.4 含 core 升级各芯片差异

4G 模组:

移芯系列模组:780Exx 系列、8000 系列等用移芯芯片的模组,含 core 升级为差分升级,需要手动差分

展锐系列模组:724UG 系列、722UG 系列、795UG 等用展锐芯片的模组,含 core 升级为差分升级,不过如果使用的是我们 iot 平台的话,可以上传新版本的量产文件,差分过程可在服务器后台自动进行,但是如果是第三方服务器升级需要手动差分,把差分包上传到自己服务器中。

特殊情况:wifi 模组 8101 和 8101A,由于芯片不支持差分升级,单脚本升级或 core+ 脚本升级时都为全量升级。在 luatools 生成全量文件的时候,在指定目录下会有两个文件 full_fota 和 script_ota。full_fota 中为 core+ 脚本 升级的升级包,script_ota 中为单脚本的升级包。

wKgZO2mC11uALhTDAABJBDG5CcY209.png

4.4 升级类型对 Flash 分区影响与二次开发关系总结

4.4.1 升级类型对 Flash 分区的影响

wKgZO2mC2D6AN_uKAABvn3bycm8413.png

4.4.2 各分区与二次开发的关系

wKgZO2mC2E-AOXs8AADMffNk2AM195.png

4.4.3 升级包特点

单脚本升级:文件小,更新频繁,直接生成.bin 文件

含 core 升级:文件大,更新频率低,4G 模组需制作差分包,WiFi 模组为全量包

五、fota 升级相关 api 简介(libfota2 扩展库和 fota 核心库)

在 luatos 中,升级一般是有两组接口都能实现 fota 功能,分别是 libfota2 扩展库和 fota 核心库

5.1 libfota2 扩展库与 fota 核心库 如何选择

5.1.1 核心区别总结

fota(底层核心库)

定位: 基础升级,提供最核心的固件写入能力

核心能力:

支持两种写入方式:fota.run() 分段写入 和 fota.file() 文件直接升级

支持内部存储和外部 SPI Flash

提供完整的升级流程控制:init → run/file → isDone → finish

fota2(libfota2 扩展库)

定位: 完整的远程升级解决方案,开箱即用

核心能力

自动处理 HTTP/HTTPS 网络下载

支持 IoT 平台和自建服务器

内置版本检查、下载、验证全流程

提供详细错误码和回调函数

代码特点:

wKgZO2mC2JOANpl0AACo3a6-1Zk187.png

5.1.2 适用场景推荐

1、选择 fota 的情况

需要自定义升级数据源

通过串口接收升级包

通过 MQTT、TCP 等自定义协议传输

从 SD 卡、U 盘等外部存储读取

对升级流程有特殊控制需求:

需要在升级前后执行特定操作

需要精细控制数据写入时机

需要自定义进度监控逻辑

资源极度受限环境

设备存储空间极小,内存紧张,无法加载额外库

开发测试阶段

需要调试升级过程的每个环节

需要验证自定义升级方案

2、选择 libfota2 的情况

标准的 HTTP 远程升级

从服务器下载升级包

使用 IoT 平台服务

需要 HTTPS 安全下载

希望快速实现升级功能

不想处理网络下载细节

需要自动版本检查

希望简单的错误处理

生产环境部署

需要稳定的远程升级方案

需要详细的升级状态反馈

支持定时自动检查更新

5.1.3 实际选择建议

新手用户 → 直接选择 libfota2

接口简单,学习成本低

内置完整错误处理

适合大多数物联网应用场景

高级用户 → 根据需求选择

标准网络升级 → libfota2

自定义数据传输 → fota + 自定义逻辑

一句话总结:

libfota2 扩展库:适合绝大多数标准远程升级场景。你只要给它一个服务器地址(合宙 iot 平台甚至不用给),它自己就帮你完成版本检查、HTTP 下载、校验所有流程。一行代码 libfota2.request(cb) 就能发起升级,省心省力。

fota 核心库:给你最大的控制权。适合非标准升级渠道,比如通过串口、MQTT、TCP 自定义协议,或者从 SD 卡、U 盘读取升级包。你需要自己控制数据流的接收和写入过程。

5.2 libfota2 扩展库 api 介绍

5.2.1 libfota2.request(cbFnc, opts)

功能

发起远程升级

libfota2.request 是 LuatOS 为物联网设备提供的一个强大、灵活且安全的远程固件升级接口,它能极大简化通过平台或私有服务器实现设备 FOTA 功能的开发流程。

参数

cbFnc

wKgZO2mC2liAWm6-AAROfbe-6r0655.png

opts

wKgZO2mC2uGASXMdABSqy38IduI118.png

返回值

示例

本示例章节仅列举一些常用功能的核心代码片段

wKgZO2mC2xmAOp6jAAPQqxt4aHc161.png

5.3 fota 核心库 api 介绍

5.3.1 fota.init()

功能

初始化 fota 流程。

参数

返回值

local result = fota.init()

result

wKgZO2mC20KAXnL_AABwlfL6J9w974.png

例子

wKgZO2mC3AKAb8eHAACOen8GPco306.png

5.3.2 fota.wait()

功能

等待底层 fota 流程准备好,等待底层固件升级流程初始化完成,包括存储设备就绪、升级上下文准备等。

参数

返回值

local isDone = fota.wait()

isDone

wKgZO2mC3CyAdY53AABq_AXWY04127.png

例子

wKgZO2mC3EaAIT7UAACatLErwdI415.png

5.3.3 fota.run(buff, offset, len)

功能

写入 fota 数据,支持逐段写入升级包。

注意事项:如果传入的是 zbuff,写入成功后,请自行清空 zbuff 内的数据

参数

buff

wKgZO2mC3HCAQUzUAACFH466OqI143.png

offset

wKgZPGmC3IuATVLpAACPjLrfLRo368.png

len

wKgZO2mC3L-AFwvQAACWiXnvxyk444.png

返回值

local result, isDone, cache = fota.run(buff, offset, len)

result

wKgZPGmC3OCAO6JVAABtVwLRu7Y986.png

isDone

wKgZPGmC3SeAWDY-AABwb3GcUrM318.png

cache

wKgZO2mC3T6Ae5IvAACGNyehbSY039.png

例子

wKgZO2mC3VyAULzzAAJIEQwu2Mk608.png

5.3.4 fota.file(path)

功能

从指定文件读取 fota 数据并写入

参数

path

wKgZPGmC3eiAR2ZrAAB3_69p4I8931.png

返回值

local result, isDone, cache = fota.file("/xxx.bin")

result

wKgZPGmC3gaAeIprAABuk7wx5Ec125.png

isDone

wKgZO2mC3iOAWNddAABuH3ucTZI350.png

cache

wKgZO2mC3T6Ae5IvAACGNyehbSY039.png

例子

wKgZO2mC3mKAApbbAAUToIxV9-Y056.png

5.3.5 fota.isDone()

功能

等待底层 fota 流程完成

参数

返回值

local result, isDone = fota.isDone()

result

wKgZO2mC3uyAGV1cAABlR1zhQng323.png

isDone

wKgZO2mC3yaAM45cAABsaXhmBHM476.png

例子

wKgZPGmC3zuAe3X2AADA6y8K08U633.png

5.3.6 fota.finish(is_ok)

功能

结束 fota 流程

参数

is_ok

wKgZPGmC31-AQ-d7AAEGpzTXssM826.png

返回值

local result = fota.finish(is_ok)

result

wKgZO2mC33mAWT68AABoaJSbaSM811.png

例子

wKgZO2mC34yAXzd5AAF6ThLu5O8790.png

六、LuatOS 上 FOTA 功能实际应用示例

本部分将深入两项具体实践:使用 FOTA 核心库进行固件升级和使用 libfota2 扩展库进行固件升级。

6.1 使用 FOTA 核心库的固件升级

6.1.1 分析项目代码

1、文件说明

main.lua:主程序入口文件。

fota_file.lua:文件系统 FOTA 升级功能实现,从文件系统直接读取升级包进行固件升级。

fota_uart.lua:串口分段升级功能实现,通过串口接收升级包数据进行固件升级。

2、演示功能

文件系统直接升级:

通过模组文件系统中的文件直接升级

代码演示通过 luatools 的烧录文件系统功能将升级包文件直接烧录到文件系统然后升级

适用于本地升级、批量生产等场景

串口分段升级:

通过串口将升级包文件分多个片段发送,每个片段接收并写入

代码演示使用 USB 虚拟串口分段写入升级包升级

适用于非标准数据传输(串口、TCP、MQTT 等自定义通道升级)

支持流程精细控制,可自定义升级前后处理逻辑

3、注意事项:

升级包必须是针对当前硬件平台的正确固件

升级过程中请勿断电或中断升级流程

串口升级时,需确保串口连接稳定

升级成功后,设备会自动重启,无需手动操作。

升级失败时,可通过日志查看具体错误信息

6.2 使用 libfota2 扩展库的固件升级

6.2.1 分析项目代码

1、目录结构

wKgZPGmC4TSAEo5IAAKgHS1jcuY810.png

2、文件说明

iot_server 目录

main.lua :主程序入口,

update.lua :使用合宙 IoT 服务器简单远程升级模块。

air_srv_fota.lua :使用合宙 IoT 平台远程通过 tcp 下发指令控制升级功能模块。

netdrv_device.lua :网络驱动设备配置,支持 4G、WIFI、以太网等多种网络连接方式

psm_power_fota.lua :PSM 低功耗模式下的 FOTA 升级实现,解决 PSM 状态下升级中断问题 TCP 客户端 IoT 模块

tcp_iot/tcp_iot_main.lua :TCP 客户端主应用,处理 TCP 连接和升级指令接收

tcp_iot/tcp_iot_sender.lua :TCP 数据发送功能,负责向服务器发送设备状态和升级反馈

tcp_iot/tcp_iot_receiver.lua :TCP 数据接收功能,解析服务器下发的升级指令 网络驱动

netdrv/netdrv_4g.lua :4G 网卡驱动实现,负责 4G 网络连接管理

netdrv/netdrv_eth_spi.lua :SPI 外挂 CH390H 芯片的以太网卡驱动

netdrv/netdrv_multiple.lua :多网卡优先级管理,支持配置多种网卡的连接优先级

self_server 目录

main.lua :主程序入口。

update.lua :自定义服务器远程简单升级功能模块。

customer_srv_fota.lua :自定义服务器通过 tcp 下发指令控制升级功能模块。

psm_power_fota.lua :PSM 低功耗模式下的 FOTA 升级实现,解决低功耗场景下的升级问题

netdrv_device.lua :网络驱动设备配置,支持多种网络连接方式 TCP 自定义服务器模块

tcp_self_server/tcp_self_main.lua :TCP 客户端主应用,处理 TCP 连接和升级指令接收

tcp_self_server/tcp_self_sender.lua :TCP 数据发送功能,负责向服务器发送设备状态和升级反馈

tcp_self_server/tcp_self_receiver.lua :TCP 数据接收功能,解析服务器下发的升级指令 网络驱动

netdrv/netdrv_4g.lua :4G 网卡驱动实现

netdrv/netdrv_eth_spi.lua :SPI 以太网卡驱动

netdrv/netdrv_multiple.lua :多网卡优先级管理

3、演示功能

场景一: IoT 服务器简单升级

使用 iot.openluat.com 服务器进行远程升级

上电就检查升级,支持版本号自动检测和升级包下载

适用于快速部署和管理大量设备

场景二:TCP 服务器下发升级指令

通过 TCP 服务器下发 JSON 格式升级指令

通控制设备使用 FOTA 功能模块

场景三:PSM 低功耗 FOTA

针对 PSM 状态下升级未完成就进入休眠导致升级失败的情况

支持低功耗设备的可靠升级

4、注意事项

进行远程升级时,版本号必须按照"XXX.YYY.ZZZ"三段格式定义

不同场景的功能模块不要同时加载,否则会导致功能冲突

升级过程中需保持网络连接稳定

建议在升级前检查设备电量,确保有足够电量完成升级。

6.3 Air780EHM 开发板上演示项目功能

准备硬件环境

Air780EHM 开发板一块

TYPE-C USB 数据线一根

可联网的 SIM 卡(用于远程升级场景)

ttl 小板(可选,用于物理串口升级)

准备软件环境

Luatools烧录工具

内核固件

脚本文件:对应场景的脚本文件

模拟工具

串口升级:Python 环境和 main.py 脚本

远程升级

演示流程

文件系统升级演示

使用 Luatools 烧录升级包到文件系统

烧录 fota_file.lua 相关脚本

观察日志输出,验证升级是否成功

串口升级演示

烧录 fota_uart.lua 相关脚本

按下 Power 键启动升级模式

运行 Python 脚本发送升级包

观察串口输出和升级进度

远程升级演示

在 IoT 平台创建设备和升级任务

烧录对应场景的脚本

设备自动连接平台并检测升级

观察升级进度和结果

6.4 升级扩展

支持蓝牙功能的模块比如 Air8000A 或者 Air8101 等模组,可以通过蓝牙升级,具体升级可参考对应教程文档。

七、注意事项与常见问题

7.1 注意事项

1、版本号格式:

使用 IoT 平台时,项目的 VERSION 必须为三段数字格式(如 "001.000.001"),否则平台版本比对可能出错。

2、PRODUCT_KEY:

使用 IoT 平台时,必须在 main.lua 中正确定义全局变量 PRODUCT_KEY,其值需从 IoT 平台的项目中获取。

3、重启时机:

下载升级包成功(result 为 0)后,通常需要调用 rtos.reboot() 重启设备以更新。你可以根据需要延迟重启。

4、自建服务器规则:

使用 libfota2 扩展库的时候,填写自建服务器 url 时候记得 url 前面拼上###

需要升级时,服务器应返回 HTTP 200,消息体为升级文件内容。

无需升级时,服务器应返回 HTTP 300 或以上的状态码。

5、使用 iot 平台需要注意:

设备在自己名下;

代码中项目 key(PRODUCT_KEY)要填写正确;

配置好升级包文件后需要指定升级设备,配置需要升级设备的 imei;

升级失败可以在 iot 平台中打开固件升级->升级日志页面,输入 iemi 来查看下升级失败的原因是什么。

wKgZPGmC49aAUhL2AANkSEvzm2k083.png

7.2 为什么升级后我的模块没有任何反应了,像是变砖一样

有多种可能,

7.2.1 检查脚本

首先先检查下用户自己的脚本,有可能是引起重启/死机的代码写在了最前面,例如新加的某个值或者函数为 nil 但是还是去做了些加减乘除或者判断大小的逻辑。可以直接本地烧录下新版本的 core+ 脚本验证,如果有 fskv 等用到 flash 的代码,可能需要仔细检查才能排除问题,比如下载的时候勾选如下图所示的两个选项。

7.2.2 检查 core

如果是仅脚本升级,但是没注意使用了新 core 中才有的接口,就有可能引起循环重启,如果重启在代码最开头,模块可能来不及打印任何日志就重启了,可以直接本地烧录下新版本的 core+ 脚本验证,如果有 fskv 等用到 flash 的代码,可能需要仔细检查。

7.3 检查过脚本和 core,没问题,为什么会循环升级 6 次以后禁止升级

检查下升级包是否正常,有时候因为人员误操作,经常会出现旧脚本 + 新 core 或者新脚本 + 旧 core 的意外组合,

例如:

本来应该如下表描述的一样

wKgZO2mC5QSAdd0OAAAaUAjniWM290.png

操作人员失误后变成了如下

wKgZO2mC5RWAVDEmAAA69ik3u8g389.png

然后误操作旧版本(1) 和误操作新版本(1)进行差分,这样虽然脚本版本号旧版本大于了新版本,但是 core 的旧版本小于新版本,所以升级平台依旧认为是依次有效的升级,下发了升级包。

升级完成后,模块内部脚本版本号变成了 001.000.000 core 版本号为 V2004 ,下次模块请求升级的时候,当前固件上报的脚本版本号(001.000.000)依旧小于云平台存储的脚本版本号(001.000.005),然后继续下发升级包,就这么循环升级,直到流量耗尽,建议可以做一个类似 iot 平台的禁止升级规则

wKgZO2mC5SyAF_N6AAEXlIhz66A670.png

在正确生成差分包,并且上传成功后,可以在 iot 平台里解除禁止升级的限制

在"我的设备"中选择升级 imei 所在的项目,然后点击右边的"解除禁止升级",

wKgZPGmC5UKAL7_LAAFNWHKofqA224.png

确定“导致设备循环升级的异常”已经处理完成后,点击确定解除,即可解除限制升级

wKgZPGmC5WaAAeDOAAFr5-HIKFY019.png

7.4 如何处理同个项目外面有多个版本设备的升级情况

7.4.1 场景 1:多种不同内核固件版本都要升级为最新版本内核固件 + 最新脚本

需要对每个版本都生成对应的差分包

操作步骤:

7.4.2 场景 2:多种不同内核固件版本 + 不同版本脚本都要升级为最新版本脚本,既只升级脚本。

操作步骤:

7.4.3 升级规则说明

内核固件:需分情况对待,4G 模组系列比如 Air780EXX 系列、Air724UG、Air8000 系列等仅支持差分升级,wifi 模组 Air8101/Air8101A 是全量升级

脚本:支持全量升级,可一次性完成

7.5 fota 升级对 fskv 或文件系统的影响

7.5.1 远程升级时,会清除 FSKV 中的数据吗?

默认不会,FSKV 数据存储在独立分区,远程升级主要操作 FOTA 专用分区、ap/cp 分区、用户脚本分区,不会直接修改 FSKV 分区。

7.5.2 远程升级时,自己创建的文件会被删除吗?

不会,用户文件系统中自行创建的文件会保留,不会影响用户文件。

八、fota 错误总结

8.1 差分包过大

wKgZPGmC5kSAAhW2AAKHaIIWB04929.png

8.2 iot 平台升级没有配置 imei

wKgZPGmC5mWAU-KiAAS9Pikv15c614.pngwKgZPGmC5oGAQjYPAAGNp8b3xS0733.png

8.3 制作差分包时的旧固件不是模组中实际的固件

下面例子模组中实际固件是 2016_2 号固件

制作差分时用的 2020_1 号固件对 2016_1 号固件制作的差分包

wKgZO2mC5qGAaWNVAARU7eLXN9o810.pngwKgZPGmC5sqARHKuAAQySFnanJs578.png

平台校验版本通过,正常下发升级包

wKgZPGmC5t-ABM1yAAG_dQrzfSc749.png

8.4 不同编号固件制作差分包

不同编号的固件制作差分包的时候通常制作的差分包过大,升级失败,日志见 8.1

wKgZPGmC5vyAP9vbAAE1FCPwbT0788.png

问题将会持续更新......

今天的内容就分享到这里了~

审核编辑 黄宇