aliyun.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. -- PROJECT = "aliyundemo"
  2. -- VERSION = "1.0.0"
  3. -- local sys = require "sys"
  4. -- sys库是标配
  5. _G.sys = require("sys")
  6. --[[特别注意, 使用mqtt库需要下列语句]]
  7. _G.sysplus = require("sysplus")
  8. aliyun = {}
  9. local ClientId,PassWord,UserName,SetClientidFnc,SetDeviceTokenFnc,SetDeviceSecretFnc
  10. local OutQueue =
  11. {
  12. SUBSCRIBE = {},
  13. PUBLISH = {},
  14. }
  15. local Item = {}
  16. local EvtCb = {}
  17. local mqttc = nil
  18. local Key,Dname
  19. --添加
  20. local function insert(type,topic,qos,payload,cbFnc,cbPara)
  21. table.insert(OutQueue[type],{t=topic,q=qos,p=payload,cb=cbFnc,para=cbPara})
  22. end
  23. --删除
  24. local function remove(type)
  25. if #OutQueue[type]>0 then return table.remove(OutQueue[type],1) end
  26. end
  27. --订阅步骤
  28. local function procSubscribe(client)
  29. local i
  30. for i=1,#OutQueue["SUBSCRIBE"] do
  31. if not client:subscribe(OutQueue["SUBSCRIBE"][i].t , OutQueue["SUBSCRIBE"][i].q) then
  32. OutQueue["SUBSCRIBE"] = {}
  33. return false,"procSubscribe"
  34. end
  35. end
  36. OutQueue["SUBSCRIBE"] = {}
  37. return true
  38. end
  39. --发布
  40. local function procSend(client)
  41. if not procSubscribe(client) then
  42. return false,"procSubscribe"
  43. end
  44. if #OutQueue["PUBLISH"] == 0 then
  45. sys.waitUntil("ALIYUN_PUB")
  46. end
  47. while #OutQueue["PUBLISH"] > 0 do
  48. Item = table.remove(OutQueue["PUBLISH"],1)
  49. local result = client:publish(Item.t,Item.p,Item.q)
  50. if type(result) == nil then
  51. if Item.cb then Item.cb(false,Item.para) end
  52. else
  53. local result, data = sys.waitUntil("PUB_SENT")
  54. if Item.cb then Item.cb(data,Item.para) end
  55. end
  56. end
  57. return true,"procSend"
  58. end
  59. --二次连接
  60. -- local function clientDataTask(ClientId,user,PassWord,mqtt_host,mqtt_port,mqtt_isssl,DeviceName,ProductKey)
  61. local function clientDataTask(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,passtoken,Registration)
  62. sys.taskInit(function()
  63. if not mobile.status() == 1 then
  64. sys.waitUntil("IP_READY",30000)
  65. end
  66. if mobile.status() == 1 then
  67. if not Registration then
  68. local client_id,user_name,password = iotauth.aliyun(ProductKey,DeviceName,SetDeviceSecretFnc)
  69. mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl) --mqtt客户端创建
  70. mqttc:auth(client_id,user_name,password) --mqtt三元组配置
  71. else
  72. mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl) --mqtt客户端创建
  73. mqttc:auth(DeviceName,ProductKey,passtoken) --mqtt三元组配置
  74. end
  75. mqttc:keepalive(30) -- 默认值240s
  76. mqttc:autoreconn(true, 3000) -- 自动重连机制
  77. mqttc:connect()
  78. mqttc:on(mqtt_cbevent) --mqtt回调注册
  79. local conres = sys.waitUntil("mqtt_conack",30000)
  80. if mqttc:ready() and conres then
  81. -- if connectCb then connectCb(true,ProductKey,DeviceName) end
  82. -- if EvtCb["connect"] then EvtCb["connect"](true) end
  83. local result,prompt = procSubscribe(mqttc)
  84. if result then
  85. while true do
  86. procSend(mqttc)
  87. end
  88. end
  89. -- if connectCb then connectCb(false,ProductKey,DeviceName) end
  90. -- if EvtCb["connect"] then EvtCb["connect"](false) end
  91. end
  92. else
  93. --进入飞行模式,20秒之后,退出飞行模式
  94. mobile.flymode(0,true)
  95. sys.wait(20000)
  96. mobile.flymode(0,false)
  97. end
  98. end)
  99. end
  100. --根据返回的数据进行二次加密
  101. local function directProc(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,Registration)
  102. if not Registration then
  103. local ClientId = DeviceName.."|securemode=3,signmethod=hmacmd5,timestamp=789|"
  104. local UserName = DeviceName.."&"..ProductKey
  105. local content = "ClientId"..DeviceName.."deviceName"..DeviceName.."productKey"..ProductKey.."timestamp789"
  106. local signKey= SetDeviceSecretFnc
  107. PassWord = crypto.hmac_md5(content,signKey)
  108. clientDataTask(ClientId,UserName,PassWord,mqtt_host,mqtt_port,mqtt_isssl,DeviceName,ProductKey)
  109. else
  110. local ClientId = SetClientidFnc.."|securemode=-2,authType=connwl|"
  111. local UserName = DeviceName.."&"..ProductKey
  112. local PassWord = SetDeviceTokenFnc
  113. clientDataTask(ClientId,UserName,mqtt_host,mqtt_port,mqtt_isssl,PassWord,Registration)
  114. end
  115. end
  116. --获取预注册和免预注册一型一密一次连接返回的数据
  117. local function clientEncryptionTask(Registration,DeviceName,ProductKey,ProductSecret,InstanceId,mqtt_host,mqtt_port,mqtt_isssl)
  118. sys.taskInit(function()
  119. local tm = os.time()
  120. --一型一密
  121. --预注册
  122. if not Registration then
  123. ClientId = DeviceName.."|securemode=2,authType=register,random="..tm..",signmethod=hmacmd5|"
  124. --免预注册
  125. else
  126. ClientId = DeviceName.."|securemode=-2,authType=regnwl,random="..tm..",signmethod=hmacmd5,instanceId="..InstanceId.."|"
  127. end
  128. UserName = DeviceName.."&"..ProductKey
  129. local content = "deviceName"..DeviceName.."productKey"..ProductKey.."random"..tm
  130. PassWord = crypto.hmac_md5(content,ProductSecret)
  131. local mqttClient = mqtt.create(nil,mqtt_host,mqtt_port,mqtt_isssl) --客户端创建
  132. mqttClient:auth(ClientId,UserName,PassWord) --三元组配置
  133. mqttClient:on(function(mqtt_client, event, data, payload) --mqtt回调注册
  134. -- 用户自定义代码
  135. if event == "conack" then
  136. elseif event == "recv" then
  137. log.info("mqtt", "downlink", "topic", data, "payload", payload)
  138. if payload then
  139. local tJsonDecode,res = json.decode(payload)
  140. if not Registration then
  141. --预注册
  142. if res and tJsonDecode["deviceName"] and tJsonDecode["deviceSecret"] then
  143. SetDeviceSecretFnc = tJsonDecode["deviceSecret"]
  144. mqttClient:disconnect()
  145. -- directProc(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,Registration)
  146. clientDataTask(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl)
  147. end
  148. else
  149. --免预注册
  150. if res and tJsonDecode["deviceName"] and tJsonDecode["deviceToken"] then
  151. SetDeviceTokenFnc = tJsonDecode["deviceToken"]
  152. SetClientidFnc = tJsonDecode["clientId"]
  153. mqttClient:disconnect()
  154. directProc(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,Registration)
  155. end
  156. end
  157. end
  158. elseif event == "sent" then
  159. log.info("mqtt", "sent", "pkgid", data)
  160. end
  161. end)
  162. mqttClient:connect()
  163. end)
  164. end
  165. --底层libMQTT回调函数,上层的回调函数,通过 aliyun.on注册
  166. function mqtt_cbevent(mqtt_client, event, data, payload)
  167. if event == "conack" then
  168. sys.publish("mqtt_conack")
  169. EvtCb["connect"](true)
  170. elseif event == "recv" then -- 服务器下发的数据
  171. log.info("mqtt", "downlink", "topic", data, "payload", payload)
  172. -- log.info("aliyun.procReceive",data,string.toHex(payload))
  173. -- --OTA消息
  174. -- if data =="/ota/device/upgrade/"..Key.."/"..Dname then
  175. -- -- if aliyun and aliyun.upgrade then
  176. -- -- log.info("数据开始传进去upgrade",payload)
  177. -- upgrade(payload)
  178. -- -- end
  179. -- end
  180. if EvtCb["receive"] then
  181. EvtCb["receive"](data, payload)
  182. end
  183. elseif event == "sent" then
  184. if type(data) == "number" then
  185. sys.publish("PUB_SENT",true)
  186. end
  187. elseif event == "disconnect" then
  188. log.info("mqtt", "disconnect")
  189. sys.publish("PUB_SENT",false)
  190. if EvtCb["connect"] then
  191. EvtCb["connect"](false)
  192. end
  193. end
  194. end
  195. --[[
  196. 配置阿里云物联网套件的产品信息和设备信息
  197. @api aliyun.setup(tPara)
  198. @table tPara,填写设备信息的表函数
  199. @usage
  200. 一机一密认证方案时,ProductSecret参数传入nil
  201. 一型一密认证方案时,ProductSecret参数传入真实的产品密钥
  202. tPara[Registration] ,是否是预注册 已预注册为false,未预注册为true
  203. tPara[DeviceName] ,设备名称
  204. tPara[ProductKey] ,产品key
  205. tPara[ProductSecret] ,产品secret,根据此信息判断是一机一密还是一型一密
  206. tPara[DeviceSecret] ,设备secret
  207. tPara[InstanceId] ,如果没有注册需要填写实例id,在实例详情页面
  208. tPara[mqtt_port] ,mqtt端口
  209. tPara[mqtt_isssl] ,是否使用ssl加密连接,true为无证书最简单的加密
  210. ]]
  211. function aliyun.setup(tPara)
  212. mqtt_host = tPara.InstanceId..".mqtt.iothub.aliyuncs.com"
  213. if tPara.ProductSecret == "" or tPara.ProductSecret == nil then
  214. confiDentialTask(tPara.DeviceName,tPara.ProductKey,tPara.DeviceSecret,mqtt_host,tPara.mqtt_port,tPara.mqtt_isssl)
  215. else
  216. clientEncryptionTask(tPara.Registration,tPara.DeviceName,tPara.ProductKey,tPara.ProductSecret,tPara.InstanceId,mqtt_host,tPara.mqtt_port,tPara.mqtt_isssl)
  217. end
  218. end
  219. --一机一密连接 confiDentialTask
  220. function confiDentialTask(DeviceName,ProductKey,DeviceSecret,mqtt_host,mqtt_port,mqtt_isssl)
  221. Key = ProductKey
  222. Dname = DeviceName
  223. sys.taskInit(function()
  224. if not mobile.status() == 1 then
  225. sys.waitUntil("IP_READY",30000)
  226. end
  227. if mobile.status() == 1 then
  228. local client_id,user_name,password = iotauth.aliyun(ProductKey,DeviceName,DeviceSecret)
  229. mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl) --mqtt客户端创建
  230. mqttc:auth(client_id,user_name,password) --mqtt三元组配置
  231. mqttc:keepalive(30) -- 默认值240s
  232. mqttc:autoreconn(true, 3000) -- 自动重连机制
  233. mqttc:connect()
  234. mqttc:on(mqtt_cbevent) --mqtt回调注册
  235. local conres = sys.waitUntil("mqtt_conack",30000)
  236. if mqttc:ready() and conres then
  237. -- if connectCb then connectCb(true,ProductKey,DeviceName) end
  238. -- if EvtCb["connect"] then EvtCb["connect"](true) end
  239. local result,prompt = procSubscribe(mqttc)
  240. if result then
  241. while true do
  242. procSend(mqttc)
  243. end
  244. end
  245. -- if connectCb then connectCb(false,ProductKey,DeviceName) end
  246. -- if EvtCb["connect"] then EvtCb["connect"](false) end
  247. end
  248. else
  249. --进入飞行模式,20秒之后,退出飞行模式
  250. mobile.flymode(0,true)
  251. sys.wait(20000)
  252. mobile.flymode(0,false)
  253. end
  254. end)
  255. end
  256. --正常连接 预注册一型一密获取DeviceSecret后就是正常的一机一密连接
  257. function aliyun.clientGetDirectDataTask(DeviceName,ProductKey,mqtt_host,mqtt_port,mqtt_isssl,Registration,DeviceSecret,deviceToken,cid)
  258. sys.taskInit(function()
  259. if not mobile.status() == 1 then
  260. sys.waitUntil("IP_READY",30000)
  261. end
  262. if mobile.status() == 1 then
  263. if not Registration then
  264. local client_id,user_name,password = iotauth.aliyun(ProductKey,DeviceName,DeviceSecret)
  265. mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl) --mqtt客户端创建
  266. mqttc:auth(client_id,user_name,password) --mqtt三元组配置
  267. else
  268. local clientId = cid.."|securemode=-2,authType=connwl|"
  269. local client_id,user_name,password = iotauth.aliyun(ProductKey,DeviceName,deviceToken)
  270. mqttc = mqtt.create(nil,mqtt_host, mqtt_port,mqtt_isssl) --mqtt客户端创建
  271. mqttc:auth(clientId,user_name,deviceToken) --mqtt三元组配置
  272. end
  273. mqttc:keepalive(30) -- 默认值240s
  274. mqttc:autoreconn(true, 3000) -- 自动重连机制
  275. mqttc:connect()
  276. mqttc:on(mqtt_cbevent) --mqtt回调注册
  277. local conres = sys.waitUntil("mqtt_conack",30000)
  278. if mqttc:ready() and conres then
  279. -- if connectCb then connectCb(true,ProductKey,DeviceName) end
  280. -- if EvtCb["connect"] then EvtCb["connect"](true) end
  281. local result,prompt = procSubscribe(mqttc)
  282. if result then
  283. while true do
  284. procSend(mqttc)
  285. end
  286. end
  287. -- if connectCb then connectCb(false,ProductKey,DeviceName) end
  288. -- if EvtCb["connect"] then EvtCb["connect"](false) end
  289. end
  290. else
  291. --进入飞行模式,20秒之后,退出飞行模式
  292. mobile.flymode(0,true)
  293. sys.wait(20000)
  294. mobile.flymode(0,false)
  295. end
  296. end)
  297. end
  298. --[[
  299. 订阅主题
  300. @api aliyun.subscribe(topic,qos)
  301. @param topic,string类型,主题内容为UTF8编码
  302. @param qos,number,qos为number类型(0/1,默认0);
  303. @return nil
  304. @usage
  305. aliyun.subscribe("/b0FMK1Ga5cp/862991234567890/get", 0)
  306. ]]
  307. function aliyun.subscribe(topic,qos)
  308. insert("SUBSCRIBE",topic,qos)
  309. end
  310. --[[
  311. 发布一条消息
  312. @api aliyun.publish(topic,qos,payload,cbFnc,cbPara)
  313. @string topic,UTF8编码的主题
  314. @number[opt=0] qos,质量等级,0/1,默认0
  315. @string payload,负载内容,UTF8编码
  316. @function[opt=nil] cbFnc,消息发布结果的回调函数,回调函数的调用形式为:cbFnc(result,cbPara)。result为true表示发布成功,false或者nil表示订阅失败;cbPara为本接口中的第5个参数
  317. @param[opt=nil] cbPara,消息发布结果回调函数的回调参数
  318. @return nil
  319. @usage
  320. aliyun.publish("/b0FMK1Ga5cp/862991234567890/update",0,"test")
  321. aliyun.publish("/b0FMK1Ga5cp/862991234567890/update",1,"test",cbFnc,"cbFncPara")
  322. ]]
  323. function aliyun.publish(topic,qos,payload,cbFnc,cbPara)
  324. insert("PUBLISH",topic,qos,payload,cbFnc,cbPara)
  325. sys.publish("ALIYUN_PUB")
  326. end
  327. --[[
  328. 注册事件的处理函数
  329. @api aliyun.on(evt,cbFnc)
  330. @string evt 事件
  331. "connect"表示接入服务器连接结果事件
  332. "receive"表示接收到接入服务器的消息事件
  333. "publish"表示发送消息的结果事件
  334. @function cbFnc 事件的处理函数
  335. 当evt为"connect"时,cbFnc的调用形式为:cbFnc(result),result为true表示连接成功,false或者nil表示连接失败
  336. 当evt为"receive"时,cbFnc的调用形式为:cbFnc(topic,payload),topic为UTF8编码的主题(string类型),payload为原始编码的负载(string类型)
  337. 当evt为"publish"时,cbFnc的调用形式为:cbFnc(result),result为true表示发送成功,false或者nil表示发送失败
  338. @return nil
  339. @usage
  340. aliyun.on("connect",cbFnc)
  341. ]]
  342. function aliyun.on(evt,cbFnc)
  343. EvtCb[evt] = cbFnc
  344. end
  345. --[[
  346. @api aliyun.getDeviceSecret()
  347. @function 预注册一型一密阿里云返回的DeviceSecret
  348. 可以在应用层使用kv区来保存该参数并使用判断来避免重启后无法连接
  349. ]]
  350. function aliyun.getDeviceSecret()
  351. return SetDeviceSecretFnc
  352. end
  353. --[[
  354. @api aliyun.getDeviceToken()
  355. @function 免预注册一型一密阿里云返回的DeviceToken
  356. 可以在应用层使用kv区来保存该参数并使用判断来避免重启后无法连接
  357. ]]
  358. function aliyun.getDeviceToken()
  359. return SetDeviceTokenFnc
  360. end
  361. --[[
  362. @api aliyun.getClientid()
  363. @function 免预注册一型一密阿里云返回的Clientid
  364. 可以在应用层使用kv区来保存该参数并使用判断来避免重启后无法连接
  365. ]]
  366. function aliyun.getClientid()
  367. return SetClientidFnc
  368. end
  369. return aliyun
  370. -- 用户代码已结束---------------------------------------------