| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- --- 模块功能:阿里云物联网套件客户端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
|