跳转至

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扩展库。