main.lua 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. -- LuaTools需要PROJECT和VERSION这两个信息
  2. PROJECT = "tlink"
  3. VERSION = "1.0.0"
  4. --[[
  5. 本示例用于演示定时把温度数据传输到tlink平台, 由tlink平台进行处理, 并在网页端和手机端进行展示
  6. 本示例需要使用中国移动或者中国联通NB专有卡, 普通2G/3G/4G卡无法保证正常
  7. -- 设备端接线
  8. 1. Air302开发板一块
  9. 2. DS18B20传感器一个
  10. - 需要带上拉电阻4.7k
  11. - 数据端接入模块/开发板的GPIO17
  12. - VCC接入VDDIO或其他3.3V供电脚
  13. - GND接入模块的GND
  14. --- tlink网页端的配置
  15. 1. 首先, 请到 http://tlink.io 注册账号, 普通账号即可, 无需企业版
  16. 2. 然后, 登录后台, 在左侧菜单, 选择 "设备管理", 然后点击 "添加设备"
  17. 3. 新增设备时
  18. - 3.1 设备名称自选(例如模块的IMEI值)
  19. - 3.2 链接协议 选"UDP"
  20. - 3.3 掉线延时 选"300秒"
  21. - 3.4 传感器, 点"追加",会新增一行
  22. - 3.4.1 传感器名称, 填"温度传感器"
  23. - 3.4.2 类型, 选 "数值型"
  24. - 3.4.3 小数位, 选 "1(小数位)"
  25. - 3.4.4 单位, 填"摄氏度"
  26. - 3.4.5 地图, 随便选一个地址就行
  27. - 3.5 滚动屏幕到底部, 点击"创建设备", 完成基本创建
  28. 4. 返回设备列表后, 点击新设备的"设置连接"
  29. - 4.1 重点, 设备信息展示栏的 "序列号", 点击最后一个图标(修改)
  30. - 4.1.1 填入值 0 和 模块的IMEI值, 共16位
  31. - 4.1.2 例如模块的IMEI为 867814046436255, 则填入 0867814046436255
  32. - 4.2 点击 "数据头标签" "[H:数据]", 填入 一个字符 "#" 即井号
  33. - 4.3 点击 "数据标签" "D?"
  34. - 4.4 点击 "结束符标签" "[回车换行]"
  35. - 4.5 点击 "保存协议", 则 "当前协议" 会显示为 "[H:] [D?] [TE:0D0A]"
  36. 5. 返回设备列表, 至此, 网页端的配置就完成了
  37. 参考文档:
  38. - 创建设备 https://www.tlink.io/help.htm?menu=2
  39. - UDP 协议 https://www.tlink.io/help.htm?menu=2&page=46
  40. - 安卓APP https://www.tlink.io/help.htm?menu=2&page=116
  41. 注意事项:
  42. - 本demo是适合单传上传数据的场景,不适合有数据下发的场景
  43. - 由UDP的特性决定, 不能100%保证数据能上报成功
  44. ]]
  45. local sys = require "sys"
  46. -----------------------------------------------------------------------------------
  47. --PM异常唤醒检测 休眠时间最低120S
  48. --- pm_wakeup_time_check() 读取上次设置hib时间,并且与本次时间作比较,异常唤醒将直接睡眠
  49. -- @return 无
  50. function pm_wakeup_time_check ()
  51. log.info("pm", pm.lastReson())
  52. if pm.lastReson() == 1 then
  53. local tdata = lpmem.read(512, 6) -- 0x5A 0xA5, 然后一个32bit的taskInit
  54. local _, mark, tsleep = pack.unpack(tdata, ">HI")
  55. if mark == 0x5AA5 then
  56. local tnow = os.time()
  57. log.info("pm", "sleep time", tsleep, tnow)
  58. --下面的120S根据休眠时间设置,最大可以设置休眠时间-12S。
  59. if tnow - tsleep < (120 - 12) then
  60. pm.request(pm.HIB) -- 建议休眠
  61. return -- 是提前唤醒, 继续睡吧
  62. end
  63. end
  64. end
  65. return true
  66. end
  67. --- PM进入休眠
  68. -- @param sec 进入hib深睡眠时间,单位:秒
  69. -- @返回值: 无
  70. -- @ pm_enter_hib_mode(sec)
  71. function pm_enter_hib_mode(sec)
  72. --设置休眠唤醒时间,并开启休眠
  73. lpmem.write(512, pack.pack(">HI", 0x5AA5, os.time())) -- 把当前时间写入lpmem
  74. pm.dtimerStart(0, sec*1000)
  75. pm.request(pm.HIB) -- 建议休眠
  76. --log.info("pm check",pm.check())
  77. --sys.wait(300*1000)
  78. end
  79. -----------------------------------------------------------------------------------------
  80. -- 读取ds18b20的数据
  81. function get_data()
  82. local data = nil
  83. for i=1,10 do -- 读取时存在失败的可能性,所以需要尝试多次
  84. local temp, result = sensor.ds18b20(17)
  85. if result and temp ~= 250 then
  86. -- 数据格式对应 [H:] [D?] [TE:0D0A]
  87. data = string.format( "0%s#%d\r\n", nbiot.imei(), math.floor(temp))
  88. log.info("data", data)
  89. log.info("data.hex", data:toHex())
  90. break
  91. end
  92. sys.wait(2000)
  93. end
  94. return data
  95. end
  96. -- 主任务
  97. function main_task()
  98. -- 等待联网
  99. while not socket.isReady() do sys.wait(1000) end
  100. -- 建立netclient对象
  101. local netc = socket.udp()
  102. netc:host("47.106.61.135") -- 这里对应的 udp.tlink.io 的IP地址
  103. netc:port(9896) -- UDP协议的端口号
  104. -- 配置好回调
  105. netc:on("connect", function(id, re)
  106. log.info("netc", "connect", id, re)
  107. sys.taskInit(function()
  108. local data = get_data()
  109. if data then
  110. log.info("netc", "send reg package", data)
  111. netc:send(data, 2)
  112. end
  113. sys.publish("NETC_OK")
  114. end)
  115. end)
  116. netc:on("recv", function(id, data)
  117. -- 注意, 唤醒操作, 调用netc:rebind后, 如果基站有缓存的数据, 这里就会被触发
  118. log.info("netc", "recv", id, data)
  119. --netc:send(data)
  120. end)
  121. -- 检查开机原因
  122. local flag = false
  123. log.info("pm", "lastReson", pm.lastReson())
  124. -- 非普通上电/复位上电,那就是唤醒上电咯
  125. if pm.lastReson() ~= 0 then
  126. -- 读取低功耗内存的 0x5A 0xA5 ? ? ? ?
  127. local data = lpmem.read(0, 6)
  128. -- 打印内容,方便调试
  129. log.info("lpmem", data:toHex())
  130. -- 使用pack库解析之
  131. local _, t1,t2,sockid = pack.unpack(data, ">bbI")
  132. -- 头两个字符是我们自定义的0x5A 0xA5,不是的话,就肯定是脏数据了
  133. if t1 == 0x5A and t2 == 0xA5 then
  134. netc:rebind(sockid) -- 重建tcp上下文
  135. local data = get_data()
  136. if data then
  137. netc:send(data, 2) -- 发送心跳
  138. end
  139. flag = true
  140. else
  141. -- 脏数据就不管了
  142. log.info("lpmem", "bad custom lpmem data, skip")
  143. end
  144. end
  145. -- 如果不唤醒流程, 或者数据是脏的,就新建连接
  146. if not flag then
  147. -- 启动tcp连接线程
  148. if netc:start() == 0 then
  149. sys.waitUntil("NETC_OK", 15000)
  150. if netc:closed() == 0 then
  151. -- 启动成功, 那就把sockid放入低功耗内存,在唤醒时重建上下文.
  152. lpmem.write(0, pack.pack(">bbI", 0x5A, 0xA5, netc:sockid()))
  153. else
  154. -- 启动,那就重启吧 or 重试2次?
  155. log.warn("Start netc FAIL!!!")
  156. sys.wait(15000)
  157. rtos.reboot()
  158. end
  159. else
  160. -- socket线程都启动失败?什么情况啊
  161. log.warn("Start netc FAIL!!!")
  162. sys.wait(5000)
  163. rtos.reboot()
  164. end
  165. end
  166. -- 要求进入休眠状态,省电.
  167. -- 如修改休眠时长, 务必做到 "同比例"修改pm_wakeup_time_check里面的检测时长
  168. pm_enter_hib_mode(120)
  169. end
  170. -- 先判断休眠是否够了, 然后再执行主任务
  171. sys.taskInit(function()
  172. if pm_wakeup_time_check() then
  173. main_task()
  174. else
  175. log.info("pm", "too early, back to hib")
  176. end
  177. end)
  178. -- 用户代码已结束---------------------------------------------
  179. -- 结尾总是这一句
  180. sys.run()
  181. -- sys.run()之后后面不要加任何语句!!!!!