libfota2 - fota升级v2
作者:孟伟
一、概述
FOTA远程升级功能是物联网设备的核心功能之一,它允许设备通过无线网络更新固件,而无需物理接触。在LuatOS开发模式下,固件分为两部分:core(底层核心固件)和script(用户脚本)。远程升级时,core采用差分升级方式,而 script 则采用全量覆盖升级方式。您可以选择仅升级script、仅升级core或同时升级core+script。
LuatOS主要提供了两套FOTA接口:传统的libfota和更新的libfota2,两者都支持通过合宙官方IoT平台和自建第三方HTTP服务器进行升级
需要注意的是第三方http服务器需要满足如下两点:
-- 若需要升级, 响应http 200, body为升级文件的内容
-- 若不需要升级, 响应300或以上的代码,务必注意
为了更清晰地了解这两个库的特点,下表对比了它们的主要特性。
区别项 | libfota2 | libfota |
---|---|---|
参数传递方式 | 使用opts表传递参数,结构清晰,可选参数管理方便 | 参数通过一长串顺序参数传递,必须按顺序填写,可选参数处理不够灵活 |
使用便捷性 | 较高。接口设计更现代,只需关注关键配置,简化了使用流程。 | 相对较低。需要关注较多参数,配置稍显繁琐。 |
功能特性 | 支持合宙IoT平台和自建服务器,定时升级,回调函数提供详细状态码。 | 同样支持合宙IoT平台和自建服务器,定时升级,回调函数提供状态码 |
推荐使用场景 | 新项目,或希望代码更清晰、易维护的项目。 | 旧项目维护,或对早期库有依赖的项目。 |
libfota2 是LuatOS提供的升级版FOTA接口,它通过opts表传递参数,相比传统的libfota库在接口清晰度和易用性上有显著提升。对于新开发的LuatOS项目,建议优先选择libfota2来实现远程升级功能。
二、核心示例
1、核心示例是指:使用本库文件提供的核心API,开发的基础业务逻辑的演示代码;
2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;
3、更加完整和详细的demo,请参考 LuatOS仓库 中各个产品目录下的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(libfota_cb)
-- 若需要定时升级
sys.timerLoopStart(libfota2.request, 4*3600*1000, libfota_cb)
三、常量详解
核心库常量,顾名思义是由合宙LuatOS内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;
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平台的升级地址
-- 如果是自建的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 = ,
-- 参数含义:设置整个 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(libfota_cb)
五、产品支持说明
支持LuatOS开发的所有产品都支持libfota2扩展库。