最终提交
This commit is contained in:
255
4G/源代码/lib/aLiYunOta.lua
Normal file
255
4G/源代码/lib/aLiYunOta.lua
Normal file
@@ -0,0 +1,255 @@
|
||||
--- 模块功能:阿里云物联网套件客户端OTA功能.
|
||||
-- 目前固件签名算法仅支持MD5
|
||||
-- @module aLiYunOta
|
||||
-- @author openLuat
|
||||
-- @license MIT
|
||||
-- @copyright openLuat
|
||||
-- @release 2018.04.16
|
||||
|
||||
require"log"
|
||||
require"http"
|
||||
|
||||
module(..., package.seeall)
|
||||
|
||||
--gVersion:固件版本号字符串,如果用户没有调用本文件的setVer接口设置,则默认为_G.PROJECT.."_".._G.VERSION.."_"..sys.getcorever()
|
||||
--gName:阿里云iot网站上配置的新固件文件下载后,在模块中的保存路径,如果用户没有调用本文件的setName接口设置,则默认为/luazip/update.bin
|
||||
--gCb:新固件下载成功后,要执行的回调函数
|
||||
local gVersion,gName,gCb = _G.PROJECT.."_".._G.VERSION.."_"..rtos.get_version()
|
||||
local gFilePath,gFileSize
|
||||
local processed = 0
|
||||
local flowMd5
|
||||
--productKey:产品标识
|
||||
--deviceName:设备名称
|
||||
local productKey,deviceName
|
||||
|
||||
--verRpted:版本号是否已经上报
|
||||
local verRpted,sConnected
|
||||
--lastStep:最后一次上报的下载新固件的进度
|
||||
local lastStep
|
||||
--下载中标志
|
||||
local downloading
|
||||
|
||||
local function otaCb(result,filePath,md5,size)
|
||||
log.info("aLiYunOta.otaCb",gCb,result,filePath,size,(type(gName) =="string") and io.fileSize(filePath) or "function")
|
||||
downloading = false
|
||||
--校验MD5
|
||||
if result then
|
||||
if type(gName) == "string" then
|
||||
local calMD5 = crypto.md5(filePath,"file")
|
||||
result = (string.upper(calMD5) == string.upper(md5))
|
||||
log.info("aLiYunOta.otaCb cmp md5",result,calMD5,md5)
|
||||
else
|
||||
local calMD5 = flowMd5:hexdigest()
|
||||
result = (string.upper(calMD5) == string.upper(md5))
|
||||
log.info("aLiYunOta.otaCb cmp md5",result,calMD5,md5)
|
||||
end
|
||||
end
|
||||
|
||||
rtos.fota_end()
|
||||
if not result then
|
||||
rtos.fota_start()
|
||||
rtos.fota_end()
|
||||
end
|
||||
|
||||
if gCb then
|
||||
gCb(result,filePath)
|
||||
else
|
||||
if result then
|
||||
sys.restart("ALIYUN_OTA")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:upgradeStepRpt
|
||||
功能 :新固件文件下载进度上报
|
||||
参数 :
|
||||
step:1到100代表下载进度比;-2代表下载失败
|
||||
desc:描述信息,可为空或者nil
|
||||
返回值:无
|
||||
]]
|
||||
local function upgradeStepRpt(step,desc)
|
||||
log.info("aLiYunOta.upgradeStepRpt",step,desc,sConnected)
|
||||
if sConnected then
|
||||
if step<=0 or step==100 then sys.timerStop(getPercent) end
|
||||
lastStep = step
|
||||
aLiYun.publish("/ota/device/progress/"..productKey.."/"..deviceName,"{\"id\":1,\"params\":{\"step\":\""..step.."\",\"desc\":\""..(desc or "").."\"}}")
|
||||
end
|
||||
end
|
||||
|
||||
function getPercent()
|
||||
local step
|
||||
if type(gName) == "string" then
|
||||
step = io.fileSize(gName)*100/gFileSize
|
||||
else
|
||||
step = processed*100/gFileSize
|
||||
end
|
||||
log.info("aLiYunOta.getPercent",step)
|
||||
if step~=0 and step~=lastStep then
|
||||
upgradeStepRpt(step)
|
||||
end
|
||||
sys.timerStart(getPercent,5000)
|
||||
end
|
||||
|
||||
local function downloadCbFnc(result,prompt,head,filePath)
|
||||
log.info("aLiYunOta.downloadCbFnc",result,prompt,filePath)
|
||||
sys.publish("ALIYUN_OTA_DOWNLOAD_IND",result)
|
||||
end
|
||||
|
||||
local function saveUpdata(pdata,binlen,statusCode)
|
||||
log.info("saveUpdata",binlen,statusCode)
|
||||
if statusCode == "206" and pdata and binlen then
|
||||
if rtos.fota_process(pdata,binlen) ~=0 then --返回-94表示没有进行初始化,需要调用rtos.fota_start()进行更新初始化
|
||||
log.info("updata.fota_process","fail!!")
|
||||
return
|
||||
else
|
||||
flowMd5:update(pdata)
|
||||
--打印此升级包的长度跟总包长度
|
||||
processed = processed + pdata:len()
|
||||
log.info("updata.fota_process",processed,binlen)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function downloadTask(url,size,md5,ver)
|
||||
log.info("aLiYunOta.downloadTask1",downloading,url,size,md5)
|
||||
if not downloading then
|
||||
downloading = true
|
||||
gFileSize = size
|
||||
|
||||
local rangeBegin,retryCnt = 0,0
|
||||
sys.timerStart(getPercent,5000)
|
||||
sys.publish("LIB_ALIYUN_OTA_DOWNLOAD_BEGIN",ver)
|
||||
while true do
|
||||
if not gName then
|
||||
gName = saveUpdata
|
||||
end
|
||||
http.request("GET",url,nil,{["Range"]="bytes="..rangeBegin.."-"},"",20000,downloadCbFnc,gName)
|
||||
--if rangeBegin==0 then os.remove(gName) end
|
||||
local _,result = sys.waitUntil("ALIYUN_OTA_DOWNLOAD_IND")
|
||||
log.info("aLiYunOta.downloadTask2",result)
|
||||
if result then
|
||||
upgradeStepRpt(100,0)
|
||||
sys.timerStart(otaCb,5000,true,gName,md5,size)
|
||||
break
|
||||
else
|
||||
retryCnt = retryCnt+1
|
||||
if retryCnt>=30 then
|
||||
upgradeStepRpt(-2,"timeout")
|
||||
otaCb(false,gName)
|
||||
break
|
||||
end
|
||||
end
|
||||
if type(gName) == "string" then
|
||||
rangeBegin = io.fileSize(gName)
|
||||
else
|
||||
rangeBegin = processed
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
函数名:upgrade
|
||||
功能 :收到云端固件升级通知消息时的回调函数
|
||||
参数 :
|
||||
payload:消息负载(原始编码,收到的payload是什么内容,就是什么内容,没有做任何编码转换)
|
||||
返回值:无
|
||||
]]
|
||||
function upgrade(payload)
|
||||
local result
|
||||
local jsonData, result = json.decode(payload)
|
||||
log.info("aLiYunOta.upgrade", result, payload)
|
||||
if result and jsonData.data and jsonData.data.url then
|
||||
if rtos.fota_start() ~= 0 then
|
||||
log.info("fota_start fail")
|
||||
sys.timerStart(verRpt, 10000)
|
||||
return
|
||||
end
|
||||
flowMd5 = crypto.flow_md5()
|
||||
sys.taskInit(downloadTask, jsonData.data.url, jsonData.data.size, jsonData.data.md5, jsonData.data.version)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
函数名:verRptCb
|
||||
功能 :上报固件版本号给云端后,收到PUBACK时的回调函数
|
||||
参数 :
|
||||
result:true表示上报成功,false或者nil表示失败
|
||||
返回值:无
|
||||
]]
|
||||
local function verRptCb(result)
|
||||
log.info("aLiYunOta.verRptCb",result)
|
||||
verRpted = result
|
||||
if not result then sys.timerStart(verRpt,20000) end
|
||||
end
|
||||
|
||||
--[[
|
||||
函数名:verRpt
|
||||
功能 :上报固件版本号给云端
|
||||
参数 :无
|
||||
返回值:无
|
||||
]]
|
||||
function verRpt()
|
||||
log.info("aLiYunOta.verRpt",sConnected,gVersion)
|
||||
if sConnected then
|
||||
aLiYun.publish("/ota/device/inform/"..productKey.."/"..deviceName,"{\"id\":1,\"params\":{\"version\":\""..gVersion.."\"}}",1,verRptCb)
|
||||
end
|
||||
end
|
||||
|
||||
function connectCb(result,key,name)
|
||||
sConnected = result
|
||||
if result then
|
||||
log.info("aLiYunOta.connectCb",verRpted)
|
||||
productKey,deviceName = key,name
|
||||
--订阅主题
|
||||
aLiYun.subscribe({["/ota/device/upgrade/"..key.."/"..name]=0, ["/ota/device/upgrade/"..key.."/"..name]=1})
|
||||
if not verRpted then
|
||||
--上报固件版本号给云端
|
||||
verRpt()
|
||||
end
|
||||
else
|
||||
sys.timerStop(verRpt)
|
||||
end
|
||||
end
|
||||
|
||||
function isDownloading()
|
||||
return downloading
|
||||
end
|
||||
|
||||
--- 设置当前的固件版本号
|
||||
-- @string version 当前固件版本号
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYunOta.setVer("MCU_VERSION_1.0.0")
|
||||
function setVer(version)
|
||||
local oldVer = gVersion
|
||||
gVersion = version
|
||||
if verRpted and version~=oldVer then
|
||||
verRpted = false
|
||||
verRpt()
|
||||
end
|
||||
end
|
||||
|
||||
--- 设置新固件保存的文件名
|
||||
-- @string name 新固件下载后保存的文件名;注意此文件名并不是保存的完整路径,完整路径通过setCb设置的回调函数去获取
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYunOta.setName("MCU_FIRMWARE.bin")
|
||||
function setName(name)
|
||||
gName = name
|
||||
end
|
||||
|
||||
--- 设置新固件下载后的回调函数
|
||||
-- @function cbFnc 新固件下载后的回调函数
|
||||
-- 回调函数的调用形式为:cbFnc(result,filePath),result为下载结果,true表示成功,false或者nil表示失败;filePath为新固件文件保存的完整路径
|
||||
-- @return nil
|
||||
-- @usage
|
||||
-- aLiYunOta.setCb(cbFnc)
|
||||
function setCb(cbFnc)
|
||||
gCb = cbFnc
|
||||
end
|
||||
Reference in New Issue
Block a user