不废话,直接上代码吧
--- 模块功能:阿里云功能测试.
-- 支持数据传输和OTA功能
-- @author openLuat
-- @module aLiYun.testALiYun
-- @license MIT
-- @copyright openLuat
-- @release 2018.04.14
module(...,package.seeall)
require "aLiYun"
require "misc"
require "nvm"
--阿里云华东2站点上创建的产品的ProductKey,用户根据实际值自行修改
--local PRODUCT_KEY = "你自己的产品key"
--采用“一机一密”认证方案除了上面的PRODUCT_KEY外,还需要设备名称和设备密钥
--设备名称使用函数getDeviceName的返回值,默认为设备的IMEI
--设备密钥使用函数getDeviceSecret的返回值,默认为设备的SN
--单体测试时,可以直接修改getDeviceName和getDeviceSecret的返回值
--批量量产时,使用设备的IMEI和SN;合宙生产的模块,都有唯一的IMEI,用户可以在自己的产线批量写入跟IMEI(设备名称)对应的SN(设备密钥)
--或者用户自建一个服务器,设备上报IMEI给服务器,服务器返回对应的设备密钥,然后调用misc.setSn接口写到设备的SN中
local PRODUCT_KEY = "你自己的产品key"
local PRODUCE_SECRET="你自己的产品密钥"
local Shadow_Changed = false
--采用“一型一密”认证方案时打开上面的注释,比“一机一密”认证方式额外提供提供PRODUCE_SECRET,PRODUCE_SECRET的值根据实际值自行修改
--设备请求接入时,云端动态下发该设备的DeviceSecret,DeviceSecret的保存方法默认使用setDeviceSecret将设备的SN替换成DeviceSecret
--之后设备密钥的获取便使用函数getDeviceSecret的返回值,然后使用ProductKey、DeviceName和DeviceSecret进行认证并建立连接。
--请参考126行的aLiYun.setup(PRODUCT_KEY,PRODUCE_SECRET,getDeviceName,getDeviceSecret,setDeviceSecret)去配置参数
--[[
函数名:getDeviceName
功能 :获取设备名称
参数 :无
返回值:设备名称
]]
local function getDeviceName()
--默认使用设备的IMEI作为设备名称
--用户单体测试时,可以在此处直接返回阿里云的iot控制台上注册的设备名称,例如return "868575021150844"
--return "Air202Test13"
--return "862991419835241"
return misc.getImei()
end
--[[
函数名:setDeviceSecret
功能 :修改设备密钥
参数 :设备密钥
返回值:无
]]
local function setDeviceSecret(s)
--默认使用设备的SN作为设备密钥
misc.setSn(s)
end
--[[
函数名:getDeviceSecret
功能 :获取设备密钥
参数 :无
返回值:设备密钥
]]
local function getDeviceSecret()
--默认使用设备的SN作为设备密钥
--用户单体测试时,可以在此处直接返回阿里云的iot控制台上生成的设备密钥,例如return "y7MTCG6Gk33Ux26bbWSpANl4OaI0bg5Q"
--return "y7MTCG6Gk33Ux26bbWSpANl4OaI0bg5Q"
return misc.getSn()
end
--阿里云客户端是否处于连接状态
sConnected = false
--[[
函数名:pubqos1testackcb
功能 :发布1条qos为1的消息后收到PUBACK的回调函数
参数 :
usertag:调用mqttclient:publish时传入的usertag
result:true表示发布成功,false或者nil表示失败
返回值:无
]]
local function publishTestCb(result,para)
log.info("myALiYun.publishTestCb",result,para)
end
--发布一条shadow更新消息
function updateShadow(desired)
if sConnected then
desired["rssi"] = net.getRssi()
desired["PowerSwitch"] = nvm.get("PowerSwitch")
local jdata = json.encode(desired)
_G.shadow_version = _G.shadow_version + 1
local resp = "{\"method\":\"update\",\"state\": {\"reported\":" .. jdata .."},\"version\":".._G.shadow_version.."}"
aLiYun.publish("/shadow/update/"..PRODUCT_KEY.."/"..getDeviceName(), resp, 1,publishTestCb, resp)
else
log.info("sorry, sConnected=false")
end
end
---数据接收的处理函数
-- @string topic,UTF8编码的消息主题
-- @number qos,消息质量等级
-- @string payload,原始编码的消息负载
local function rcvCbFnc(topic,qos,payload)
log.info("myALiYun.rcvCbFnc",topic,qos,payload)
if topic == "/shadow/get/"..PRODUCT_KEY.."/"..getDeviceName() then
local shadow,result,errinfo = json.decode(payload)
if result then
log.info("Shadow Method=" .. shadow["method"])
if shadow["version"] ~= nil then
_G.shadow_version = shadow["version"]
log.info("Shadow Version = " .. _G.shadow_version)
end
local desired = nil
if shadow["method"] == "reply" and shadow["payload"]["state"] ~= nil and shadow["payload"]["state"]["desired"] ~= nil then
return
end
if shadow["method"] == "control" and shadow["payload"]["state"] ~= nil and shadow["payload"]["state"]["desired"] ~= nil then
desired = shadow["payload"]["state"]["desired"]
end
if desired ~= nil then
-- reply如果有数据,那么肯定是get请求, 要更新本地状态
log.info("Shadow control/reply begin ...")
--if shadow["payload"]["status"] == "success" then
local tmp = json.encode(desired)
log.info("desired=" .. tmp)
Shadow_Changed = false
-- 这里写业务逻辑
if Shadow_Changed then
updateShadow(desired)
end
--else
-- log.info("desired is emtry")
--end
--end
log.info("Shadow control ... end")
end
else
log.info("Bad Shadow JSON!")
end
return
end
local tjsondata,result,errinfo = json.decode(payload)
if result then
-- 处理mqtt payload
else
log.info("BAD JSON")
end
end
--- 连接结果的处理函数
-- @bool result,连接结果,true表示连接成功,false或者nil表示连接失败
local function connectCbFnc(result)
log.info("testALiYun.connectCbFnc",result)
sConnected = result
if result then
--订阅主题,不需要考虑订阅结果,如果订阅失败,aLiYun库中会自动重连
aLiYun.subscribe({["/"..PRODUCT_KEY.."/"..getDeviceName().."/get"]=0,
["/"..PRODUCT_KEY.."/"..getDeviceName().."/get"]=1,
["/shadow/get/"..PRODUCT_KEY.."/"..getDeviceName()]=0})
--注册数据接收的处理函数
aLiYun.on("receive",rcvCbFnc)
--PUBLISH消息测试
aLiYun.publish("/shadow/update/"..PRODUCT_KEY.."/"..getDeviceName(), "{\"method\":\"get\"}", 1,publishTestCb, "{\"method\":\"get\"}")
end
end
--配置产品key,设备名称和设备密钥;采用一机一密的认证方式是,第二个参数传入nil,采用一型一密认证方式时,需要PRODUCE_SECRET,并提供第五个参数
--注意:如果使用imei和sn作为设备名称和设备证书时,不要把getDeviceName和getDeviceSecret替换为misc.getImei()和misc.getSn()
--注意:采用一型一密认证方式时,仅在首次激活时动态下发DeviceSecret
--因为开机就调用misc.getImei()和misc.getSn(),获取不到值
--一机一密
--aLiYun.setup(PRODUCT_KEY,nil,getDeviceName,getDeviceSecret)
--一型一密
aLiYun.setup(PRODUCT_KEY,PRODUCE_SECRET,getDeviceName,getDeviceSecret,setDeviceSecret)
--setMqtt接口不是必须的,aLiYun.lua中有这个接口设置的参数默认值,如果默认值满足不了需求,参考下面注释掉的代码,去设置参数
--aLiYun.setMqtt(1, nil, 65)
aLiYun.on("connect",connectCbFnc)
--要使用阿里云OTA功能,必须参考本文件124或者126行aLiYun.setup去配置参数
--然后加载阿里云OTA功能模块(打开下面的代码注释)
require"aLiYunOta"
流程
- 开机
- 连接阿里云
- 定义shadow更新的topic
- 发送一个{“method”:”get”}到/shadow/update的topic,以获取最新的shadow version
- 阿里云下发最新的shadow,读取verison,更新本地状态
- 根据业务逻辑发送最新shadow或者处理下发的shadow状态