本文提供LoRa溫溼度傳感器通過Link WAN接入,同時採用阿里雲物聯網平臺實現端到端應用。
背景信息
- 開通物聯網絡管理平臺完成賬號的註冊之後,使用賬號登錄Link WAN 開通服務。
- LoRa節點設備接入
- 搭建與管理網絡參見搭建與管理網絡搭建和管理網絡、創建節點組並添加節點。
- 配置數據流轉目前數據流轉支持阿里雲物聯網平臺、消息隊列MQ兩種方式,這裡選擇將數據流轉至阿里雲物聯網平臺,詳情請參見數據接入物聯網平臺-1對1。
-
物聯網平臺LoRa節點設備接入本章介紹如何在物聯網平臺開發平臺上進行設備接入的開發。主要的開發內容包括:
- 項目和產品創建
- 產品功能定義
- 平臺腳本開發
本文以一個空氣溫溼度傳感器為例,同時可以配置溫溼度的閾值,在溫溼度超出閾值時上報事件。
創建產品和設備
- 登錄物聯網平臺控制檯。
- 在左側導航欄上選擇設備管理 > 產品,單擊創建產品, 填寫產品信息後單擊完成。詳情請參見數據接入物聯網平臺-1對1。
| 參數 | 描訴 |
產品名稱 | 可填寫任意名稱 |
所屬品類 | 自定義品類 |
節點類型 | 直連設備 |
連網方式 | LoRaWAN |
入網憑證 | 從表單選擇,如無可單擊創建憑證 |
數據格式 | 透傳/自定義 |
-
為產品添加LoRa設備。在左側導航欄上單擊設備,參見單個創建設備添加設備。
說明使用LoRaWAN設備的DevEUI需小寫作為deviceName。
產品功能定義
產品創建完成之後,需要在平臺上定義產品有哪些功能。功能定義是為了讓平臺能夠理解設備上下行的數據定義,便於上層應用的讀寫。
- 在左側導航欄上選擇設備管理 > 產品,單擊產品對應操作欄中的查看。
- 選擇功能定義 > 編輯草稿,單擊自定義功能 > 添加自定義功能
-
在添加自定義功能彈框中,功能類型選擇屬性,添加溫溼度屬性。
-
功能類型選擇服務,添加溫度溼度閾值,參數配置如下圖所示。
其中輸入參數設置如下圖所示。
| 參數名稱 | 標誌符 | 數據類型 | 取值範圍 | 步長 | 單位 |
溫度過高閾值 | 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,數據幀為nconfirmed Data Down。 -
腳本模擬運行。
說明 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
}
}
設備在線調試
腳本提交後,可以結合節點測試數據的上下行鏈路是否打通,LoRa節點如何發送以及接收數據請參考各模組廠商的相關手冊。
-
節點數據上行。
-
節點數據下行。
說明 對於Class A類型的節點,需要先發送數據才能啟動接收。
固件升級
LoRa節點設備可以通過本地端燒錄方式升級固件,目前不支持網絡在線升級(FUOTA)。