txiot.lua 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. --- 模块功能:腾讯云平台
  2. -- @module txiot
  3. -- @author Dozingfiretruck
  4. -- @license MIT
  5. -- @copyright OpenLuat.com
  6. -- @release 2020.12.31
  7. module(...,package.seeall)
  8. require "ntp"
  9. require "misc"
  10. require "mqtt"
  11. require "utils"
  12. require "patch"
  13. require "socket"
  14. require "http"
  15. require "common"
  16. -- 产品ID和产品动态注册秘钥
  17. local ProductId = "5FCW79CXYD"
  18. local ProductSecret = "XOBuiSs4EUCjmP5NcFWrwdOe"
  19. local tx_mqttClient
  20. --[[
  21. 函数名:getDeviceName
  22. 功能 :获取设备名称
  23. 参数 :无
  24. 返回值:设备名称
  25. ]]
  26. local function getDeviceName()
  27. --默认使用设备的IMEI作为设备名称,用户可以根据项目需求自行修改
  28. return misc.getImei()
  29. end
  30. function txiot_publish()
  31. --sys.publish("APP_SOCKET_SEND_DATA")
  32. --mqtt发布主题
  33. tx_mqttClient:publish("$thing/up/property/"..ProductId.."/"..getDeviceName(), "publish from luat mqtt client", 0)
  34. end
  35. -- 无网络重启时间,飞行模式启动时间
  36. local rstTim, flyTim = 600000, 300000
  37. local enrol_end = false
  38. local mqtt_ready = false
  39. --- MQTT连接是否处于激活状态
  40. -- @return 激活状态返回true,非激活状态返回false
  41. -- @usage mqttTask.isReady()
  42. function isReady()
  43. return mqtt_ready
  44. end
  45. --- MQTT客户端数据接收处理
  46. -- @param mqttClient,MQTT客户端对象
  47. -- @return 处理成功返回true,处理出错返回false
  48. -- @usage mqttInMsg.proc(mqttClient)
  49. local function proc(mqttClient)
  50. local result,data
  51. while true do
  52. result,data = mqttClient:receive(120000,"APP_SOCKET_SEND_DATA")
  53. --接收到数据
  54. if result then
  55. log.info("mqttInMsg.proc",data.topic,string.toHex(data.payload))
  56. --TODO:根据需求自行处理data.payload
  57. else
  58. break
  59. end
  60. end
  61. return result or data=="timeout" or data=="APP_SOCKET_SEND_DATA"
  62. end
  63. local function cbFnc(result,prompt,head,body)
  64. log.info("testHttp.cbFnc",result,prompt,head,body)
  65. local dat, result, errinfo = json.decode(body)
  66. if result then
  67. if dat.code==0 then
  68. io.writeFile("/txiot.dat", body)
  69. log.info("腾讯云注册设备成功:", body)
  70. else
  71. log.info("腾讯云设备注册失败:", body)
  72. end
  73. enrol_end = true
  74. end
  75. end
  76. local function device_enrol()
  77. local deviceName = getDeviceName()
  78. local nonce = math.random(1,100)
  79. local timestamp = os.time()
  80. local data = "deviceName="..deviceName.."&nonce="..nonce.."&productId="..ProductId.."&timestamp="..timestamp
  81. local hmac_sha1_data = crypto.hmac_sha1(data,#data,ProductSecret,#ProductSecret):lower()
  82. local signature = crypto.base64_encode(hmac_sha1_data,#hmac_sha1_data)
  83. local tx_body = {
  84. deviceName=deviceName,
  85. nonce=nonce,
  86. productId=ProductId,
  87. timestamp=timestamp,
  88. signature=signature,
  89. }
  90. local tx_body_json = json.encode(tx_body)
  91. http.request("POST","https://ap-guangzhou.gateway.tencentdevices.com/register/dev",nil,{["Content-Type"]="application/json; charset=UTF-8"},tx_body_json,30000,cbFnc)
  92. end
  93. local function tencent_iot()
  94. if not io.exists("/txiot.dat") then device_enrol() while not enrol_end do sys.wait(100) end end
  95. if not io.exists("/txiot.dat") then device_enrol() log.warn("设备注册失败或设备已注册") return end
  96. local dat = json.decode(io.readFile("/txiot.dat"))
  97. local clientid = ProductId .. getDeviceName() --生成 MQTT 的 clientid 部分, 格式为 ${productid}${devicename}
  98. local connid = math.random(10000,99999)
  99. local expiry = tostring(os.time() + 3600)
  100. local username = string.format("%s;12010126;%s;%s", clientid, connid, expiry) --生成 MQTT 的 username 部分, 格式为 ${clientid};${sdkappid};${connid};${expiry}
  101. local payload = json.decode(crypto.aes_decrypt("CBC","ZERO",crypto.base64_decode(dat.payload, #dat.payload),string.sub(ProductSecret,1,16),"0000000000000000"))
  102. local password
  103. if payload.encryptionType==2 then
  104. local raw_key = crypto.base64_decode(payload.psk, #payload.psk) --生成 MQTT 的 设备密钥 部分
  105. password = crypto.hmac_sha256(username, raw_key):lower() .. ";hmacsha256" --根据物联网通信平台规则生成 password 字段
  106. elseif payload.encryptionType==1 then
  107. io.writeFile("/client.crt", payload.clientCert)
  108. io.writeFile("/client.key", payload.clientKey)
  109. end
  110. while true do
  111. if not socket.isReady() and not sys.waitUntil("IP_READY_IND", rstTim) then sys.restart("网络初始化失败!") end
  112. --创建一个MQTT客户端
  113. tx_mqttClient = mqtt.client(clientid,300,username,password)
  114. --阻塞执行MQTT CONNECT动作,直至成功
  115. if payload.encryptionType==2 then
  116. while not tx_mqttClient:connect(ProductId..".iotcloud.tencentdevices.com",1883,"tcp",nil,2000) do sys.wait(2000) end
  117. elseif payload.encryptionType==1 then
  118. while not tx_mqttClient:connect(ProductId..".iotcloud.tencentdevices.com",8883,"tcp_ssl",{clientCert="/client.crt",clientKey="/client.key"},2000) do sys.wait(2000) end
  119. end
  120. log.info("mqtt连接成功")
  121. tx_mqttClient:publish("$thing/up/property/"..ProductId.."/"..getDeviceName(), "publish from luat mqtt client", 0)
  122. --mqtt订阅主题
  123. local txiot_subscribetopic = {
  124. ["$thing/down/property/"..ProductId.."/"..getDeviceName()]=0
  125. }
  126. --订阅主题
  127. if tx_mqttClient:subscribe(txiot_subscribetopic, nil) then
  128. log.info("mqtt订阅成功")
  129. --循环处理接收和发送的数据
  130. while true do
  131. mqtt_ready = true
  132. if not proc(tx_mqttClient) then log.error("mqttTask.mqttInMsg.proc error") break end
  133. end
  134. else
  135. log.info("mqtt订阅失败")
  136. end
  137. mqtt_ready = false
  138. --断开MQTT连接
  139. tx_mqttClient:disconnect()
  140. end
  141. end
  142. local function iot()
  143. ntp.timeSync()
  144. if not socket.isReady() and not sys.waitUntil("IP_READY_IND", rstTim) then sys.restart("网络初始化失败!") end
  145. while not ntp.isEnd() do sys.wait(1000) end
  146. tencent_iot()
  147. end
  148. net.switchFly(false)
  149. -- NTP同步失败强制重启
  150. local tid = sys.timerStart(function()
  151. net.switchFly(true)
  152. sys.timerStart(net.switchFly, 5000, false)
  153. end, flyTim)
  154. sys.subscribe("IP_READY_IND", function()
  155. sys.timerStop(tid)
  156. log.info("---------------------- 网络注册已成功 ----------------------")
  157. end)
  158. sys.taskInit(iot)