跳转至

FOTA升级

一、FOTA 介绍

FOTA 即远程升级功能,此功能可以让客户在不方便大量线刷升级(设备不在身边/量产 PCB 没引出 USB/需要大批量进行功能升级)的情况下,快速进行底层固件/脚本/脚本 + 底层固件的远程更新。

LuatOS 开发模式下,固件分为两部分:core 和 script

远程升级时:升级 script 和 core+script 为全量覆盖升级

远程升级时:可以仅升级 script;也可以同时升级 core+script

支持合宙 iot 平台升级和自建第三方服务器(HTTP)升级

二、演示功能概述

本 demo 教你怎么使用 LuatOS 脚本语言,就可以让 Air8101 开发板实现远程升级。

三、准备硬件环境

“古人云:‘工欲善其事,必先利其器。’在深入介绍本功能示例之前,我们首先需要确保以下硬件环境的准备工作已经完成。”

参考:硬件环境清单,准备以及组装好硬件环境。

四、软件环境

“凡事预则立,不预则废。”在详细阐述本功能示例之前,我们需先精心筹备好以下软件环境。

  1. Luatools 工具
  2. 内核固件文件(底层 core 固件文件):LuatOS-SoC_V10001_Air8101.soc;参考项目使用的内核固件
  3. luatos 需要的脚本和资源文件

脚本和资源文件:https://gitee.com/openLuat/LuatOS-Air8101/tree/master/demo/FOTA

lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;

准备好软件环境之后,接下来查看如何烧录项目文件到 Air8101 开发板,将本篇文章中演示使用的项目文件烧录到 Air8101 开发板中。

五、 合宙自有服务器 FOTA 简介

5.1 云平台配置

使用合宙自建服务器的话,需要先登录合宙 IOT 平台,如下图所示,没有账号的,可以先注册一个 客户向合宙采购 4G 模块时,如果采购人员没有告知合宙这批模块放在 iot.openluat.com 上的哪个产品下,则合宙会以采购人的手机号为账号,默认密码 888888,创建一个“AirXX 标准模块”的项目,此次采购的所有模块都会放在这个项目下,如果你的账号下没有对应 imei,可以联系合宙销售帮忙添加模块进对应项目下(最好还是从哪里买的模块,就让他给你转移到你自己名下)

登录以后点击红框所示位置

然后依次点击如下图所示红框所示的地方,创建一个新项目

在所有项目的最后,找到自己刚刚新建的项目,并且点击红框内的"查看/点击复制"复制后面升级所需要的校验码,复制到自己剪切板中。代码中需要填写。

PRODUCT_KEY = "q01f89YFOFYxluL05jMI6aiIlKJy1SYA"

至此,合宙云平台上的预备动作就做完了

5.2 仅升级脚本

5.2.1 远程升级文件制作

打开 Luatools 的项目管理界面,点击生成量产文件,纯脚本升级文件放在 luatools 根目录下的"SOC 量产及远程升级文件\Air8101"目录下

生成文件如下

因为模块烧录的是 001.000.000 版本,所以我们需要给脚本里的版本号改一下,改为 001.000.001 版本

再将脚本中增加几行打印(为了模拟用户修改脚本的动作)

然后重新生成一次量产固件

修改文件名称为:fotademo_001.000.001_LuatOS-SoC_V0002_Air8101.bin

打开刚刚的合宙 iot 平台点击固件升级--我的固件--创建固件

点击选择文件,把刚刚的 bin 后缀文件上传到 iot 平台

文件名、固件名、版本号都是自动识别的,用户无需修改

点击确定,等待上传成功的动画提示

上传完成界面

5.2.2 指定设备

模块刚开机,luatools 会打印模块的 STA MAC 号,如下图所示,为 C8478CB8242E 记录下来

在刚刚创建固件的地方,点击指定设备按钮,如下图所示

然后按下图所示,添加模块的 imei,注意这里填写 STA MAC 号,即 C8478CB8242E 。

添加完成如图所示

5.2.3 重启设备开始远程升级

启动设备检查升级包

升级中:模块请求升级,下载完升级包以后会进行 MD5 验证升级包有无问题,如果没问题,就会启动重启程序,然后进行升级工作

升级完成的日志

5.3 同时升级 core+script

每一次 core 的升级都会带来一些网络上的优化(例如信号差时的网络稳定性)以及一些 bug 修复,所以在发布新版本以后,用户可以先测试下 core 对自己脚本有无明显影响或性能提升,然后进行远程 FOTA

使用的 DEMO 程序与仅升级脚本的程序相同,直接使用即可。

设计的固件如下所示,具体生成方法参见 5.2.1 中

由于 V10002 已经本文写作时最新版本的 core 了,所以为了演示方便,001.000.000 脚本选择搭配的 core 为 V10001 版本,点击"下载底层和脚本"

添加固件

点击确定如下图所示

更新固件后重启,开始远程升级

升级完成自动重启设备

六、代码示例介绍

6.1 API 介绍

软件 API 参考 https://docs.openluat.com/air780e/luatos/api/ext/libfota2/

libfota.request(cbFnc, opts)

fota 升级

参数

**传入值类型**
**解释**
table
fota参数
function
cbFnc 用户回调函数,回调函数的调用形式为:cbFnc(result) , 必须传

返回值

**返回值类型**
**解释**
nil
无返回值

例子

6.2 核心脚本代码详解

6.2.1 系统初始化与联网

该部分代码尝试通过 Wi-Fi 连接到指定的 SSID 和密码。如果设备支持 Wi-Fi 并能成功连接,会等待 Wi-Fi 网络连接成功并发布 net_ready 消息。

sys.taskInit(function()
    if wlan and wlan.connect then
        local ssid = "Xiaomi_1100"
        local password = "1234567890"
        wlan.init()
        wlan.setMode(wlan.STATION)
        wlan.connect(ssid, password, 1)
        local result, data = sys.waitUntil("IP_READY")
        device_id = wlan.getMac()
    else
        while 1 do
            sys.wait(1000)
            log.info("bsp", "本bsp可能未适配网络层, 请查证")
        end
    end
    sys.publish("net_ready")
end)

6.2.2 版本号打印

该部分代码每 5 秒打印一次当前的脚本版本号和底层操作系统(rtos.version())的版本号。这对于调试和查看版本更新是否正常有帮助。

sys.taskInit(function()
    while 1 do
        sys.wait(5000)
        log.info("fota", "脚本版本号", VERSION, "core版本号", rtos.version())
    end
end)

6.2.3 FOTA 升级回调函数

该部分代码首先检查 PRODUCT_KEY 是否正确配置,如果不正确,则进入死循环提示用户修改正确的密钥。

然后,等待网络连接成功,开始检查是否有可用的 FOTA 升级。

libfota2.request(fota_cb, ota_opts) 触发 FOTA 升级请求,ota_opts 是 OTA 请求的配置参数。

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.info("接收报文错误", "检查模块固件或升级包内文件是否正常")
    elseif ret == 5 then
        log.info("版本号书写错误", "iot平台版本号需要使用xxx.yyy.zzz形式")
    else
        log.info("不是上面几种情况 ret为",ret)
    end
end
fota_cb  FOTA 请求的回调函数,根据返回值 ret 打印不同的日志信息。
0: 升级成功,触发设备重启。
1: 连接失败,提示检查 URL 拼写或服务器配置。
2: URL 错误,提示检查 URL 拼写。
3: 服务器断开,提示检查服务器的白名单配置。
4: 接收报文错误,提示检查模块固件或升级包文件是否正常。
5: 版本号错误,提示使用符合格式 xxx.yyy.zzz 的版本号。
OTA 升级请求与定时检查
lua
sys.taskInit(function()
    if "123" == _G.PRODUCT_KEY and not ota_opts.url then
        while 1 do
            sys.wait(1000)
            log.info("fota", "请修改正确的PRODUCT_KEY")
        end
    end
    sys.waitUntil("net_ready")
    log.info("开始检查升级")
    sys.wait(500)
    libfota2.request(fota_cb, ota_opts)
end)

6.2.4 定时自动升级

该行代码通过 sys.timerLoopStart 设置每 4 小时自动检查一次升级。

sys.timerLoopStart(libfota2.request, 4 * 3600000, fota_cb, ota_opts)

6.3 完整程序清单

注:完整复制后保存为 main.lua,可直接使用

-- main.lua文件
-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "fota_demo"

-- iot限制,只能上传xxx.yyy.zzz格式的三位数的版本号,但实际上现在只用了XXX和ZZZ,中间yyy暂未使用
-- 需要注意的是,因为yyy不生效,所以111.222.333版本和111.444.333版本,对iot平台来说都一样,所以建议中间那一位永远写000
VERSION = "001.000.000"

-- 使用合宙iot平台时需要这个参数
PRODUCT_KEY = "q01f89YFOFYxluL05jMI6aiIlKJy1SYA" -- 到 iot.openluat.com 创建项目,获取正确的项目key(刚刚剪切板里的校验码就填这里)

sys = require "sys"
libfota2 = require "libfota2"

-- 联网函数, 可自行删减
sys.taskInit(function()
        -----------------------------
        -- 统一联网函数, 可自行删减
        ----------------------------
        if wlan and wlan.connect then
            -- wifi 联网, Air8101系列均支持
            local ssid = "Xiaomi_1100"
            local password = "1234567890"
            log.info("wifi", ssid, password)

            wlan.init()
            wlan.setMode(wlan.STATION)
            wlan.connect(ssid, password, 1)
            --等待WIFI联网结果,WIFI联网成功后,内核固件会产生一个"IP_READY"消息
            local result, data = sys.waitUntil("IP_READY")
            log.info("wlan", "IP_READY", result, data)
            device_id = wlan.getMac()

        else
             -- 其他不认识的bsp, 循环提示一下吧
             while 1 do
                sys.wait(1000)
                log.info("bsp", "本bsp可能未适配网络层, 请查证")
            end
        end
        log.info("已联网")

        sys.publish("net_ready")
    end)

-- 循环打印版本号, 方便看版本号变化, 非必须
sys.taskInit(function()
    while 1 do
        sys.wait(5000)
        log.info("降功耗,找合宙")
        log.info("fota", "脚本版本号", VERSION, "core版本号", rtos.version())
    end
end)

-- 升级结果的回调函数
-- 功能:获取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.info("接收报文错误", "检查模块固件或升级包内文件是否正常")
    elseif ret == 5 then
        log.info("版本号书写错误", "iot平台版本号需要使用xxx.yyy.zzz形式")
    else
        log.info("不是上面几种情况 ret为",ret)
    end
end

-- 使用合宙iot平台进行升级,不需要管下面这段代码
-- 使用第三方服务器时打开下面这段代码
local ota_opts = {
    --url = "",
    -- 合宙IOT平台的默认升级URL, 不填就是这个默认值
    -- 如果是自建的OTA服务器, 则需要填写正确的URL, 例如 http://192.168.1.5:8000/update
    -- 如果自建OTA服务器,且url包含全部参数,不需要额外添加参数, 请在url前面添加 ###
    -- 如果不加###,则默认会上传如下参数
    -- 1. opts.version string 版本号, 默认是 BSP版本号.x.z格式
    -- 2. opts.timeout int 请求超时时间, 默认300000毫秒,单位毫秒
    -- 3. opts.project_key string 合宙IOT平台的项目key, 默认取全局变量PRODUCT_KEY. 自建服务器不用填
    -- 4. opts.imei string 设备识别码, 默认取IMEI(Cat.1模块)或WLAN MAC地址(wifi模块)或MCU唯一ID
    -- 5. opts.firmware_name string 底层版本号
    -- 请求的版本号, 合宙IOT有一套版本号体系,不传就是合宙规则, 自建服务器的话当然是自行约定版本号了
    --version = ""
    -- 其他更多参数, 请查阅libfota2的文档 https://wiki.luatos.com/api/libs/libfota2.html
}

sys.taskInit(function()
    -- 这个判断是提醒要设置PRODUCT_KEY的,实际生产请删除
    if "123" == _G.PRODUCT_KEY and not ota_opts.url then
        while 1 do
            sys.wait(1000)
            log.info("fota", "请修改正确的PRODUCT_KEY")
        end
    end
    -- 等待网络就行后开始检查升级
    sys.waitUntil("net_ready")
    log.info("开始检查升级")
    sys.wait(500)
    libfota2.request(fota_cb, ota_opts)
end)
-- 演示定时自动升级, 每隔4小时自动检查一次
sys.timerLoopStart(libfota2.request, 4 * 3600000, fota_cb, ota_opts)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

七、 总结

至此,我们已使用 Air8101 开发板完成了远程升级的基本功能。