本文提供LoRaWAN温湿度传感器通过Link WAN接入,同时采用阿里云物联网平台实现端到端应用的方案。
背景信息
开通物联网络管理平台
完成账号的注册之后,使用账号登录Link WAN 开通服务。
LoRa节点设备接入
搭建与管理网络
参见搭建与管理网络搭建和管理网络、创建节点组并添加节点。
配置数据流转
目前数据流转支持阿里云物联网平台、消息队列MQ两种方式,这里选择将数据流转至阿里云物联网平台,详情请参见数据接入物联网平台-1对1。
物联网平台LoRa节点设备接入
本章介绍如何在物联网平台开发平台上进行设备接入的开发。主要的开发内容包括:
创建产品和设备
产品功能定义
平台脚本开发
本文以一个空气温湿度传感器为例,同时可以配置温湿度的阈值,在温湿度超出阈值时上报事件。
创建产品和设备
登录物联网平台控制台。
在实例概览页面,找到对应的实例,单击实例进入实例详情页面。
在左侧导航栏上选择数据接入物联网平台-1对1。 ,单击创建产品, 填写产品信息后单击确认。详情请参见
参数
描述
产品名称
可填写任意名称
所属品类
自定义品类
节点类型
直连设备
连网方式
LoRaWAN
入网凭证
从表单选择。如无,可单击创建凭证
数据格式
透传/自定义
为产品添加LoRa设备。
在左侧导航栏上单击设备,参见单个创建设备添加设备。
说明使用LoRaWAN设备的DevEUI需小写作为DeviceName。
添加完成后,显示如下,此时设备状态为未激活。
数据流转已自动同步。
产品创建完成后,可在Link WAN里看到自动同步的数据流转设置。
说明在网管平台只能查阅,新增终端请移步至物联网平台维护。
产品功能定义
产品创建完成之后,需要在平台上定义产品有哪些功能。功能定义是为了让平台能够理解设备上下行的数据定义,便于上层应用的读写。
在左侧导航栏上选择 ,单击产品对应操作栏中的查看。
选择 ,单击添加自定义功能。
在添加自定义功能弹框中,功能类型选择属性,添加温湿度属性。
添加温度属性,配置参数如下图所示。
添加湿度属性,配置参数如下图所示。
功能类型选择服务,添加温度湿度阈值,参数配置如下图所示。
其中输入参数设置如下图所示。
参数名称
标志符
数据类型
取值范围
步长
单位
温度过高阈值
MaxTemp
int32(整数型)
-40~55
1
摄氏度/℃
温度过低阈值
MinTemp
int32(整数型)
40~55
1
摄氏度/℃
湿度过高阈值
MaxHumi
int32(整数型)
1~100
1
百分比/%
湿度过低阈值
MinHumi
int32(整数型)
1~100
1
百分比/%
功能类型选择事件,添加湿度过高/过低告警事件。告警输出参数为当前湿度。
其中输出参数设置如下。
单击确认,单击页面左下方的发布更新。
上述属性、服务、事件添加完成后,在自定义功能一栏下方可确认添加的结果。
平台脚本开发
进入产品的数据解析标签页,可以添加解析脚本。由于数据是以自定义格式透传到平台,所以需要添加脚本来解析自定义协议。
将下列代码添加到上图的脚本编辑区。
var ALINK_ID = "12345"; var ALINK_VERSION = "1.1"; var ALINK_PROP_POST_METHOD = 'thing.event.property.post'; var ALINK_EVENT_TEMPERR_METHOD = 'thing.event.TempError.post'; var ALINK_EVENT_HUMIERR_METHOD = 'thing.event.HumiError.post'; var ALINK_PROP_SET_METHOD = 'thing.service.property.set'; var ALINK_SERVICE_THSET_METHOD = 'thing.service.SetTempHumiThreshold'; /* * 示例数据: * 传入参数 -> * 000102 // 共3个字节 * 输出结果 -> * {"method":"thing.event.property.post", "id":"12345", "params":{"Temperature":1,"Humidity":2}, "version":"1.1"} * 传入参数 -> * 0102 // 共2个字节 * 输出结果 -> * {"method":"thing.event.TempError.post","id":"12345","params":{"Temperature":2},"version":"1.1"} * 传入参数 -> * 0202 // 共2个字节 * 输出结果 -> * {"method":"thing.event.HumiError.post","id":"12345","params":{"Humidity":2},"version":"1.1"} */ function rawDataToProtocol(bytes) { var uint8Array = new Uint8Array(bytes.length); for (var i = 0; i < bytes.length; i++) { uint8Array[i] = bytes[i] & 0xff; } var params = {}; var jsonMap = {}; var dataView = new DataView(uint8Array.buffer, 0); var cmd = uint8Array[0]; // command if (cmd === 0x00) { params['Temperature'] = dataView.getInt8(1); params['Humidity'] = dataView.getInt8(2); jsonMap['method'] = ALINK_PROP_POST_METHOD; } else if (cmd == 0x01) { params['Temperature'] = dataView.getInt8(1); jsonMap['method'] = ALINK_EVENT_TEMPERR_METHOD; } else if (cmd == 0x02) { params['Humidity'] = dataView.getInt8(1); jsonMap['method'] = ALINK_EVENT_HUMIERR_METHOD; } else { return null; } jsonMap['version'] = ALINK_VERSION; jsonMap['id'] = ALINK_ID; jsonMap['params'] = params; return jsonMap; } /* * 示例数据: * 传入参数 -> * {"method":"thing.service.SetTempHumiThreshold", "id":"12345", "version":"1.1", "params":{"MaxTemp":50, "MinTemp":8, "MaxHumi":90, "MinHumi":10}} * 输出结果 -> * 0x5d0a000332085a0a */ function protocolToRawData(json) { var id = json['id']; var method = json['method']; var version = json['version']; var payloadArray = []; // 追加下行帧头部 payloadArray = payloadArray.concat(0x5d); payloadArray = payloadArray.concat(0x0a); payloadArray = payloadArray.concat(0x00); if (method == ALINK_SERVICE_THSET_METHOD) { var params = json['params']; var maxtemp = params['MaxTemp']; var mintemp = params['MinTemp']; var maxhumi = params['MaxHumi']; var minhumi = params['MinHumi']; payloadArray = payloadArray.concat(0x03); if (maxtemp !== null) { payloadArray = payloadArray.concat(maxtemp); } if (mintemp !== null) { payloadArray = payloadArray.concat(mintemp); } if (maxhumi !== null) { payloadArray = payloadArray.concat(maxhumi); } if (minhumi !== null) { payloadArray = payloadArray.concat(minhumi); } } return payloadArray; } // 以下是部分辅助函数 function buffer_uint8(value) { var uint8Array = new Uint8Array(1); var dv = new DataView(uint8Array.buffer, 0); dv.setUint8(0, value); return [].slice.call(uint8Array); } function buffer_int16(value) { var uint8Array = new Uint8Array(2); var dv = new DataView(uint8Array.buffer, 0); dv.setInt16(0, value); return [].slice.call(uint8Array); } function buffer_int32(value) { var uint8Array = new Uint8Array(4); var dv = new DataView(uint8Array.buffer, 0); dv.setInt32(0, value); return [].slice.call(uint8Array); } function buffer_float32(value) { var uint8Array = new Uint8Array(4); var dv = new DataView(uint8Array.buffer, 0); dv.setFloat32(0, value); return [].slice.call(uint8Array); }
脚本解析下行数据的函数protocolToRawData中必须设定输出结果的起始三个字节(用于指定下行的端口号以及下行消息类型),否则系统会丢掉下行帧。另外,节点实际接收到的数据将不会包含起始的三个字节。
起始三字节的说明如下表所示。
Size(bytes)
LoRa Downlink
描述
1
DFlag
固定为0x5D
1
FPort
下行端口号
1
DHDR
0表示 “Unconfirmed Data Down”数据帧
1表示 “Confirmed Data Down”数据帧
示例:
0x5D 0x0A 0x00
表示:下行帧端口号为10,数据帧为Unconfirmed Data Down。脚本模拟运行。
设备上报数据调试。
在脚本调试区1里输入下面数据,模拟类型选择设备上报数据后,单击运行按钮。
说明000102中的00表示后面的两个字节分别表示温度和湿度,01表示温度为1摄氏度,02表示湿度为2%。
设备接收数据调试。
在脚本调试区1里输入以下数据,模拟类型选择设备接收数据后,单击运行按钮。
{ "method": "thing.service.SetTempHumiThreshold", "id": "12345", "version": "1.1", "params": { "MaxTemp": 50, "MinTemp": 8, "MaxHumi": 90, "MinHumi": 10 } }
查看脚本调试区2的运行结果如下:
脚本调试无误后,单击提交按钮提交脚本。
设备在线调试
脚本提交后,可以结合节点测试数据的上下行链路是否打通,LoRa节点如何发送以及接收数据请参考各模组厂商的相关手册。
节点数据上行。
上报温湿度属性。
在LoRa节点侧选择输入十六进制的000102后发送数据。
从左侧导航栏的
选择对应节点,单击查看。在设备详情页面选择
。确认节点的湿度与温度信息是否已经上报且设置如下。
上报温湿度告警事件。
温度告警事件上报
在LoRa节点侧选择输入十六进制的0102后发送数据。
在
中确认温度告警事件是否已经上报。
湿度告警事件上报
在LoRa节点侧选择输入十六进制的0202后发送数据。
在
中确认湿度告警事件是否已经上报。
节点数据下行。
在 ,单击对应节点的调试按钮,进入在线调试页面。
选择调试功能为之前添加的温度湿度阈值,具体格式如下所示,单击发送指令。
发送完成后在节点侧确认输出是否是16进制的
0332085a0a
。
对于Class A类型的节点,需要先发送数据才能启动接收。
固件升级
LoRa节点设备可以通过本地端烧录方式升级固件,目前不支持网络在线升级(FUOTA)。