31 libfota2-远程升级
作者:孟伟 | 最后修改:2026-09-22
一、概述
FOTA远程升级功能是物联网设备的核心功能之一,它允许设备通过无线网络更新固件,而无需物理接触。在LuatOS开发模式下,固件分为两部分:core(底层核心固件)和script(用户脚本)。远程升级时,core采用差分升级方式,而 script 则采用全量覆盖升级方式。您可以选择仅升级script、仅升级core或同时升级core+script。
LuatOS主要提供了三代FOTA接口,按演进顺序依次为libfota、libfota2和libfota3:
-
libfota:初代FOTA接口,通过一长串顺序参数传递配置,必须按顺序填写,可选参数处理不够灵活。
-
libfota2:第二代FOTA接口,改用opts表传递参数,结构清晰、可选参数管理方便,并对合宙IoT平台的错误码做了更细致的提示。
-
libfota3:第三代FOTA接口,面向支持LuatOS开发的所有产品,仅对接合宙IoT平台。扩展库内部完成「检测新版本 → 下载升级包 → 校验 → 写入升级分区 → 重启生效 → 结果上报」的完整升级闭环,并内置自动定时检测、运行期动态配置、全过程状态回调与带屏确认能力。使用前需在合宙IoT平台创建项目并获取project_key(具体操作演示请参考 libfota3 文档)。
其中libfota和libfota2既支持通过合宙官方IoT平台升级,也支持自建第三方HTTP服务器升级。
需要注意的是第三方http服务器需要满足如下两点(仅适用于libfota和libfota2;libfota3仅对接合宙IoT平台,不适用此约定):
-- 若需要升级, 响应http 200, body为升级文件的内容
-- 若不需要升级, 响应300或以上的代码,务必注意
为了更清晰地了解这三个库的特点,下表对比了它们的主要特性。
| 区别项 | libfota | libfota2 | libfota3 |
|---|---|---|---|
| 参数传递方式 | 一长串顺序参数传递,必须按顺序填写,可选参数处理不够灵活 | 使用opts表传递参数,结构清晰,可选参数管理方便 | 使用opts表传递参数,配置项覆盖检测、下载、回调等完整流程 |
| 使用便捷性 | 相对较低。需要关注较多参数,配置稍显繁琐。 | 较高。接口设计更现代,只需关注关键配置,简化了使用流程。 | 较高。一次request()完成配置,内部自动处理等待网络、时间同步、定时检测、下载、刷写与重启 |
| 服务器支持 | 支持合宙IoT平台和自建服务器 | 支持合宙IoT平台和自建服务器 | 仅支持合宙IoT平台,不支持自建升级服务器 |
| 升级包校验 | 无 | 无 | 下载完成后进行SHA256校验,校验通过才写入升级分区 |
| 过程状态回调 | 回调函数提供状态码 | 回调函数提供详细状态码 | on_status覆盖检测、下载(含百分比)、刷写、重启等环节;on_confirm可为带屏设备提供下载前、重启前确认 |
| 升级结果上报 | 无 | 无 | 升级结果在重启后自动比对并上报服务器 |
| 定时升级 | 需应用层定时调用 | 需应用层定时调用 | 内置auto+interval定时检测,重启后按时间戳续算不漏检 |
| 适用产品 | 支持LuatOS开发的所有产品 | 支持LuatOS开发的所有产品 | 支持LuatOS开发的所有产品 |
| 推荐使用场景 | 存量老项目维护,或代码已依赖初代接口、不打算调整的项目。 | 新项目需对接自建第三方HTTP升级服务器时;或对接合宙IoT平台、但已有成熟libfota2代码,或团队/客户更熟悉libfota2、希望沿用现有实现时。 | 对接合宙IoT平台的项目,除上一栏所述情况外优先选择;尤其是希望省去设备归属处理、定时检测、升级包校验、升级结果上报等自研工作量的场景。 |
如何选择 libfota / libfota2 / libfota3
(一)按项目情况选择
- libfota:仅建议存量老项目维护使用,或代码已依赖初代接口、不打算调整的项目。新项目不建议使用。
- libfota2:以下两种情况建议使用 libfota2 ——
- 新项目需要对接自建的第三方HTTP升级服务器(libfota 和 libfota2 都支持自建服务器,但新项目建议用 libfota2;libfota3 只对接合宙IoT平台,不支持自建服务器);
- 项目对接合宙IoT平台,但已有成熟的 libfota2 代码,或团队/客户对 libfota2 更熟悉,希望沿用现有实现。
- libfota3:对接合宙IoT平台的项目,除上述第 2 种情况外优先选择 libfota3。
libfota 和 libfota2 目前仍在持续维护,已使用这两个库的项目继续使用不受影响。
(二)libfota3 解决了 libfota2 的哪些问题
- 设备归属:使用 libfota2 通过合宙IoT平台升级时,需要自行判断设备(IMEI/MAC)是否已归属到目标账号及对应项目下,未归属时还要做自动化的归属处理,处理不当就会升级失败;使用 libfota3 时不需要判断设备IMEI/MAC与IoT账号的归属关系,也不需要对设备IMEI/MAC做任何自动化归属处理。
- 定时检测:libfota2 需要应用层自己起定时器周期调用检测;libfota3 内置自动定时检测,且重启后会按已保存的时间戳续算,不会因为设备中途重启而漏检。
- 升级包校验:libfota2 下载完成后直接写入;libfota3 下载后先做 SHA256 校验,校验通过才写入升级分区,降低坏包/半包把设备刷坏的风险。
- 升级结果上报:libfota2 不提供升级结果上报;libfota3 在重启后自动比对实际版本并上报服务器,平台侧可直接掌握升级成功与否。
- 过程可观测、可交互:libfota2 只有一个升级结果回调;libfota3 的 on_status 覆盖检测、下载(含百分比)、刷写、重启等过程,on_confirm 还能为带屏设备提供下载前、重启前的确认交互。
- 联网与时间同步、运行期配置:libfota2 需要应用层自行保证已联网、时间已同步;libfota3 的 request() 内部自动等待网络就绪并完成时间同步,还支持 config() 在运行期动态开闭自动检测、调整检测间隔。
(三)一句话总结
存量老项目维护、或代码已依赖初代接口的项目,继续用 libfota,新项目不建议使用;需要对接自建服务器,选 libfota2(libfota 虽同样支持自建服务器,但新项目不建议使用);对接合宙IoT平台且团队已熟悉 libfota2,可继续用 libfota2;其余对接合宙IoT平台的项目,优先选 libfota3。
本文档介绍第二代接口 libfota2 的用法。libfota2 通过 opts 表传递参数,相比 libfota 在接口清晰度和易用性上有明显提升,点击此处了解libfota,点击此处了解libfota3。
二、核心示例
1、核心示例是指:使用本库文件提供的核心API,开发的基础业务逻辑的演示代码;
2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;
3、更加完整和详细的demo,请参考 LuatOS仓库 中各个产品目录下的demo/fota/fota2
--用法实例
local libfota2 = require("libfota2")
-- 升级结果的回调函数
-- 功能:获取fota的回调函数
-- 参数:
-- result:number类型
-- 0表示成功
-- 1表示连接失败
-- 2表示url错误
-- 3表示服务器断开
-- 4表示接收报文错误
-- 5表示使用iot平台VERSION需要使用 xxx.yyy.zzz形式
local function fota_cb(ret)
log.info("fota", ret)
if ret == 0 then
log.info("升级包下载成功,重启模块")
rtos.reboot()
elseif ret == 1 then
log.info("连接失败", "请检查url拼写或服务器配置(是否为内网)")
elseif ret == 2 then
log.info("url错误", "检查url拼写")
elseif ret == 3 then
log.info("服务器断开", "检查服务器白名单配置")
elseif ret == 4 then
log.error("FOTA 失败",
"原因可能有:\n" ..
"1) 服务器返回 200/206 但报文体为空(0 字节)—— 通常是升级包文件缺失或 URL 指向空文件;\n" ..
"2) 服务器返回 4xx/5xx 等异常状态码 —— 请确认升级包已上传、URL 正确、鉴权信息有效;\n"..
"3) 已经是最新版本,无需升级" )
elseif ret == 5 then
log.info("版本号书写错误", "iot平台版本号需要使用xxx.yyy.zzz形式")
else
log.info("不是上面几种情况 ret为", ret)
end
end
libfota2.request(fota_cb)
-- 若需要定时升级
sys.timerLoopStart(libfota2.request, 4*3600*1000, fota_cb)
三、常量详解
libfota2核心库没有常量。
四、函数详解
libfota2.request(cbFnc, opts)
功能
发起远程升级
libfota2.request 是LuatOS为物联网设备提供的一个强大、灵活且安全的远程固件升级接口,它能极大简化通过合宙平台或私有服务器实现设备FOTA功能的开发流程。
参数
cbFnc
参数含义:升级包下载结果回调函数,用于返回升级包下载结果。回调函数的调用形式为:cbFnc(result),
result: number类型:
0表示成功;
1表示连接失败;
2表示url错误;
3表示服务器断开;
4表示接收报文错误;
5表示使用iot平台VERSION需要使用 xxx.yyy.zzz形式
数据类型:function类型;
取值范围:任意有效的函数名都行;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:如下方所示,定义了一个函数fota_cb就可以做为此参数传入;
local function fota_cb(result)
log.info("fota", result)
if result == 0 then
log.info("升级包下载成功,重启模块")
rtos.reboot()
elseif result == 1 then
log.info("连接失败", "请检查url拼写或服务器配置(是否为内网)")
elseif result == 2 then
log.info("url错误", "检查url拼写")
elseif result == 3 then
log.info("服务器断开", "检查服务器白名单配置")
elseif result == 4 then
log.error("FOTA 失败",
"原因可能有:\n" ..
"1) 服务器返回 200/206 但报文体为空(0 字节)—— 通常是升级包文件缺失或 URL 指向空文件;\n" ..
"2) 服务器返回 4xx/5xx 等异常状态码 —— 请确认升级包已上传、URL 正确、鉴权信息有效;\n"..
"3) 已经是最新版本,无需升级" )
elseif result == 5 then
log.info("版本号书写错误", "iot平台版本号需要使用xxx.yyy.zzz形式")
else
log.info("不是上面几种情况 result为", result)
end
end
opts
参数含义:fota升级参数配置;参数为table类型,table内容格式说明如下:
{
-- 参数含义:指定固件升级服务器的URL。默认值是合宙iot平台的升级地址。所以若使用合宙iot平台,则不 需要填
-- 数据类型:string类型
-- 取值范围:支持HTTP、HTTPS,支持域名、IP地址,支持自定义端口,标准的HTTP URL格式都支持;
-- 是否必选:可选传入此参数
-- 注意事项:-- 如果是使用合宙IOT平台,不需要填写URL, 因为默认值是合宙iot平台的升级地址,默认升级平台地址是: iot.openluat.com
-- 如果是自建的OTA服务器, 则需要填写正确的URL, 例如 http://192.168.1.5:8000/update
-- 如果自建OTA服务器,且url包含全部参数,不需要额外添加参数, 请在url前面添加 ###
-- 如果不加###,则默认会上传如下参数
--1. opts.version string 版本号, 默认是 固件版本号.xxx.zzz格式。注:固件版本号是 rtos.version()返回的版本号,xxx.zzzz是_G.VERSION参数中x和z
--2. opts.timeout int 请求超时时间, 默认300000毫秒,单位毫秒
--3. opts.project_key string 合宙IOT平台的项目key, 默认取全局变量PRODUCT_KEY,自建服务器不用填
--4. opts.imei string 设备识别码, Cat.1模块默认取IMEI,wifi模块默认取WLAN的STA MAC地 址,mcu默认取 mcu.unique_id() 返回的唯一ID
--5. opts.firmware_name string 底层版本号;默认是 _G.PROJECT.. "_LuatOS-SoC_" .. rtos.bsp();其中rtos.bsp()返回的是模组型号
-- 参数示例:-- 合宙iot服务器:local opts ={}
-- 自建服务器:库会自动附加默认参数(imei, version等):local opts = { url = "http://192.168.1.5:8000/update%simei=xxxxxxx&project_key=xxxxxxxx&firmware_name=xxxxxxxxx&version=xxxxxxxx" }
-- 自建服务器,且url包含全部参数,不需要额外添加参数, 请在url前面添加###:local opts = { url = "###http://192.168.1.5:8000/update?device_id=12345&fw_ver=1.2.3"}
url = ,
-- 参数含义:请求的版本号
-- 数据类型:string类型
-- 取值范围:合宙IOT有一套版本号体系,不传就是合宙规则(默认是 BSP版本号.xxxx.zzz格式), 自建服务器的话当然是自行约定版本号了;注:BSP版本号是通过rtos.version()返回的版本号,xxx.zzz是_G.VERSION参数中x和z
-- 是否必选:可选传入此参数
-- 注意事项:暂无
-- 参数示例:默认BSP版本号.xxx.zzz是"2012.001.000"或者自定义"1.0.0"
version = ,
-- 参数含义:上网使用的网卡ID;
-- 数据类型:number或者nil;
-- 取值范围:number类型时,取值范围参考socket api中的常量详解;
-- 是否必选:可选传入此参数;
-- 注意事项:如果没有传入此参数,内核固件会自动选择当前时间点其他功能模块设置的默认网卡;
-- 除非你HTTP请求时,一定要使用某一种网卡,才设置此参数;
-- 如果没什么特别要求,不要设置此参数,使用系统中设置的默认网卡即可 ;
-- 一般来说,LuatOS的网络应用demo中都会有netdrv_device功能模块设置默认网卡;
-- 所以建议使用http.request接口时,不要设置此参数,直接使用netdrv_device设置的默认网卡就行;
-- 参数示例:socket.LWIP_GP表示使用4G网卡;
adapter = ,
-- 参数含义:设置整个 FOTA HTTP 请求过程的超时时间
-- 数据类型:int类型,
-- 取值范围:number类型时,取值范围为大于等于0的整数,0表示永久等待;
-- 是否必选:可选传入此参数
-- 注意事项:暂无
-- 参数示例:300000表示300s
timeout = ,
-- 参数含义:合宙IOT平台的项目key, 默认取全局变量PRODUCT_KEY. 自建服务器不用填
-- 数据类型:string类型
-- 取值范围:无特别限制;
-- 是否必选:可选传入此参数
-- 注意事项:仅用于合宙 IoT 平台。自建服务器不需要此参数。
--如果未设置,库会尝试从全局变量 PRODUCT_KEY 中获取。
-- 参数示例:"user123" 或者 nil
project_key = ,
-- 参数含义:设备识别码,用于服务器识别具体设备
-- 数据类型:string类型
-- 取值范围:默认取IMEI(Cat.1模块)或WLAN的STA MAC地址 (wifi模块)或 mcu.unique_id()获取MCU唯一ID
-- 是否必选:可选传入此参数
-- 注意事项:暂无
-- 参数示例:imei = mobile.imei(),
imei = ,
-- 参数含义:固件名称
-- 数据类型:string类型
-- 取值范围:默认是 _G.PROJECT.. "_LuatOS-SoC_" .. rtos.bsp()
-- 是否必选:可选传入此参数
-- 注意事项:暂无
-- 参数示例:FOTA2_DEMO_LuatOS-SoC_Air780EPM
firmware_name = ,
-- 参数含义:服务器ca证书数据;
-- 数据类型:string或者nil;
-- 取值范围:无特别限制;
-- 是否必选:可选传入此参数;
-- 注意事项:当客户端需要验证服务器证书时,需要此参数,如果证书数据在一个文件中,要把文件内容读出来,赋值给server_ca_cert;
-- 参数示例:例如通过Luatools烧录了server_ca.crt文件,就可以通过io.readFile("/luadb/server_ca.crt")读出文件内容赋值给赋值给server_ca_cert;
server_cert = ,
-- 参数含义:客户端证书数据;
-- 数据类型:string或者nil;
-- 取值范围:无特别限制;
-- 是否必选:可选传入此参数;
-- 注意事项:当服务器需要验证客户端证书时,需要此参数,如果证书数据在一个文件中,要把文件内容读出来,赋值给client_cert;
-- 参数示例:例如通过Luatools烧录了clinet.crt文件,就可以通过io.readFile("/luadb/clinet.crt")读出文件内容赋值给赋值给client_cert;
client_cert = ,
-- 参数含义:加密后的客户端私钥数据;
-- 数据类型:string或者nil;
-- 取值范围:无特别限制;
-- 是否必选:可选传入此参数;
-- 注意事项:当服务器需要验证客户端证书时,需要此参数,如果加密后的私钥数据在一个文件中,要把文件内容读出来,赋值给client_key;
-- 参数示例:例如通过Luatools烧录了clinet.key文件,就可以通过io.readFile("/luadb/clinet.key")读出文件内容赋值给client.key;
client_key = ,
-- 参数含义:客户端私钥口令数据;
-- 数据类型:string或者nil;
-- 取值范围:无特别限制;
-- 是否必选:可选传入此参数;
-- 注意事项:当服务器需要验证客户端证书时,需要此参数,如果加密后的私钥数据在一个文件中,要把文件内容读出来,赋值给client_password;
-- 参数示例:例如通过Luatools烧录了clinet.password文件,就可以通过io.readFile("/luadb/clinet.password")读出文件内容赋值给client_password;
client_password = ,
-- 参数含义:HTTP请求方法;
-- 数据类型:string;
-- 取值范围:支持"GET"、"POST"、"HEAD"等所有HTTP请求方法,请求方法用大写字母表示;
-- 是否必选:可选传入此参数;如果没有传入此参数或者传入了nil类型,则使用默认值,默认值分为以下两种情况:
-- 如果没有设置files,forms,body,bodyfile参数,则默认为"GET"
-- 如果至少设置了files,forms,body,bodyfile中的一种参数,则默认为"POST"
-- 注意事项:暂无;
-- 参数示例:GET请求时填"GET",POST请求时填"POST";
method = ,
-- 参数含义:HTTP请求头列表,键值对的形式;
-- 数据类型:table或者nil;
-- 取值范围:当为table数据类型时,请求头列表中支持一个或者多个请求头;
-- 是否必选:可选传入此参数;
-- 注意事项:暂无;
-- 参数示例:{
-- ["Content-Type"] = "application/x-www-form-urlencoded",
-- ["self_defined_key"] = "self_defined_value"
-- }
headers = ,
-- 参数含义:HTTP请求体;
-- 数据类型:string或者zbuff或者nil;
-- 取值范围:无特别限制;
-- 是否必选:可选传入此参数;
-- 注意事项:如果请求体是一个文件中的内容,需要把文件内容读出来,赋值给body使用;
-- 参数示例:"123456" 或者 一个zbuff对象 或者 nil
body = ,
}
数据类型:table或者nil;
取值范围:参考参数含义内各字段说明
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:如下方所示,如果url是"http://192.168.1.5:8000/update",version是"1.0.0";
--local opts = {
-- url = "http://192.168.1.5:8000/update",
-- version = "1.0.0"
--}
返回值
无
示例
本示例章节仅列举一些常用功能的核心代码片段
更加完整和详细的demo,请参考 https://gitee.com/openLuat/LuatOS/tree/master/module 各个产品目录下的demo/fota2文件夹下内容
--用法实例
local libfota2 = require("libfota2")
-- 升级结果的回调函数
-- 功能:获取fota的回调函数
-- 参数:
-- result:number类型
-- 0表示成功
-- 1表示连接失败
-- 2表示url错误
-- 3表示服务器断开
-- 4表示接收报文错误
-- 5表示使用iot平台VERSION需要使用 xxx.yyy.zzz形式
local function fota_cb(ret)
log.info("fota", ret)
if ret == 0 then
log.info("升级包下载成功,重启模块")
rtos.reboot()
elseif ret == 1 then
log.info("连接失败", "请检查url拼写或服务器配置(是否为内网)")
elseif ret == 2 then
log.info("url错误", "检查url拼写")
elseif ret == 3 then
log.info("服务器断开", "检查服务器白名单配置")
elseif ret == 4 then
log.error("FOTA 失败",
"原因可能有:\n" ..
"1) 服务器返回 200/206 但报文体为空(0 字节)—— 通常是升级包文件缺失或 URL 指向空文件;\n" ..
"2) 服务器返回 4xx/5xx 等异常状态码 —— 请确认升级包已上传、URL 正确、鉴权信息有效;\n"..
"3) 已经是最新版本,无需升级" )
elseif ret == 5 then
log.info("版本号书写错误", "iot平台版本号需要使用xxx.yyy.zzz形式")
else
log.info("不是上面几种情况 ret为", ret)
end
end
libfota2.request(fota_cb)
libfota2.version()
功能 获取库文件版本信息
参数
无参数
返回值
local version= libfota2.version()
log.info("libfota2", "version -> " .. version)
version
含义说明:库文件版本信息,string类型的年月日时分;
数据类型:string;
取值范围:12位数字;
返回示例:"202607021200"
示例
--返回string类型的年月日时分,例如:"202607021200"
libfota2.version()
五、版本更新说明
版本号:202607021200
1、更新时间:2026-07-02 12:00
2、更新内容
- 新增libfota2.version()接口
- 支持libfota2库文件版本号管理功能,版本号的格式为:yyyymmddhhmm,表示yyyy年mm月dd日hh时mm分发布的版本
六、产品支持说明
支持LuatOS开发的所有产品都支持libfota2扩展库。