LoRaWAN設(shè)備與物聯(lián)網(wǎng)平臺(tái)的通信數(shù)據(jù)格式為透?jìng)?自定義,因此需要使用消息解析腳本,解析上下行數(shù)據(jù)。本文以LoRaWAN溫濕度傳感器為例,介紹LoRaWAN設(shè)備消息解析腳本的編輯和調(diào)試方法。

步驟一:編輯腳本

  1. 物聯(lián)網(wǎng)平臺(tái)控制臺(tái),創(chuàng)建連網(wǎng)方式為L(zhǎng)oRaWAN的產(chǎn)品。
  2. 為該產(chǎn)品定義物模型。功能定義具體方法,請(qǐng)參見(jiàn)單個(gè)添加物模型

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

    表 1. 屬性
    標(biāo)識(shí)符數(shù)據(jù)類(lèi)型取值范圍讀寫(xiě)類(lèi)型
    Temperatureint32-40~55讀寫(xiě)
    Humidityint321~100讀寫(xiě)
    表 2. 服務(wù)
    標(biāo)識(shí)符調(diào)用方式輸入?yún)?shù)
    SetTempHumiThreshold異步四個(gè)輸入?yún)?shù),數(shù)據(jù)類(lèi)型均為int32:
    • 溫度過(guò)高告警閾值(標(biāo)識(shí)符:MaxTemp)
    • 溫度過(guò)低告警閾值(標(biāo)識(shí)符:MinTemp)
    • 濕度過(guò)高告警閾值(標(biāo)識(shí)符:MaxHumi)
    • 濕度過(guò)低告警閾值(標(biāo)識(shí)符:MinHumi)
    表 3. 事件
    標(biāo)識(shí)符事件類(lèi)型輸入?yún)?shù)
    TempError告警溫度Temperature
    HumiError告警濕度Humidity
  3. 編寫(xiě)腳本。

    在物聯(lián)網(wǎng)平臺(tái)控制臺(tái),產(chǎn)品詳情頁(yè)面的消息解析頁(yè)簽下,選擇腳本語(yǔ)言,編寫(xiě)腳本。

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

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

    腳本中,解析下行數(shù)據(jù)的函數(shù)protocolToRawData中,需設(shè)定輸出結(jié)果的起始三個(gè)字節(jié),用于指定下行端口號(hào)和下行消息類(lèi)型。否則,系統(tǒng)會(huì)丟掉下行幀。 設(shè)備實(shí)際接收的數(shù)據(jù)中不會(huì)包含這三個(gè)字節(jié)。

    表 4. 下行數(shù)據(jù)解析輸出結(jié)果起始三字節(jié)
    LoRa Downlink字節(jié)數(shù)說(shuō)明
    DFlag1固定為0x5D
    FPort1下行端口號(hào)。
    DHDR1可選:
    • 0:表示 “Unconfirmed Data Down”數(shù)據(jù)幀。
    • 1:表示 “Confirmed Data Down”數(shù)據(jù)幀。

    例如,0x5D 0x0A 0x00表示下行幀端口號(hào)為10,數(shù)據(jù)幀為“Unconfirmed Data Down”。

    完整的示例腳本Demo,請(qǐng)參見(jiàn)本文附錄:示例腳本。

步驟二: 在線測(cè)試腳本

在數(shù)據(jù)解析編輯器中,使用模擬數(shù)據(jù)測(cè)試腳本。

  • 設(shè)備上報(bào)數(shù)據(jù)模擬解析 。

    模擬輸入框中,選擇模擬類(lèi)型為設(shè)備上報(bào)數(shù)據(jù),輸入模擬數(shù)據(jù)000102,單擊運(yùn)行

    運(yùn)行結(jié)果為:

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

    選擇模擬類(lèi)型為設(shè)備接收數(shù)據(jù),輸入以下JSON格式的下行模擬數(shù)據(jù),單擊運(yùn)行

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

    運(yùn)行結(jié)果為:

    0x5d0a000332085a0a

步驟三:提交腳本

確認(rèn)腳本可以正確解析數(shù)據(jù)后,單擊提交,將該腳本提交到物聯(lián)網(wǎng)平臺(tái)系統(tǒng),以供數(shù)據(jù)上下行時(shí),物聯(lián)網(wǎng)平臺(tái)調(diào)用該腳本解析數(shù)據(jù)。

說(shuō)明 僅提交后的腳本才能被物聯(lián)網(wǎng)平臺(tái)調(diào)用;草稿狀態(tài)的腳本不能被調(diào)用。

步驟四:使用真實(shí)設(shè)備調(diào)試

腳本提交后,正式使用之前,請(qǐng)使用真實(shí)設(shè)備進(jìn)行測(cè)試。LoRaWAN節(jié)點(diǎn)設(shè)備如何發(fā)送和接收數(shù)據(jù),請(qǐng)參見(jiàn)模組廠商的相關(guān)手冊(cè)。

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

附錄:示例腳本

根據(jù)以上產(chǎn)品和其功能定義,編輯的示例腳本如下:

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';
/*
 * 示例數(shù)據(jù):
 *  傳入?yún)?shù):
 *      000102 // 共3個(gè)字節(jié)
 *  輸出結(jié)果:
 *      {"method":"thing.event.property.post", "id":"12345", "params":{"Temperature":1,"Humidity":2}, "version":"1.1"}
 *  傳入?yún)?shù):
 *      0102 // 共2個(gè)字節(jié)
 *  輸出結(jié)果:
 *      {"method":"thing.event.TempError.post","id":"12345","params":{"Temperature":2},"version":"1.1"}
 *  傳入?yún)?shù):
 *      0202 // 共2個(gè)字節(jié)
 *  輸出結(jié)果:
 *     {"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;
}
/*
 * 示例數(shù)據(jù):
 *  傳入?yún)?shù):
 *      {"method":"thing.service.SetTempHumiThreshold", "id":"12345", "version":"1.1", "params":{"MaxTemp":50, "MinTemp":8, "MaxHumi":90, "MinHumi":10}}
 *  輸出結(jié)果:
 *      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;
}

/**
 * 將設(shè)備自定義Topic數(shù)據(jù)轉(zhuǎn)換為JSON格式數(shù)據(jù),設(shè)備上報(bào)數(shù)據(jù)到物聯(lián)網(wǎng)平臺(tái)時(shí)調(diào)用。
 * 入?yún)ⅲ簍opic,字符串,設(shè)備上報(bào)消息的Topic。    
 * 入?yún)ⅲ簉awData,byte[]數(shù)組,不能為空。
 * 出參:jsonObj,對(duì)象,不能為空。
 */
function transformPayload(topic, rawData) {
    var jsonObj = {}
    return jsonObj;
}

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

相關(guān)文檔