LoRaWAN設備與物聯網平臺的通信數據格式為透傳/自定義,因此需要使用消息解析腳本,解析上下行數據。本文以LoRaWAN溫濕度傳感器為例,介紹LoRaWAN設備消息解析腳本的編輯和調試方法。

步驟一:編輯腳本

  1. 物聯網平臺控制臺,創建連網方式為LoRaWAN的產品。
  2. 為該產品定義物模型。功能定義具體方法,請參見單個添加物模型

    本示例中,定義了以下屬性、事件和服務:

    表 1. 屬性
    標識符數據類型取值范圍讀寫類型
    Temperatureint32-40~55讀寫
    Humidityint321~100讀寫
    表 2. 服務
    標識符調用方式輸入參數
    SetTempHumiThreshold異步四個輸入參數,數據類型均為int32:
    • 溫度過高告警閾值(標識符:MaxTemp)
    • 溫度過低告警閾值(標識符:MinTemp)
    • 濕度過高告警閾值(標識符:MaxHumi)
    • 濕度過低告警閾值(標識符:MinHumi)
    表 3. 事件
    標識符事件類型輸入參數
    TempError告警溫度Temperature
    HumiError告警濕度Humidity
  3. 編寫腳本。

    在物聯網平臺控制臺,產品詳情頁面的消息解析頁簽下,選擇腳本語言,編寫腳本。

    支持的腳本語言設備自定義數據格式轉Alink JSON格式數據的函數(上行通信)Alink JSON格式數據轉為設備自定義數據格式的函數(下行通信)
    JavaScript(ECMAScript 5)rawDataToProtocolprotocolToRawData
    Python 2.7raw_data_to_protocolprotocol_to_raw_data
    PHP 7.2rawDataToProtocolprotocolToRawData

    本文示例的語言為JavaScript(ECMAScript 5)。

    腳本中,解析下行數據的函數protocolToRawData中,需設定輸出結果的起始三個字節,用于指定下行端口號和下行消息類型。否則,系統會丟掉下行幀。 設備實際接收的數據中不會包含這三個字節。

    表 4. 下行數據解析輸出結果起始三字節
    LoRa Downlink字節數說明
    DFlag1固定為0x5D
    FPort1下行端口號。
    DHDR1可選:
    • 0:表示 “Unconfirmed Data Down”數據幀。
    • 1:表示 “Confirmed Data Down”數據幀。

    例如,0x5D 0x0A 0x00表示下行幀端口號為10,數據幀為“Unconfirmed Data Down”。

    完整的示例腳本Demo,請參見本文附錄:示例腳本。

步驟二: 在線測試腳本

在數據解析編輯器中,使用模擬數據測試腳本。

  • 設備上報數據模擬解析 。

    模擬輸入框中,選擇模擬類型為設備上報數據,輸入模擬數據000102,單擊運行

    運行結果為:

    {
      "method": "thing.event.property.post",
      "id": "12345",
      "params": {
        "Temperature": 1,
        "Humidity": 2
      },
      "version": "1.1"
    }
  • 設備接收數據模擬解析 。

    選擇模擬類型為設備接收數據,輸入以下JSON格式的下行模擬數據,單擊運行

    {
        "method": "thing.service.SetTempHumiThreshold",
        "id": "12345",
        "version": "1.1",
        "params": {
            "MaxTemp": 50,
            "MinTemp": 8,
            "MaxHumi": 90,
            "MinHumi": 10
        }
    }

    運行結果為:

    0x5d0a000332085a0a

步驟三:提交腳本

確認腳本可以正確解析數據后,單擊提交,將該腳本提交到物聯網平臺系統,以供數據上下行時,物聯網平臺調用該腳本解析數據。

說明 僅提交后的腳本才能被物聯網平臺調用;草稿狀態的腳本不能被調用。

步驟四:使用真實設備調試

腳本提交后,正式使用之前,請使用真實設備進行測試。LoRaWAN節點設備如何發送和接收數據,請參見模組廠商的相關手冊。

  • 測試LoRaWAN設備上報溫濕度屬性。
    1. 使用設備端發送數據,例如000102
    2. 在該設備的設備詳情物模型數據 > 運行狀態頁簽下,查看設備上報的屬性數據。
  • 測試LoRaWAN設備上報事件。
    1. 使用設備端發送事件數據,如,溫度告警數據0102,或濕度告警數據0202
    2. 在該設備的設備詳情物模型數據 > 事件管理頁簽下,查看設備上報的事件數據。
  • 測試調用LoRaWAN設備服務。
    1. 在物聯網平臺控制臺,選擇監控運維 > 在線調試
    2. 選擇要調試的產品和設備,并選擇調試真實設備,功能選擇為溫度濕度閾值(SetTempHumiThreshold),輸入以下數據后,單擊發送指令
      {
          "MaxTemp": 50,
          "MinTemp": 8,
          "MaxHumi": 90,
          "MinHumi": 10
      }
    3. 檢查設備端是否接收到服務調用命令。
    4. 在該設備的設備詳情物模型數據 > 服務調用頁簽下,查看設備服務調用數據。

附錄:示例腳本

根據以上產品和其功能定義,編輯的示例腳本如下:

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;
}

/**
 * 將設備自定義Topic數據轉換為JSON格式數據,設備上報數據到物聯網平臺時調用。
 * 入參:topic,字符串,設備上報消息的Topic。    
 * 入參:rawData,byte[]數組,不能為空。
 * 出參:jsonObj,對象,不能為空。
 */
function transformPayload(topic, rawData) {
    var jsonObj = {}
    return jsonObj;
}

// 以下是部分輔助函數。
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);
}

相關文檔