跳转至

远程固件升级服务

一、FOTA 概述

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

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

远程升级时:core 为差分升级;script 为全量覆盖升级

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

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

二、演示功能概述

本文将详细讲述如何进行远程升级。

三、准备硬件环境

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

3.1 Air780E 开发板

本次使用的开发板为 Air780E 核心板,如下图所示:

点击链接购买:Air780E 核心板淘宝购买链接

此核心板的详细使用说明参考:Air780E 产品手册 中的《开发板 Core_Air780E 使用说明 VX.X.X.pdf》,写这篇文章时最新版本的使用说明为:《开发板 Core_Air780E 使用说明 V1.0.5.pdf》 ;核心板使用过程中遇到任何问题,可以直接参考这份使用说明 pdf 文档。

3.2 SIM 卡

请准备一张可正常上网的 SIM 卡,该卡可以是物联网卡或您的个人手机卡。

特别提醒:请确保 SIM 卡未欠费且网络功能正常,以便顺利进行后续操作。

3.3 PC 电脑

请准备一台配备 USB 接口且能够正常上网的电脑。电脑操作系统为:WIN10 以及以上版本的 WINDOWS 系统(mac 以及 win8 及其以下电脑请更换为 win10 及其以上系统)

3.4 数据通信线

请准备一根用于连接 Air780E 开发板和 PC 电脑的数据线,该数据线将实现业务逻辑的控制与交互。

  • USB 数据线(其一端为 Type-C 接口,用于连接 Air780E 开发板)。通常,这种数据线的外观如下示意图所示:

普通的手机 USB 数据线一般都可以直接使用;

在本教程中,我们将采用 USB 数据线配置进行测试和数据查看:

  • USB 数据线:此数据线不仅用于为测试板供电,还用于查看数据日志。其一端为 Type-C 接口,连接 Air780E 开发板;另一端为标准 USB 接口,连接 PC 电脑。

3.5 组装硬件环境

3.5.1 请按照 SIM 卡槽上的指示方向正确插入 SIM 卡,务必确保插入方向正确,避免插反导致损坏!

通常,插入 SIM 卡的步骤如下:

  • 将 SIM 卡的金属接触面朝下,对准卡槽的开口。
  • 用力平稳地将 SIM 卡推入卡槽,直至听到“咔嚓”一声,表示 SIM 卡已正确安装到位。

3.5.2 USB 数据线,连接电脑和 Air780E 开发板,如下图所示:

四、FOTA 软硬件参考

FOTA 和硬件基本无关,软件 API 参考 https://docs.openluat.com/air780e/luatos/api/ext/libfota2/

五、准备软件环境

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

5.1 Luatools 工具

要想烧录 LUATOS 固件到 4G 模组中,需要用到合宙的强大的调试工具:Luatools(特别注意,luatools 一定要是 3.0.9 及其以后的版本);

下载地址:Luatools v3 下载调试工具

Luatools 工具集具备以下几大核心功能:

  • 一键获取最新固件:自动连接合宙服务器,轻松下载最新的合宙模组固件。
  • 固件与脚本烧录:便捷地将固件及脚本文件烧录至目标模组中。
  • 串口日志管理:实时查看模组通过串口输出的日志信息,并支持保存功能。
  • 串口调试助手:提供简洁的串口调试界面,满足基本的串口通信测试需求。

Luatools 下载之后, 无需安装, 解压到你的硬盘,点击 Luatools_v3.exe 运行,出现如下界面,就代表 Luatools 安装成功了:

5.2 烧录代码

首先要说明一点: 脚本代码, 要和固件的 LuatOS-SoC_V1112_EC618_FULL.soc 文件一起烧录。

5.2.1 烧录所需文件

底层 core+ 脚本 +lib

脚本所在位置:https://gitee.com/openLuat/LuatOS-Air780E/blob/master/demo/fota2/main.lua

lib 所在位置:https://gitee.com/openLuat/LuatOS-Air780E/tree/master/script/libs

5.2.2 找到烧录的固件文件

官网下载,底层 core 下载地址:LuatOS 底层 core 注:本 demo 使用如图所示固件(V1112)以及历史版本中的 V1111

5.2.3 正确连接电脑和 4G 模组电路板

使用带有数据通信功能的数据线,不要使用仅有充电功能的数据线;

5.2.4 识别 4G 模组的 BOOT 引脚

在下载之前,要用模组的 BOOT 引脚触发下载, 也就是说,要把 4G 模组的 BOOT 引脚拉到 1.8v,或者直接把 BOOT 引脚和 VDD_EXT 引脚相连。我们要在按下 BOOT 按键时让模块开机,就可以进入下载模式了。

具体到 Air780E 开发板,

  • 当我们模块没开机时,按着 BOOT 键然后长按 PWR 开机。
  • 当我们模块开机时,按着 BOOT 键然后点按重启键即可。

5.2.5 识别电脑的正确端口

判断是否进入 BOOT 模式:模块上电,此时在电脑的设备管理器中,查看串口设备, 会出现一个端口表示进入了 BOOT 下载模式,如下图所示:

如果设备管理器出现了 3 个连续数字的 com 端口,这时候, 证明硬件连接就绪,模块正常开机且端口识别正常

此时按住 BOOT 按键,再按下重启按键就可以进行烧录了!

5.2.6 新建项目

首先,确保你的 Luatools 的版本大于或者等于 3.0.9 版本.

在 Luatools 的左上角上有版本显示的,如图所示:

Luatools 版本没问题的话, 就点击 Luatools 右上角的“项目管理测试”按钮,如下图所示:

这时会弹出项目管理和烧录管理的对话框,如下图:

点击下载后,我们需要进入 BOOT 模式才能正常下载。

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

FOTA 有多种方式,可以使用合宙的 iot 平台进行升级,也可以使用用户自建平台升级,可以只升级 core,可以只升级用户脚本,也可以 core+ 脚本一起升级,接下来先介绍合宙自有服务器升级,第三方升级在下一篇文章中介绍

6.1 云平台配置

使用合宙自建服务器的话,需要先登录合宙 IOT 平台,如下图所示,没有账号的,可以先注册一个

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

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

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

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

6.2 仅脚本升级简介

如果用户只是新增一些自己的脚本逻辑,没有更新底层,可以选择仅脚本升级

6.2.1 仅脚本升级示例

仅脚本升级 demo 链接

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

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

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

-- 联网函数, 可自行删减
sys.taskInit(function()
    -- 默认都等到联网成功
    sys.waitUntil("IP_READY")
    log.info("4G网络链接成功")
    sys.publish("net_ready")
end)

-- 循环打印版本号, 方便看版本号变化, 非必须
sys.taskInit(function()
    while 1 do
        sys.wait(5000)
        log.info("降功耗,找合宙")
        log.info("fota", "version", 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

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()之后后面不要加任何语句!!!!!

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

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

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

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

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

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

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

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

如果上传了其他文件,会提示"升级文件有误"

模块刚开机,luatools 会打印模块的 imei 号,如下图所示,当然也可以使用手机扫描模块上的二维码获得模块 imei 号,或者将模块屏蔽盖上二维码的下方第一排模块的 imei 号,记录下来

在刚刚创建固件的地方,下拉,找到刚刚创建的升级任务,然后按下图所示,添加模块的 imei

将升级文件上传和添加 imei 工作完成以后,点击 luatools 上的重启模块或者按照代码里的等待 4 小时,触发远程升级逻辑

6.2.2 示例效果展示

升级前:因为没有升级固件,所以请求升级的结果为"已是最新版本",每隔 5S 会打印一句"降功耗找合宙"和当前版本号

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

升级后:升级成功后按我们之前的脚本,每隔 1S,打印五次"降功耗,找合宙"以及当前脚本版本号,可以看出,当前版本号已经由原来的 001.000.000 变为了 001.000.001

当然:升级完成以后,因为脚本中有联网就去请求一次升级的代码,所以,会再去请求一次升级,因为没有更高的版本了,所以会上报"已是最新版本"

6.3 含 core 升级简介

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

6.3.1 含 core 升级测试 demo

demo 示例(和上文几乎一样,只是每秒的打印增加了一个 core 版本号)

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

-- 使用合宙iot平台时需要这个参数
PRODUCT_KEY = "UFsrnKt6g3h1IKV7ryKkpZnGrjnPg0xR" -- 到 iot.openluat.com 创建项目,获取正确的项目id

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

-- 联网函数, 可自行删减
sys.taskInit(function()
    -- 默认都等到联网成功
    sys.waitUntil("IP_READY")
    log.info("4G网络链接成功")
    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

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()之后后面不要加任何语句!!!!!

由于 1112 已经本文写作时最新版本的 core 了,所以为了演示方便,001.000.000 脚本选择搭配的 core 为 V1111 版本,再次点击"生成量产文件",稍等即可在 luatools 根目录下的"SOC 量产及远程升级文件\EC618"目录下,找到 core 版本号为 1111 且脚本版本号为 001.000.000 的 soc 后缀文件

生成了这个版本以后,再去脚本中改动脚本版本号为 001.000.003(只要前后两位有一位大于 1,一位大于 0 即可(当然第一位大于 1 的时候,整个版本号自然大于 001.000.000),如 002.000.200,001.000.001,030.000.311 等)

脚本版本号分为 A.B.C 三段;

因为历史原因,中间这一段 B 没有任何意义,但是必须存在;

假设旧的脚本版本号为 A1.B1.C1,新的脚本版本号为 A2.B2.C2;

当新旧脚本版本号满足以下任何一种条件时,版本号允许升级:

1、A2 等于 A1,同时 C2 大于 C1;

2、A2 大于 A1,同时 C2 大于或者等于 C1;

3、B1 B2 无意义

然后在 luatools 里重新选择 core 为 V1112 版本,同样点击生成固件(注:即使是同一个版本号的 core,因为功能着重点的不同,依然分为了好几个 core,可以用后缀去区分这几个版本,关于不同 core 之间不同的功能着重点,可以点击这里查看,远程升级时,不同后缀的 core 不能进行相互间的升级),稍等即可在在 luatools 根目录下的"SOC 量产及远程升级文件\EC618"目录下,找到 core 版本号为 1112 且脚本版本号为 001.000.003 的 soc 后缀文件

接下来就是针对这两个量产文件,制作一个差分文件,用来远程升级(注:远程升级中 core 为差分,脚本为全量升级)

点击到 luatools 的主界面,依次点击图中蓝框所示意的地方(注:必须使用 luatools_3.0.9 及其以上版本,要不差分包升级的时候可能会出问题)

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

如果生成差分固件的时候提示"固件超过大小"首先检查下自己的新旧固件,如下图就是错误选择了新版本 binpkg 固件。

因为包含了 core 中的改动,所以时间会稍长一些,luatools 可能会出现"假死"情况,不要关闭它,稍等一会即可看到

同样,在你选择的目录下(没选择的在 luatools 根目录下)看到如下所示,带着脚本的 PROJECT core 版本号 脚本版本号的 bin 后缀的差分文件。

至于 iot 平台的配置还是和上文一样的步骤

登录 iot 平台

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

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

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

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

如果上传了其他文件,会提示"升级文件有误"

模块刚开机,luatools 会打印模块的 imei 号,如下图所示,当然也可以使用手机扫描模块上的二维码获得模块 imei 号,或者在模块屏蔽盖 二维码的下方第一排就是模块的 imei 号,记录下来

在刚刚创建固件的地方,下拉,找到刚刚创建的升级任务,然后按下图所示,添加模块的 imei

将升级文件上传和添加 imei 工作完成以后,为了防止模块当前固件不是最早的 fotademo_001.000.000_LuatOS-SoC_V1111_EC618 版本,可以点击 luatools 主界面右边的"下载固件"选择最早的 001.000.000 版本,按住 BOOT 重启模块,然后点击下载,将最早的固件下载进模块里

6.3.2 示例效果展示

升级前:模块打印当前脚本版本号和 core 版本号

升级中下载差分包,校验差分包完成后就会重启模块进入真正的升级逻辑

因为升级包带 core,所以从重启命令执行下去,到最终升级完成,模块重新开机打印了第一条开机日志,中间隔了快一分钟,如果 core 中代码改动较多,两分钟三分钟都是正常的

七、常见问题

7.1 为什么升级后我的模块没有任何反应了,像是变砖一样

有多种可能,

7.1.1 检查脚本

首先先检查下用户自己的脚本,有可能是引起重启/死机的代码写在了最前面,例如新加的某个值或者函数为 nil 但是还是去做了些加减乘除或者判断大小的逻辑。可以直接本地烧录下新版本的 core+ 脚本验证,如果有 fskv 等用到 flash 的代码,可能需要仔细检查才能排除问题,比如下载的时候勾选如下图所示的两个选项。

7.1.2 检查 core

如果是仅脚本升级,但是没注意使用了新 core 中才有的接口,就有可能引起循环重启,如果重启在代码最开头,模块可能来不及打印任何日志就重启了,可以直接本地烧录下新版本的 core+ 脚本验证,如果有 fskv 等用到 flash 的代码,可能需要仔细检查。

7.2 检查过脚本和 core,没问题,为什么会循环升级 6 次以后禁止升级

检查下升级包是否正常,有时候因为人员误操作,经常会出现旧脚本 + 新 core 或者新脚本 + 旧 core 的意外组合,

例如:

本来应该如下表描述的一样


脚本版本号
core 版本号
旧版本
001.000.000
V1111
新版本
001.000.005
v1112

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


脚本版本号
core 版本号
误操作旧版本(1)
001.000.005
V1111
误操作旧版本(2)
001.000.000
V1112
误操作新版本(1)
001.000.000
V1112
误操作新版本(2)
001.000.005
V1111

然后误操作旧版本(1) 和误操作新版本(1)进行差分,这样虽然脚本版本号旧版本大于了新版本,但是 core 的旧版本小于新版本,所以升级平台依旧认为是依次有效的升级,下发了升级包。

升级完成后,模块内部脚本版本号变成了 001.000.000 core 版本号为 V1112 ,下次模块请求升级的时候,当前固件上报的脚本版本号(001.000.000)依旧小于云平台存储的脚本版本号(001.000.005),然后继续下发升级包,就这么循环 6 次,然后触发合宙 iot 平台的禁止升级规则

在正确生成差分包,并且上传成功后,可以在 iot 平台里解除禁止升级的限制

在"我的设备"中选择升级 imei 所在的项目,然后点击右边的"解除禁止升级",

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

7.3 我想在服务器发送特定的字符串如"update"时再触发升级,应该怎么做

只需要在你希望的升级升级逻辑后面加上升级语句即可,例如 mqtt 的 demo 里增加几句话

elseif event == "recv" then
    libfota2 = require "libfota2"
    log.info("mqtt", "downlink", "topic", data, "payload", payload)
    --假定mqtt发过来的字符串为"update"就启动升级
    if payload =="update" then
        libfota2.request()
    end
    sys.publish("mqtt_payload", data, payload)

又或者希望按键升级:

--这里假定使用GPIO0进行按键升级
   gpio.debounce(0, 3000, 1)
   gpio.setup(0, function()
   libfota2.request()
   end, gpio.PULLUP)

给读者的话

本篇文章由 黄何 开发;

本篇文章描述的内容,如果有错误、细节缺失、细节不清晰或者其他任何问题,总之就是无法解决您遇到的问题; 请先登录合宙技术交流论坛,然后点击右边链接文档找错赢奖金-Air780E-LuatOS-软件指南-LuatOS基础-FOTA升级 用截图标注 + 文字描述的方式跟帖回复,记录清楚您发现的问题;

我们会迅速核实并且修改文档;

同时也会为您累计找错积分,您还可能赢取月度找错奖金!