全部产品
云市场

LoRaWAN温湿度传感器方案

更新时间:2019-05-27 14:49:43

LoRaWAN温湿度传感器接入实践

前言

本文提供LoRaWAN温湿度传感器通过物联网络管理平台(Alibaba Cloud Link WAN)接入,同时采用阿里云物联网平台实现端到端应用。

1 LoRaWAN节点设备接入

1.1 开通物联网络管理平台

完成账号的注册之后,使用账号登陆https://linkwan.console.aliyun.com/ 开通服务。开通完成后登陆,可以正式进入管理控制台。LW控制台

1.2 搭建与管理网络

请按照以下说明搭建和管理网络、创建节点组并添加节点。
搭建与管理网络

1.3 配置数据流转

目前数据流转支持”阿里云物联网平台”、”消息队列”两种方式,这里选择 ”阿里云物联网平台”,可参考以下方式。
数据出口配置指南

2 物联网平台设备接入

本章介绍如何在物联网平台开发平台上进行设备接入的开发。主要的开发内容包括:

  • 项目和产品创建
  • 产品功能定义
  • 平台脚本开发

2.1 物联网平台LoRaWAN节点设备接入

这里示例是一个空气温湿度传感器,同时可以配置温湿度的阈值,在温湿度超出阈值时上报事件。

2.1.1 产品创建

登录https://iot.console.aliyun.com/data_board,选择“设备管理”->“产品”,点击“创建产品”。

填写产品信息后点击“完成”。
注意:下图“所属分类”选择“自定义品类”仅是本示例说明使用,真实产品请务必选择其他某一具体分类,如分类未包含相关产品,请和阿里支持人员联系。

4-1注意:这里“连网方式”请选择“LoRaWAN”,物联网平台与网络管理平台(Link WAN)会自动同步设备清单,具体详见:
数据出口配置指南的自动同步说明

4-2

产品建立后,选择新增设备:
5

6

注意请使用LoRaWAN设备的DevEUI小写作为deviceName
7


添加完成后,显示如下,此时设备状态为“未激活”:
8

2.1.2 数据流转配置

产品创建完成后,可在网络管理平台里看到自动同步的数据流转设置。
9-1
在网管平台只能查阅,新增终端请移步至物联网平台维护。

2.1.3 产品功能定义

产品创建完成之后,需要在平台上定义产品有哪些功能。功能定义是为了让平台能够理解设备上下行的数据定义,便于上层应用的读写。
进入“产品详情”->“功能定义”->“自定义功能”,点击“添加功能”按钮,追加如下自定义功能。

2.1.3.1 追加属性:温湿度

1.功能类型选择“属性”,追加温度& 湿度属性,具体如下:
10

2.1.3.2 追加事件:温湿度异常告警

功能类型选择“事件”,追加温度湿度异常告警,具体如下:
1. 事件 - 温度异常告警(TempError),温度过高/过低告警事件。告警输出参数为当前温度。
11

  1. 事件– 湿度异常告警(HumiError),湿度过高/过低告警事件。告警输出参数为当前湿度。
    12

2.1.3.3 追加服务:温度湿度阈值

功能类型选择“服务”,追加温度湿度阈值,具体如下:
13


其中输入参数设置如下:
- 温度过高告警阈值(MaxTemp)
- 温度过低告警阈值(MinTemp)
- 湿度过高告警阈值(MaxHumi)
- 湿度过低告警阈值(MinHumi)

具体设置如下:14
15

2.1.3.4 自定义功能确认

上述属性和服务追加完成后,在“自定义功能”一栏下方可确认追加结果。
16

2.1.4 平台脚本开发

进入“产品详情”的“数据解析”标签页,可以添加解析脚本。由于数据是以自定义格式透传到平台,所以需要添加脚本来解析自定义协议。

17

2.1.4.1 示例脚本

注意事项:脚本解析下行数据的函数 protocolToRawData 中必须设定输出结果的起始三个字节(用于指定下行的端口号以及下行消息类型),否则系统会丢掉下行帧。
另外,节点实际接收到的数据将不会包含起始的三个字节。

起始三字节的说明如下:
18

  • DFlag:固定为 0x5D
  • FPort:下行端口号
  • DHDR:0 表示 “Unconfirmed Data Down”数据帧,1 表示 “Confirmed Data Down”数据帧

示例: 0x5D 0x0A 0x00 表示:下行帧端口号为10、为“Unconfirmed Data Down”。


将下列代码追加到上图的“脚本编辑区”。

  1. var ALINK_ID = "12345";
  2. var ALINK_VERSION = "1.1";
  3. var ALINK_PROP_POST_METHOD = 'thing.event.property.post';
  4. var ALINK_EVENT_TEMPERR_METHOD = 'thing.event.TempError.post';
  5. var ALINK_EVENT_HUMIERR_METHOD = 'thing.event.HumiError.post';
  6. var ALINK_PROP_SET_METHOD = 'thing.service.property.set';
  7. var ALINK_SERVICE_THSET_METHOD = 'thing.service.SetTempHumiThreshold';
  8. /*
  9. * 示例数据:
  10. * 传入参数 ->
  11. * 000102 // 共3个字节
  12. * 输出结果 ->
  13. * {"method":"thing.event.property.post", "id":"12345", "params":{"Temperature":1,"Humidity":2}, "version":"1.1"}
  14. * 传入参数 ->
  15. * 0102 // 共2个字节
  16. * 输出结果 ->
  17. * {"method":"thing.event.TempError.post","id":"12345","params":{"Temperature":2},"version":"1.1"}
  18. * 传入参数 ->
  19. * 0202 // 共2个字节
  20. * 输出结果 ->
  21. * {"method":"thing.event.HumiError.post","id":"12345","params":{"Humidity":2},"version":"1.1"}
  22. */
  23. function rawDataToProtocol(bytes)
  24. {
  25. var uint8Array = new Uint8Array(bytes.length);
  26. for (var i = 0; i < bytes.length; i++)
  27. {
  28. uint8Array[i] = bytes[i] & 0xff;
  29. }
  30. var params = {};
  31. var jsonMap = {};
  32. var dataView = new DataView(uint8Array.buffer, 0);
  33. var cmd = uint8Array[0]; // command
  34. if (cmd === 0x00)
  35. {
  36. params['Temperature'] = dataView.getInt8(1);
  37. params['Humidity'] = dataView.getInt8(2);
  38. jsonMap['method'] = ALINK_PROP_POST_METHOD;
  39. }
  40. else if (cmd == 0x01)
  41. {
  42. params['Temperature'] = dataView.getInt8(1);
  43. jsonMap['method'] = ALINK_EVENT_TEMPERR_METHOD;
  44. }
  45. else if (cmd == 0x02)
  46. {
  47. params['Humidity'] = dataView.getInt8(1);
  48. jsonMap['method'] = ALINK_EVENT_HUMIERR_METHOD;
  49. }
  50. else
  51. {
  52. return null;
  53. }
  54. jsonMap['version'] = ALINK_VERSION;
  55. jsonMap['id'] = ALINK_ID;
  56. jsonMap['params'] = params;
  57. return jsonMap;
  58. }
  59. /*
  60. * 示例数据:
  61. * 传入参数 ->
  62. * {"method":"thing.service.SetTempHumiThreshold", "id":"12345", "version":"1.1", "params":{"MaxTemp":50, "MinTemp":8, "MaxHumi":90, "MinHumi":10}}
  63. * 输出结果 ->
  64. * 0x5d0a000332085a0a
  65. */
  66. function protocolToRawData(json)
  67. {
  68. var id = json['id'];
  69. var method = json['method'];
  70. var version = json['version'];
  71. var payloadArray = [];
  72. // 追加下行帧头部
  73. payloadArray = payloadArray.concat(0x5d);
  74. payloadArray = payloadArray.concat(0x0a);
  75. payloadArray = payloadArray.concat(0x00);
  76. if (method == ALINK_SERVICE_THSET_METHOD)
  77. {
  78. var params = json['params'];
  79. var maxtemp = params['MaxTemp'];
  80. var mintemp = params['MinTemp'];
  81. var maxhumi = params['MaxHumi'];
  82. var minhumi = params['MinHumi'];
  83. payloadArray = payloadArray.concat(0x03);
  84. if (maxtemp !== null)
  85. {
  86. payloadArray = payloadArray.concat(maxtemp);
  87. }
  88. if (mintemp !== null)
  89. {
  90. payloadArray = payloadArray.concat(mintemp);
  91. }
  92. if (maxhumi !== null)
  93. {
  94. payloadArray = payloadArray.concat(maxhumi);
  95. }
  96. if (minhumi !== null)
  97. {
  98. payloadArray = payloadArray.concat(minhumi);
  99. }
  100. }
  101. return payloadArray;
  102. }
  103. // 以下是部分辅助函数
  104. function buffer_uint8(value)
  105. {
  106. var uint8Array = new Uint8Array(1);
  107. var dv = new DataView(uint8Array.buffer, 0);
  108. dv.setUint8(0, value);
  109. return [].slice.call(uint8Array);
  110. }
  111. function buffer_int16(value)
  112. {
  113. var uint8Array = new Uint8Array(2);
  114. var dv = new DataView(uint8Array.buffer, 0);
  115. dv.setInt16(0, value);
  116. return [].slice.call(uint8Array);
  117. }
  118. function buffer_int32(value)
  119. {
  120. var uint8Array = new Uint8Array(4);
  121. var dv = new DataView(uint8Array.buffer, 0);
  122. dv.setInt32(0, value);
  123. return [].slice.call(uint8Array);
  124. }
  125. function buffer_float32(value)
  126. {
  127. var uint8Array = new Uint8Array(4);
  128. var dv = new DataView(uint8Array.buffer, 0);
  129. dv.setFloat32(0, value);
  130. return [].slice.call(uint8Array);
  131. }

2.1.4.2 脚本模拟运行

设备上报数据调试
在“脚本调试区1”里输入下面数据,模拟类型选择“设备上报数据”后,点击“运行”按钮。
19

说明:000102 中的 00 表示后面的两个字节分别表示温度和湿度,01 表示温度为1摄氏度,02表示湿度为2%
查看“脚本调试区2”的运行结果如下:

设备接收数据调试
在“脚本调试区1”里输入下面数据,模拟类型选择“设备接收数据”后,点击“运行”按钮。

  1. {
  2. "method": "thing.service.SetTempHumiThreshold",
  3. "id": "12345",
  4. "version": "1.1",
  5. "params": {
  6. "MaxTemp": 50,
  7. "MinTemp": 8,
  8. "MaxHumi": 90,
  9. "MinHumi": 10
  10. }
  11. }


20

查看“脚本调试区2”的运行结果如下:
21

提交脚本
脚本调试无误后,点击“提交”按钮提交脚本。

2.2 设备在线调试

脚本提交后,可以结合节点测试数据的上下行链路是否打通,LoRaWAN节点如何发送以及接收数据请参考各模组厂商的相关手册。

2.2.1 节点数据上行

2.2.1.1 上报温湿度属性

在LoRa 节点侧选择输入十六进制的 000102 后发送数据。
在产品详情 -> 设备开发 -> 设备列表选择对应节点,点击“查看”,如下所示。
22


在设备详情 -> 运行状态中确认节点的“湿度”“温度”信息是否已经上报且设置如下。
23

23-1

2.2.1.2 上报温湿度告警事件

1.温度告警事件上报
在 LoRa 节点侧选择输入十六进制的0102 后发送数据。
在“设备详情” -> “事件管理”中确认温度告警事件是否已经上报。
24


2.湿度告警事件上报
在 LoRaWAN 节点侧选择输入十六进制的0202 后发送数据。
在“设备详情” -> “事件管理”中确认湿度告警事件是否已经上报。
25

2.2.2 节点数据下行

在“产品详情”-> “设备开发”点击对应节点的“调试”按钮,进入“在线调试”页面。
选择调试功能为之前添加的“温度湿度阈值”,具体格式如下所示,点击“发送指令”。
26
发送完成后在节点侧确认输出是否是16进制的:0332085a0a
注意:对于Class A类型的节点,需要先发送数据才能启动接收。

3 固件升级

注意:LoRaWAN节点目前不支持固件升级功能