本文以LuatOS平台为基础,手把手引导开发者完成FOTA升级的全流程配置与操作,涵盖脚本更新、固件上传、设备绑定等关键步骤,助你快速实现设备的无感升级。
一、Flash 分区与 FOTA 分区简介
大家好,在开始动手操作 FOTA 之前,我们必须先了解它的"工作场地"------模组内部的 Flash 存储器。这就像装修房子前,一定要先看懂户型图。
不同型号的模组,Flash 分区就像不同的"户型",格局各异,但都有一个核心设计:必须有一个独立的"系统升级专用间",也就是 FOTA 分区。
今天,我们就以 Air780EHM 为例,来详细解读这份关键的"Flash 户型图"。

首先,我们看全局。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 检查升级标志 → 验证升级包 → 写入目标分区 → 启动新固件 → 验证新固件

1、升级包写入流程:
初始化 FOTA 模块: 创建升级上下文结构体,分配必要的缓冲区和资源,为升级流程做准备。
下载升级包:
通过 HTTP、MQTT 或 UART 等通信协议从服务器或本地下载升级包数据。支持下载到文件系统,或者直接分包写入 fota 分区。
写入 FOTA 分区:
如果是下载到文件系统中暂存,待整个升级包在文件系统中下载完成后,再一次性从文件系统读取并写入到专用的 FOTA 分区。
也可以实时分包接收升级包数据并写入到文件系统中。
完成写入,验证升级包:
执行升级包的完整性校验(MD5)
若校验通过,则在特定位置中设置升级标志,并重启系统。Bootloader 下次启动时执行升级。
若校验失败,则报告错误并终止升级流程。
2、重启后升级流程:
设备上电,执行 Bootloader
设备重启后,Bootloader 首先检查是否存在有效的升级标志。
验证与执行升级 若无升级标志:Bootloader 跳过升级流程,直接启动现有固件。 若存在升级标志:
1.5 各模组 fota 分区空间
在实际使用中,不同的固件版本或型号为了支持不同的功能,会调整 FOTA 分区内的大小,因此用户实际可用的升级空间可查看下表。
Air7xx 系列模组 LuatOS 多固件版本


Air8000 系列固件版本

二、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,具体包含如下参数:

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 解压后都有哪些文件:

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. 差分升级的工作流程
下面看一下差分升级的工作流程,具体流程如下:

流程详解:
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 及其以上版本,要不差分包升级的时候可能会出问题)

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

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

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 中为单脚本的升级包。

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

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

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 平台和自建服务器
内置版本检查、下载、验证全流程
提供详细错误码和回调函数
代码特点:

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

opts

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

5.3 fota 核心库 api 介绍
5.3.1 fota.init()
功能
初始化 fota 流程。
参数
无
返回值
local result = fota.init()
result

例子

5.3.2 fota.wait()
功能
等待底层 fota 流程准备好,等待底层固件升级流程初始化完成,包括存储设备就绪、升级上下文准备等。
参数
无
返回值
local isDone = fota.wait()
isDone

例子

5.3.3 fota.run(buff, offset, len)
功能
写入 fota 数据,支持逐段写入升级包。
注意事项:如果传入的是 zbuff,写入成功后,请自行清空 zbuff 内的数据
参数
buff

offset

len

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

isDone

cache

例子

5.3.4 fota.file(path)
功能
从指定文件读取 fota 数据并写入
参数
path

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

isDone

cache

例子

5.3.5 fota.isDone()
功能
等待底层 fota 流程完成
参数
无
返回值
local result, isDone = fota.isDone()
result

isDone

例子

5.3.6 fota.finish(is_ok)
功能
结束 fota 流程
参数
is_ok

返回值
local result = fota.finish(is_ok)
result

例子

六、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、目录结构

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 来查看下升级失败的原因是什么。

7.2 为什么升级后我的模块没有任何反应了,像是变砖一样
有多种可能,
7.2.1 检查脚本
首先先检查下用户自己的脚本,有可能是引起重启/死机的代码写在了最前面,例如新加的某个值或者函数为 nil 但是还是去做了些加减乘除或者判断大小的逻辑。可以直接本地烧录下新版本的 core+ 脚本验证,如果有 fskv 等用到 flash 的代码,可能需要仔细检查才能排除问题,比如下载的时候勾选如下图所示的两个选项。
7.2.2 检查 core
如果是仅脚本升级,但是没注意使用了新 core 中才有的接口,就有可能引起循环重启,如果重启在代码最开头,模块可能来不及打印任何日志就重启了,可以直接本地烧录下新版本的 core+ 脚本验证,如果有 fskv 等用到 flash 的代码,可能需要仔细检查。
7.3 检查过脚本和 core,没问题,为什么会循环升级 6 次以后禁止升级
检查下升级包是否正常,有时候因为人员误操作,经常会出现旧脚本 + 新 core 或者新脚本 + 旧 core 的意外组合,
例如:
本来应该如下表描述的一样

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

然后误操作旧版本(1) 和误操作新版本(1)进行差分,这样虽然脚本版本号旧版本大于了新版本,但是 core 的旧版本小于新版本,所以升级平台依旧认为是依次有效的升级,下发了升级包。
升级完成后,模块内部脚本版本号变成了 001.000.000 core 版本号为 V2004 ,下次模块请求升级的时候,当前固件上报的脚本版本号(001.000.000)依旧小于云平台存储的脚本版本号(001.000.005),然后继续下发升级包,就这么循环升级,直到流量耗尽,建议可以做一个类似 iot 平台的禁止升级规则

在正确生成差分包,并且上传成功后,可以在 iot 平台里解除禁止升级的限制
在"我的设备"中选择升级 imei 所在的项目,然后点击右边的"解除禁止升级",

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

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 差分包过大

8.2 iot 平台升级没有配置 imei


8.3 制作差分包时的旧固件不是模组中实际的固件
下面例子模组中实际固件是 2016_2 号固件
制作差分时用的 2020_1 号固件对 2016_1 号固件制作的差分包


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

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

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