# 数据解析

​ 本文档介绍了如何将数据手册中描述的二进制数据解码为 JSON 格式,以及如何将 JSON 格式编码为二进制数据,方便用户将硬件设备直接集成在自己的系统里。JS 脚本主要运行在 Lora 网络服务器中,若网络服务器支持脚本解析,可将编解码的脚本配置在服务器中,这样服务器直接输出 JSON 格式的数据,方便应用服务器的数据处理,同理,应用服务器可直接向网络服务器发送 JSON 格式的参数或命令。若网络服务器不支持脚本解析,用户可参考 Java 代码在自己的应用服务器中实现数据的编解码。

目前支持

  • JavaScript
  • Java(开发中)

# JavaScript

# 解码 (opens new window)

输入参数:

fPort:lora 协议的端口号

bytes:二进制数组,从基站接收的原始数据,若为 Base64 编码,需要先解码。

function Decode(fPort, bytes, variables) {
  var obj = {};
  var offset = 0;
  if( (fPort < 10 || fPort > 27) && fPort != 197 && fPort != 198){
    obj.msgType = "unknown";
    obj.code = 1;
    obj.error = "Unknown fPort,expect([10,25]),actual(" + fPort + ")";
    return obj;
  }

  if(null == bytes){
    return {};
  }

  var indexI, indexJ;
  switch(fPort){
  //It's a heartbeat message.
    case 10:
     {
      obj.msgType = "heartbeat";
      if(bytes.length < 16){
       obj.code = 2;
       obj.error = "Wrong message length,expect(16,18,20),actual(" + bytes.length + ")";
       return obj;
      }
      obj.code = 0;
    
    obj.version = {};
    obj.version.swVer = ((bytes[0] >> 4) & 0x3).toString() + "." + (bytes[0]  & 0xf).toString();
    obj.version.hardwareType = (bytes[0] >> 6);// 1: "gateway" , 0: "badge";
    obj.signal = {};
     if (bytes[1] <= 127)
        obj.signal.rssi = bytes[1] - 20;
    else
        obj.signal.rssi = bytes[1] - 275;
    if(bytes[2] <= 127)
       obj.signal.snr = bytes[2];
    else
      obj.signal.snr = bytes[2]- 255;
    obj.status = {};
    //GNSS status
    obj.status.gnss = (bytes[3] >>> 5) & 0x3; //0:off,1:positioning,2:successful,3:failed
    obj.status.battery = (bytes[3] >> 3) & 0x3; //0:not charging,1:charging,2:complete,3:unknown
    //During the heartbeat period, whether the device is moved.
    obj.status.vibstate = (bytes[3] >> 2) & 0x1;//1: moved,0:static;
    /*
     Only used when the hardware type is badge.
     0 indicates it works as a tracker
     1 indicates it works as a BLE gateway
    */
    obj.workmode = (bytes[3] >> 1) & 0x1;//1:gateway,0:tracker;
    //Calculate the available power of the battery.
    var voltage = bytes[4]/ 100.0 + 2;
    if(voltage < 3.3){
       obj.status.soc = 0;
    }
    else if(voltage >= 4.15){
       obj.status.soc = 100;
    }
    else if(voltage >= 4.1){
       obj.status.soc = 99;
   }
   else if(voltage >= 4.04){
         obj.status.soc = Math.round(96 + 3 * (voltage - 4.04) / (4.1 - 4.04));
    }
    else if(voltage >= 3.97){
         obj.status.soc = Math.round(90 + 6 * (voltage - 3.97) / (4.04 - 3.97));
    }
    else if(voltage >= 3.92){
         obj.status.soc = Math.round(69 + 21 * (voltage - 3.92) / (3.97 - 3.92));
    }
    else if(voltage >= 3.69){
         obj.status.soc =Math.round (25 + 44 * (voltage - 3.69) / (3.92 - 3.69));
    }
    else if(voltage >= 3.4){
         obj.status.soc = Math.round(3 + 22 * (voltage - 3.4) / (3.69 - 3.4));
    }
    else if(voltage >= 3.3){
         obj.status.soc =  Math.round(3 * (voltage - 3.3) / (3.4 - 3.3));
    }
    //Parse the parameters.
    obj.parameters = {};
    obj.parameters.txPower = bytes[5] >>> 6;
    //Data rate
    obj.parameters.dr = (bytes[5] >> 3) & 0x7;
    //Scheme
    // 0:US915,1:EU868,2:AU915,3:CN470,4:AS923,5:KR920,6:IN865,7:RU864;
    obj.parameters.scheme = bytes[5] & 0x7;
    obj.parameters.ble = {};
    obj.parameters.ble.auReport = bytes[6] >>> 7;//1:enable,0:disable;
    var bleperiod = [0,5,10,20,30,60,120,300,600,900,1200,1800,3600,7200,21600,43200];
    obj.parameters.ble.period = bleperiod[(bytes[6] >> 3) & 0x0f];
    var scan = [1,2,3,6,9,12,15,255];
    obj.parameters.ble.scan = scan[bytes[6] & 0x7];
    obj.parameters.ble.scale = (bytes[7] >> 6) & 0x3;
    obj.parameters.ble.stepsOff = ((bytes[7] >> 3) & 0x7) * 5;
    obj.parameters.ble.bleOff = bytes[7] & 0x7;
    obj.parameters.warning = {};
    //buzzer 0:disable,1:enable;
    obj.parameters.warning.buzzer = (bytes[8] >> 5) & 0x1;
    obj.parameters.warning.vibrator = (bytes[8] >> 4) & 0x1;//1:enabled,0:disabled;
    var distance = [2,4,6,8,10,15,255];
    obj.parameters.warning.distance = distance[(bytes[8] >> 1) & 0x7];
    obj.parameters.warning.proximity = bytes[8] & 0x1;//1:enabled,0:disabled;
    
    //Gnss report period
    var gnssperiod = [0,10,20,30,60,120,300,600,1800,3600,7200,10800,21600,43200];
    obj.parameters.gnssPeriod = gnssperiod[(bytes[9] >> 4) & 0xf];
    //Heartbeat report period
    var heartbeatperiod = [60,300,600,1200,1800,3600,7200,21600,43200,86400,86400,86400,86400,86400,86400,86400];
    obj.parameters.heartBeatPeriod = heartbeatperiod[bytes[9] & 0xf];
   
    if(obj.version.swVer > "1.4"){
        obj.parameters.sleepy = {};
        obj.parameters.sleepy.start =  ((bytes[10] & 0x3) << 3) | ((bytes[11] >> 5) & 0x7);
        obj.parameters.sleepy.end = bytes[11] & 0x1f;
        obj.parameters.sleepy.degree = (bytes[10] >> 2) & 0x7;
      
      if(obj.parameters.sleepy.start != obj.parameters.sleepy.end){
        obj.parameters.timestamp = ((bytes[12] & 0xff) << 24) | ((bytes[13] & 0xff) << 16) | ((bytes[14] & 0xff) << 8) | (bytes[15] & 0xff);
        obj.bleAck = (bytes[16] >> 3) & 0x01;
        obj.thres =  bytes[16] & 0x07;
        obj.steps = ((bytes[17] & 0xff) << 16) |  ((bytes[18] & 0xff) << 8) | (bytes[19] & 0xff);
      }
      else{
        obj.bleAck = (bytes[12] >> 3) & 0x01;
        obj.thres =  bytes[12] & 0x07;
        obj.steps = ((bytes[13] & 0xff) << 16) |  ((bytes[14] & 0xff) << 8) | (bytes[15] & 0xff);
      }
    }
    else{
        var dateInd = bytes[10] & 0x40;
        obj.parameters.time = {};
        if(dateInd){
            obj.parameters.time.date = ((bytes[14] & 0xff) << 8) | (bytes[15] & 0xff);
        }
        obj.parameters.time.hour = ((bytes[11] & 0x1)<<4) | (bytes[12] >> 4);
        obj.parameters.time.minute = ((bytes[12] & 0xf) << 2) | (bytes[13] >> 6);
        obj.parameters.time.second = bytes[13] & 0x3f;

        obj.parameters.sleepy = {};
        obj.parameters.sleepy.start =  ((bytes[10] & 0x7) << 2) | (bytes[11] >> 6);
        obj.parameters.sleepy.end = (bytes[11] >> 1) & 0x1f;
        obj.parameters.sleepy.degree = (bytes[10] >> 3) & 0x7;
        //Threshold
        if(dateInd){
            obj.bleAck = (bytes[16] >> 3) & 0x01;
            obj.thres =  bytes[16] & 0x07;
            obj.steps = ((bytes[17] & 0xff) << 16) |  ((bytes[18] & 0xff) << 8) | (bytes[19] & 0xff);
        }
        else{
            obj.bleAck = (bytes[14] >> 3) & 0x01;
            obj.thres =  bytes[14] & 0x07;
            obj.steps = ((bytes[15] & 0xff) << 16) |  ((bytes[16] & 0xff) << 8) | (bytes[17] & 0xff);
        }
    }
    return obj;
  }
case 11:
  {
    obj.msgType = "GNSS coordinate";
    if(bytes.length != 9 && bytes.length != 25){
      obj.code = 2;
      obj.error = "Wrong message length,expect(9,25),actual(" + bytes.length + ")";
      return obj;
    }
    obj.code = 0;
    if(bytes.length == 9)
    {
        obj.longitude = {};
        obj.longitude.orientation = bytes[0] & 0x80  ? "W" : "E";
        var longi = (bytes[0] & 0x7f) << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3];
        obj.longitude.value = (parseInt(longi / 10000000) + (longi % 10000000) / 6000000.0).toFixed(7);
        obj.latitude = {};
        obj.latitude.orientation = bytes[4] & 0x80  ? "S" : "N";
        var lati = (bytes[4] & 0x7f) << 24 | bytes[5] << 16 | bytes[6] << 8 | bytes[7];
        obj.latitude.value = (parseInt(lati / 10000000) + (lati % 10000000) / 6000000.0).toFixed(7);
        obj.time = bytes[8];
    }
    else if(bytes.length == 25)
    {
        obj.longitude = {};
        obj.longitude.orientation = bytes[0] & 0x80  ? "W" : "E";
        var longi = (bytes[0] & 0x7f) << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3];
        obj.longitude.value = (parseInt(longi / 10000000) + (longi % 10000000) / 6000000.0).toFixed(7);
        obj.latitude = {};
        obj.latitude.orientation = bytes[4] & 0x80  ? "S" : "N";
        var lati = (bytes[4] & 0x7f) << 24 | bytes[5] << 16 | bytes[6] << 8 | bytes[7];
        obj.latitude.value = (parseInt(lati / 10000000) + (lati % 10000000) / 6000000.0).toFixed(7);
        obj.longitude1 = {};
        obj.longitude1.orientation = bytes[8] & 0x80  ? "W" : "E";
        longi = (bytes[8] & 0x7f) << 24 | bytes[9] << 16 | bytes[10] << 8 | bytes[11];
        obj.longitude1.value = (parseInt(longi / 10000000) + (longi % 10000000) / 6000000.0).toFixed(7);
        obj.latitude1 = {};
        obj.latitude1.orientation = bytes[12] & 0x80  ? "S" : "N";
        lati = (bytes[12] & 0x7f) << 24 | bytes[13] << 16 | bytes[14] << 8 | bytes[15];
        obj.latitude1.value = (parseInt(lati / 10000000) + (lati % 10000000) / 6000000.0).toFixed(7);  
        obj.longitude2 = {};
        obj.longitude2.orientation = bytes[16] & 0x80  ? "W" : "E";
        longi = (bytes[16] & 0x7f) << 24 | bytes[17] << 16 | bytes[18] << 8 | bytes[19];
        obj.longitude2.value = (parseInt(longi / 10000000) + (longi % 10000000) / 6000000.0).toFixed(7);
        obj.latitude2 = {};
        obj.latitude2.orientation = bytes[20] & 0x80  ? "S" : "N";
        lati = (bytes[20] & 0x7f) << 24 | bytes[21] << 16 | bytes[22] << 8 | bytes[23];
        obj.latitude2.value = (parseInt(lati / 10000000) + (lati % 10000000) / 6000000.0).toFixed(7);
        obj.time = bytes[24];
    }
    return obj;
  }
case 12:
  {
    obj.msgType = "BLE coordinate";
    if(bytes.length < 7){
      obj.code = 2;
      obj.error = "wrong message length,less than minimum length(10)";
      return obj;
    }
    obj.code = 0;

    var majorLen,majorShift,beaconStart;
    if(bytes[1] == 0)
    {
          obj.step = bytes[0] << 16 | bytes[1] << 8 | bytes[2];
         //How many kinds of Major value of the beacons.
          majorLen = bytes[3] & 0xf;
          majorShift = 4 + majorLen;
          beaconStart = 4;
    }
    else{
         majorLen = bytes[0] & 0xf;
         majorShift = 1 + majorLen;
         beaconStart = 1;
         obj.closecontact = (bytes[0] >> 4) & 0x1;
    }
    
    obj.beaconList = [];

    
    for(indexI = 0; indexI < majorLen; indexI++){
      var beaconNum = bytes[beaconStart + indexI];
      var major = (bytes[majorShift]  << 8 | bytes[majorShift + 1]).toString(16);
      
      var minorShift = majorShift + 2;
      for(indexJ = 0; indexJ < beaconNum; indexJ++){
        //var minorShift = majorShift + 2 + indexJ *3;
        var beaconObj = {};
        beaconObj.major = major;
        beaconObj.minor = ((bytes[minorShift] << 8) | bytes[minorShift + 1]).toString(16);
        beaconObj.type = (bytes[minorShift + 2] >> 5) & 0x3;//0:locator,1:asset,2:alarm,3:proximity
        beaconObj.rssi = -(bytes[minorShift + 2] & 0x1f) - 59;
        minorShift += 2;
        if(bytes[minorShift] >> 7){
          beaconObj.battery = bytes[minorShift + 1] & 0x7f;
          minorShift += 1;
        }
        minorShift += 1;
        obj.beaconList.push(beaconObj);
      } 
      majorShift = minorShift;
    }
    return obj;       
  }
case 13:
  {
    obj.msgType = "alarm";
    if(bytes.length != 2){
      obj.code = 2;
      obj.error = "Wrong message length,expect(2),actual(" + bytes.length + ")";
      return obj;
    }

    obj.code = 0;
    obj.msgId = bytes[0];
    obj.ack = (bytes[1] >> 4) & 0x1;// 1:true,0:false;
	//0:SOS,1:SOS dismissed,2:power off,3:BLE disable,4:LoRa disable,5:GPS disable,6:Enter hazardous area,7:Unknown
    obj.alarm = bytes[1] & 0x7;
    
    return obj;
  }
case 14:
  {
    obj.msgType = "ack";
    if(bytes.length != 2){
      obj.code = 2;
      obj.error = "Wrong message length,expect(2),actual(" + bytes.length + ")";
      return obj;
    }
    
    obj.code = 0;
    obj.msgId = bytes[0];
    //0:succeed,1:parameter not supported,2:parameter out of range,3:unknown;
    obj.result = bytes[1] & 0x3;
    
    return obj;
   }
case 15:
  {
    obj.msgType = "Positioning UUID list";
    obj.code = 0;
    if(bytes[0] == 0){
      return {};
    }
    obj.uuidList = [];
    for(indexI = 0; indexI <  bytes[0]; indexI++){
      var uuid = {};
      uuid.index = bytes[1 + 17 * indexI];
      var str = "";
      for(indexJ = 2 + 17 * indexI; indexJ < 18 + 17 * indexI; indexJ++){
        var tmp = bytes[indexJ].toString(16);
        if(tmp.length == 1){
           tmp = "0" + tmp;
        } 
        str += tmp;
      }
      uuid.uuid = str;
      obj.uuidList.push(uuid);
    }
    return obj;
  }
case 16:
  {
    obj.msgType = "Asset UUID list";
    obj.code = 0;
    if(bytes[0] == 0){
      return {};
    }
    obj.uuidList = [];
    for(indexI = 0; indexI <  bytes[0]; indexI++){
      var uuidAsset = {};
      uuidAsset.index = bytes[1 + 17 * indexI];
      var strAsset = "";
      for(indexJ = 2 + 17 * indexI; indexJ < 18 + 17 * indexI; indexJ++){
        var tmpAsset = bytes[indexJ].toString(16);
        if(tmpAsset.length == 1){
           tmpAsset = "0" + tmpAsset;
        } 
        strAsset += tmpAsset;
      }
      uuidAsset.uuid = strAsset;
      obj.uuidList.push(uuidAsset);
    }
    return obj;
  }
case 17:
  {
    obj.msgType = "Pass-through filter list";
    obj.code = 0;
    if(bytes[0] == 0){
      return obj;
    }
    obj.filterList = [];
    var pos = 1;
    for(indexI = 0; indexI <  bytes[0]; indexI++){
      var filterPosPass = {};
      filterPosPass.port = bytes[pos];
      filterPosPass.start = bytes[pos+1];
      filterPosPass.end = bytes[pos+2];
      filterPosPass.filterStart = bytes[pos + 3];
      filterPosPass.filterLen= bytes[pos+4];
      var filterLen = bytes[pos+4];
      var strPosPass = "";
      for(indexJ = 0; indexJ < filterLen; indexJ++){
        var tmpPosPass = bytes[pos+5+indexJ].toString(16);
        if(tmpPosPass.length == 1){
           tmpPosPass = "0" + tmpPosPass;
        } 
        strPosPass += tmpPosPass;
      }
      pos = pos + 5 + filterLen;
      filterPosPass.filter = strPosPass;
      obj.filterList.push(filterPosPass);
    }
    return obj;
  }
case 18:
{
    obj.msgType = "History Beacon Config List";
     obj.code = 0;
    if(bytes[0] == 0){
      return {};
    }
    obj.number = bytes[0];
    obj.beaconList = [];
    for(indexI = 0; indexI <  bytes[0]; indexI++){
      var beacon= {};
      offset = 5 * indexI;
      beacon.index = bytes[1 + offset];
      beacon.major =  (bytes[2 + offset]  << 8 | bytes[3 + offset]).toString(16);
      beacon.minor =  (bytes[4 + offset]  << 8 | bytes[5 + offset]).toString(16);
      obj.beaconList.push(beacon);
    }
    return obj;
}
case 19:
{
    obj.msgType = "History Beacon Info List";
     obj.code = 0;
    if(bytes[0] == 0){
      return {};
    }
    obj.number = bytes[0];
    obj.beaconList = [];
    for(indexI = 0; indexI <  bytes[0]; indexI++){
      var bea= {};
      offset = 9 * indexI;
      bea.major =  (bytes[1 + offset]  << 8 | bytes[2 + offset]).toString(16);
      bea.minor =  (bytes[3 + offset]  << 8 | bytes[4 + offset]).toString(16);
      bea.rssi = -((bytes[5 + offset] & 0x1f) + 59);
      bea.frmOff =  (bytes[6 + offset]  << 8 | bytes[7 + offset]);
      bea.timeOff =  (bytes[8 + offset]  << 8 | bytes[9 + offset]);
      obj.beaconList.push(bea);
    }
    return obj;
}
case 20:
{
    obj.msgType = "History GNSS Info List";
     obj.code = 0;
    if(bytes[0] == 0){
      return {};
    }
    obj.number = bytes[0];
    obj.gnssList = [];
    for(indexI = 0; indexI <  bytes[0]; indexI++){
      var gnss = {};
      offset = 12 * indexI;
      gnss.longitude = {};
      gnss.longitude.orientation = bytes[1+offset] & 0x80  ? "W" : "E";
    var longiH = (bytes[1+offset] & 0x7f) << 24 | bytes[2+offset] << 16 | bytes[3+offset] << 8 | bytes[4+offset];
    gnss.longitude.value = (parseInt(longiH / 10000000) + (longiH % 10000000) / 6000000.0).toFixed(7);
    gnss.latitude = {};
    gnss.latitude.orientation = bytes[5+offset] & 0x80  ? "S" : "N";
    var latiH = (bytes[5+offset] & 0x7f) << 24 | bytes[6+offset] << 16 | bytes[7+offset] << 8 | bytes[8+offset];
    gnss.latitude.value = (parseInt(latiH / 10000000) + (latiH % 10000000) / 6000000.0).toFixed(7);
    gnss.frmOff = (bytes[9 + offset]  << 8 | bytes[10 + offset]);
    gnss.timeoff =  (bytes[11 + offset]  << 8 | bytes[12 + offset]);
      obj.gnssList.push(gnss);
    }
    return obj;
}
case 21:
case 22:
case 23:
case 24:
case 25:
  {
    obj.msgType = "Pass-through data list";
    obj.port = fPort;
    obj.code = 0;
    if(bytes[0] == 0){
      return {};
    }
    obj.dataList = [];
    var dataLen = (bytes.length - 1)/bytes[0];
    for(indexI = 0; indexI <  bytes[0]; indexI++){
      var passData = {};
      passData.index =  indexI;
      var strDataPass = "";
      for(indexJ = 1 + dataLen * indexI; indexJ < 1 + dataLen + dataLen * indexI; indexJ++){
        var tmpDataPass = bytes[indexJ].toString(16);
        if(tmpDataPass.length == 1){
           tmpDataPass = "0" + tmpDataPass;
        } 
        strDataPass += tmpDataPass;
      }
      passData.payload = strDataPass;
      obj.dataList.push(passData);
    }
    return obj;        
  }
  case 26:
  {
      var ii = 0;
      var len = bytes.length;
      var out = {access_points: []};

     for (; ii < len ;) {
    out.access_points.push({macAddress: bytes.slice(ii, ii + 6), signalStrength: int8(bytes[ii+6])});
    ii += 7;
    }
   return out;
  }
case 27:
  {
    return {
       "sat_count": bytes[0],
       "lr1110_gnss": bytes.slice(2, bytes.length)
      };
  }
default:
  return {};
}
return {};
}

// convert a byte value to signed int8
function int8(byte) {
  var sign = byte & (1 << 7);
  if (sign) {
  return 0xFFFFFF00 | byte;
  }
  return byte;
}

# 编码 (opens new window)

输入参数:

fPort:下行 lora 协议端口。

obj:JSON 格式的参数或命令。

function Encode(fPort, obj) {
  var rst = [];
  if(fPort < 10 || fPort > 17){
  console.log("Unknown fPort,expect([10,17]),actual(" + fPort + ")");
  return rst;
}

if(null == obj){
 console.log("obj is null");
 return [];
}

if(obj.msgId <0 || obj.msgId > 255){
  return [];
}
var arrayIndex = 0;
switch(fPort){
  //It's a parameters setting message.
case 10:
  {
    var index;
    rst[arrayIndex++] = obj.msgId;
    var power = [0,1,2,3];
    index = power.indexOf(obj.txPower);
    if(-1 != index){
      rst[arrayIndex++] = 1;
      rst[arrayIndex++] = index;
    }
    
    var dr = [0,1,2];
    index = dr.indexOf(obj.dr);
    if(-1 != index){
      rst[arrayIndex++] = 2;
      rst[arrayIndex++] = index;
    }
    
    var auReport = ["disable","enable"];
    index = auReport.indexOf(obj.bleAuReport);
    if(-1 != index){
      rst[arrayIndex++] = 3;
      rst[arrayIndex++] = index;
    }
    
    var blePeriod = [0,5,10,20,30,60,120,300,600,900,1200,1800,3600,7200,21600,43200];
    index = blePeriod.indexOf(obj.blePeriod);
    if(-1 != index){
      rst[arrayIndex++] = 4;
      rst[arrayIndex++] = index;
    }
    
    var bleScan = [1,2,3,6,9,12,15,255];
    index = bleScan.indexOf(obj.bleScan);
    if(-1 != index){
      rst[arrayIndex++] = 5;
      rst[arrayIndex++] = index;
    }
    
    var scale = [0,1,2,3];
    index = scale.indexOf(obj.scale);
    if(-1 != index){
      rst[arrayIndex++] = 6;
      rst[arrayIndex++] = index;
    }
    
    var bleStepsOff = [0,1,2,3,4,5,6,7];
    index = bleStepsOff.indexOf(obj.bleStepsOff);
    if(-1 != index){
      rst[arrayIndex++] = 7;
      rst[arrayIndex++] = index;
    }
    
    var bleBleOff = [0,1,2,3,4,5,6,7];
    index = bleBleOff.indexOf(obj.bleBleOff);
    if(-1 != index){
      rst[arrayIndex++] = 8;
      rst[arrayIndex++] = index;
    }
    
    var warnBuzzer = ["disable","enable"];
    index = warnBuzzer.indexOf(obj.warnBuzzer);
    if(-1 != index){
      rst[arrayIndex++] = 9;
      rst[arrayIndex++] = index;
    }
    
    var warnVibrator = ["disable","enable"];
    index = warnVibrator.indexOf(obj.warnVibrator);
    if(-1 != index){
      rst[arrayIndex++] = 10;
      rst[arrayIndex++] = index;
    }
    
    var warnDistance = [2,4,6,8,10,15,255];
    index = warnDistance.indexOf(obj.warnDistance);
    if(-1 != index){
      rst[arrayIndex++] = 11;
      rst[arrayIndex++] = index;
    }
    
    var warnProximity = ["disable","enable"];
    index = warnProximity.indexOf(obj.warnProximity);
    if(-1 != index){
      rst[arrayIndex++] = 12;
      rst[arrayIndex++] = index;
    }
    
    var gnssPeriod = [0,10,20,30,60,120,300,600,1800,3600,7200,10800,21600,43200];
    index = gnssPeriod.indexOf(obj.gnssPeriod);
    if(-1 != index){
      rst[arrayIndex++] = 13;
      rst[arrayIndex++] = index;
    }
    
    var heartBeatPeriod = [60,300,600,1200,1800,3600,7200,21600,43200,86400];
    index = heartBeatPeriod.indexOf(obj.heartBeatPeriod);
    if(-1 != index){
      rst[arrayIndex++] = 14;
      rst[arrayIndex++] = index;
    }
   
    if(null != obj.time){
      if(obj.time.hour >= 0 && obj.time.hour <=23 &&
         obj.time.minute >= 0 && obj.time.minute <= 59 &&
         obj.time.second >=0 && obj.time.second <=59){
             rst[arrayIndex++] = 15;
             rst[arrayIndex++] = obj.time.hour;
             rst[arrayIndex++] = obj.time.minute;
             rst[arrayIndex++] = obj.time.second;
      }
      if(null != obj.time.date){
           rst[arrayIndex++] = 19;
           rst[arrayIndex++] =  obj.time.date >> 8;
           rst[arrayIndex++] = obj.time.date & 0xff;  
      }
    }
    else{
        if(null != obj.datetime){
           rst[arrayIndex++] = 15;
           rst[arrayIndex++] =  (obj.datetime >> 24) & 0xff;
           rst[arrayIndex++] = (obj.datetime >> 16) & 0xff;
           rst[arrayIndex++] = (obj.datetime >> 8) & 0xff;
           rst[arrayIndex++] = obj.datetime & 0xff;
        }
    }
    
    if(null != obj.sleepy){
      if(obj.sleepy.start >= 0 && obj.sleepy.start <=23 &&
         obj.sleepy.end >= 0 && obj.sleepy.end <= 23 &&
         obj.sleepy.degree >=0 && obj.sleepy.degree <=7){
         rst[arrayIndex++] = 16;
         rst[arrayIndex++] = obj.sleepy.degree;
         rst[arrayIndex++] = obj.sleepy.start;
         rst[arrayIndex++] = obj.sleepy.end;
      }
    }
    
   var bleThres= [0,1,2,3,4,5,6,7];
    index = bleThres.indexOf(obj.thres);
    if(-1 != index){
      rst[arrayIndex++] = 17;
      rst[arrayIndex++] = index;
    }

   var bleAck = [0,1];
    index = bleAck.indexOf(obj.bleAck);
    if(-1 == index){
       var bleAckStr= ["disable","enable"];
       index = bleAckStr.indexOf(obj.bleAck);
       if(-1 != index){
            rst[arrayIndex++] = 18;
            rst[arrayIndex++] = index;
        }
    }
   else{
         rst[arrayIndex++] = 18;
         rst[arrayIndex++] = index;
   }

    return rst;    
  }
  break;
case 11:
{
        rst[arrayIndex++] = obj.msgId;
        rst[arrayIndex++] = obj.time >> 8;
        rst[arrayIndex++] = obj.time & 0xFF;
        return rst;
}
break;
  //command
case 12:
  {
      rst[arrayIndex++] = obj.msgId;
      if(obj.cmd >= 0 && obj.cmd <= 9){
        rst[arrayIndex] = obj.cmd;
        return rst;
      }
  }
  break;
  //ack
case 13:
  {
      rst[arrayIndex] = obj.msgId;
      return rst;
  }
  break;
 //Positioning beacon UUID setting
case 14:
  //Asset beacon UUID setting
case 15:
  {
       var uuidNum = obj.uuidList.length;
       rst[arrayIndex++] = obj.msgId;
       rst[arrayIndex++] = uuidNum;
       if(uuidNum >= 1 && uuidNum <=5){
         for(var i=0; i<uuidNum; i++){
           var uuidIndex = obj.uuidList[i].index;
           if(uuidIndex >= 0 && uuidIndex <= 4){
             var uuid = obj.uuidList[i].uuid;
             if(uuid.length == 32){
               rst[arrayIndex++] = uuidIndex;
               var pos=0;
               for(var j=0; j<16; j++)
               {
                 var s = uuid.substr(pos, 2);
                 var v = parseInt(s, 16);
                 rst[arrayIndex++] = v;
                 pos += 2;
               }
             }
             else{
                 return [];
             }
           }
         }
         return rst;
       }
  }
  break;
case 16:
  {
       rst[arrayIndex++] = obj.msgId;
       var filterPort = obj.port;
       if(filterPort >= 21 && filterPort <= 25){
             var filterLen = obj.filterLen;
             var filter = obj.filter;          
             if(filter.length == (filterLen<<1)){
               rst[arrayIndex++] = filterPort;
               rst[arrayIndex++] = obj.start;
               rst[arrayIndex++] = obj.end;
               rst[arrayIndex++] = obj.filterStart;
               rst[arrayIndex++] = obj.filterLen;
               var filterPos=0;
               for(var j1=0; j1<filterLen ; j1++)
               {
                 var s1 = filter.substr(filterPos, 2);
                 var v1 = parseInt(s1, 16);
                 rst[arrayIndex++] = v1;
                 filterPos += 2;
               }
            }
            return rst;
         }
     }
  break; 
case 17:
{
       var beaconNum = obj.number;
       rst[arrayIndex++] = obj.msgId;
       rst[arrayIndex++] = beaconNum;
       if(beaconNum >= 1 && beaconNum <=20){
         for(var beaconI=0; beaconI<beaconNum; beaconI++){
           var beaconIndex = obj.beaconList[beaconI].index;
           if(beaconIndex >= 0 && beaconIndex <= 19){
               var beaconMajor= obj.beaconList[beaconI].major;
               var beaconMinor= obj.beaconList[beaconI].minor;
               rst[arrayIndex++] = beaconIndex;
               rst[arrayIndex++] =  beaconMajor >> 8;
               rst[arrayIndex++] = beaconMajor & 0xff;   
               rst[arrayIndex++] =  beaconMinor >> 8;
               rst[arrayIndex++] = beaconMinor & 0xff;   
             }
         }
         return rst;
       }
  }
  break;
default:
}

return [];
}

# 上行消息

本章列出各消息解码后的 JSON 格式示例。

# 心跳消息

端口:10

{
	"bleAck":1,
	"code":0,
	"msgType":"heartbeat",
	"parameters":
	{
		"ble":
		{
			"alReport":0,
			"bleOff":0,
			"period":10,
			"scale":3,
			"scan":1,
			"stepsOff":5
		},
		"dr":3,
		"gnssPeriod":30,
		"heartBeatPeriod":300,
		"scheme":3,
		"sleepy":
		{
			"degree":0, //睡眠程度
			"end":7,
			"start":22
		},
		"time":
		{
			"hour":9,
			"minute":29,
			"second":3
		},
		"txPower":20,
		"warning":{
			"buzzer":0,
			"distance":6,
			"proximity":0,
			"vibrator":0
		}
	},
	"signal":
	{
		"rssi":-27,
		"snr":10.0
	},
	"status":
	{
		"battery":0,
		"gnss":0,
		"soc":100, //电池剩余电量,百分比
		"vibstate":0
	},
	"steps":60,
	"thres":0, //信标RSSI检测门限
	"version":
	{
		"hardwareType":0,
		"swVer":"1.3"
	},
	"workMode":0
}

# GNSS

端口:11

{
	"latitude":  //纬度
	{
		"orientation":"N",
		"value":32.0818933
	},
	"longitude": //经度
	{
		"orientation":"E",
		"value":118.8034133
	},
	"msgType":"GNSS coordinate",
	"time":4
}

# 蓝牙信息

端口:12

{
	"beaconList":[
	{
		"major":"3001",
		"minor":"3000",
		"rssi":-68,
		"type":0
	},
	{
		"major":"3001",
		"minor":"3001",
		"rssi":-75,
		"type":0
	},
	{
		"major":"2001",
		"minor":"2000",
		"rssi":-75,
		"type":0
	}
	],
	"code":0,
	"msgType":"BLE coordinate",
	"step":9710
}

# 报警消息

端口:13

{
	"ack":0,
	"alarm":2, //报警类型,参考数据手册获取具体定义。
	"msgId":0,
	"msgType":"alarm"
}

# 确认消息

端口:14

{
	"code":0,
	"msgId":0,
	"msgType":"ack",
	"result":0
}

# 定位信标 UUID

端口:15

{
	"code":0,
	"msgType":"Positioning UUID list",
	"uuidList":[
	{
		"index":0,
		"uuid":"22222222222222222222222222222222"
	},
	{
		"index":1,
		"uuid":"33333333333333333333333333333333"
	},
	{
		"index":2,
		"uuid":"44444444444444444444444444444444"
	},
	{
		"index":3,
		"uuid":"55555555555555555555555555555555"
	},
	{
		"index":4,
		"uuid":"66666666666666666666666666666666"
	}
	]
}

# 资产信标 UUID

端口:16

{
	"code":0,
	"msgType":"Asset UUID list",
	"uuidList":[
	{
		"index":0,
		"uuid":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	},
	{
		"index":1,
		"uuid":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
	},
	{
		"index":2,
		"uuid":"cccccccccccccccccccccccccccccccc"
	},
	{
		"index":3,
		"uuid":"dddddddddddddddddddddddddddddddd"
	},
	{
		"index":4,
		"uuid":"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
	}
	]
}

# 透传信标过滤器

端口:17

{
	"code":0,
	"filterList":[
	{
		"end":30,
		"filter":"02a6fd0110004653",
		"filterLen":8,
		"filterStart":7,
		"port":21,
		"start":0
	}
	],
	"length":1,
	"msgType":"Pass-through filter list"
}

# 需确认信标配置列表

端口:18

{
	"beaconList":[
	{
		"index":0,
		"major":"2001",
		"minor":"2000"
	},
	{
		"index":1,
		"major":"2001",
		"minor":"2001"
	},
	{
		"index":2,
		"major":"3001",
		"minor":"3000"
	},
	{
		"index":3,
		"major":"3001",
		"minor":"3001"
	}
	],
	"code":0,
	"msgType":"History Beacon Config List",
	"number":4
}

# 历史信标列表

端口:19

{
	"beaconList":[
	{
		"frmOff":3,
		"major":"1101",
		"minor":"110e",
		"rssi":-85,
		"timeOff":50
	}
	],
	"code":0,
	"msgType":"History Beacon Info List",
	"number":1
}

# 历史室外坐标列表

端口:20

{
	"code":0,
	"gnssList":[
	{
		"frmOff":9,
		"latitude":
		{
			"orientation":"N",
			"value":32.0813933
		},
		"longitude":
		{
			"orientation":"E",
			"value":118.8016
		},
		"timeoff":141
	},
	{
		"frmOff":8,
		"latitude":
		{
			"orientation":"N",
			"value":32.0821467
		},
		"longitude":
		{
			"orientation":"E",
			"value":118.80228
		},
		"timeoff":101
	}
	],
	"msgType":"History GNSS Info List",
	"number":2
}

# 透传蓝牙消息

端口:21 到 25

{
	"code":0,
	"dataList":[
	{
		"index":0,
		"payload":"0201061bffa60202a6fd011000465392deae3f6b83a6d5450c61aaaabbbbbf"
	}
	],
	"msgType":"Pass-through data list"
}

# 下行数据

数据以 JSON 格式提供,经编码后转为二进制数据。用户可发送下列 JSON 数据对设备进行控制。

# 参数设置

端口:10

TX Power

{
	"txPower":18,
	"msgId":2
}

Data Rate

{
	"msgId":4,
	"dr":4
}

AUREPORT

{
	"msgId":5,
	"bleAlReport":"enable"
}

BLE

{
	"blePeriod":20,
	"msgId":7
}

SCAN

{
	"msgId":8,
	"bleScan":2
}

SCALE

{
	"scale":0,
	"msgId":9
}

STEPSOFF

{
	"bleStepsOff":3,
	"msgId":11
}

BLEOFF

{
	"bleBleOff":2,
	"msgId":10
}

BUZZER

{
	"warnBuzzer":"enable",
	"msgId":13
}

VIBRATOR

{
	"warnVibrator":"enable",
	"msgId":14
}

DISTANCE

{
	"warnDistance":4,
	"msgId":15
}

PROXIMITY

{
	"warnProximity":"enable",
	"msgId":12
}

GNSS

{
	"gnssPeriod":60,
	"msgId":17
}

HEARTBEAT

{
	"msgId":18,
	"heartBeatPeriod":600
}

TIME

{
	"msgId":1,
	"time":
	{
		"hour":20,
		"minute":20,
		"second":30
	}
}

SLEEP

{
	"msgId":19,
	"sleepy":
	{
		"degree":1,
		"end":6,
		"start":21
	}
}

THRES

{
	"msgId":20,
	"thres":2
}

BLEACK

{
	"bleAck":0,
	"msgId":21
}

多个参数可以同时配置,如:

{
	"warnProximity":"disable",
	"warnVibrator":"disable",
	"bleBleOff":1,
	"warnBuzzer":"disable",
	"scale":3,
	"msgId":22,
	"dr":5,
	"txPower":20,
	"blePeriod":10,
	"bleAck":1,
	"bleStepsOff":2,
	"warnDistance":2,
	"gnssPeriod":30,
	"bleAlReport":"disable",
	"bleScan":1,
	"sleepy":
	{
		"degree":0,
		"end":7,
		"start":23
	},
	"heartBeatPeriod":300,
	"thres":0
}

# BLE 扫描开始时间

端口:11

{
	“time": 12,
	"msgId": 22
}

# 控制命令

端口:12

{
	"cmd": 2,
	"msgId", 23
}

# 确认消息

端口:13

{
	"msgId": 24
}

# 配置定位信标 UUID

端口:14

{
	"msgId":23,
	"uuidList":	[
	{
		"index":0,
		"uuid":"22222222222222222222222222222222"
	},
	{
		"index":1,
		"uuid":"33333333333333333333333333333333"
	},
	{
		"index":2,
		"uuid":"44444444444444444444444444444444"
	},
	{
		"index":3,
		"uuid":"55555555555555555555555555555555"
	},
	{
		"index":4,
		"uuid":"66666666666666666666666666666666"
	}
	]
}

# 配置资产信标 UUID

端口:15

{
	"msgId":24,
	"uuidList":[
	{
		"index":0,
		"uuid":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	},
	{
		"index":1,
		"uuid":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
	},
	{
		"index":2,
		"uuid":"cccccccccccccccccccccccccccccccc"
	},
	{
		"index":3,
		"uuid":"dddddddddddddddddddddddddddddddd"
	},
	{
		"index":4,
		"uuid":"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
	}]
}

# 透传蓝牙过滤器

端口:16

{
	"filter":"02a6fd0110004653",
	"filterStart":7,
	"port":21,
	"start":15,
	"filterLen":8,
	"end":30
}

# 配置需确认信标列表

端口:17

{
	"number":4,
	"beaconList":[
	{
		"major":8193,
		"minor":8192,
		"index":0
	},
	{
		"major":8193,
		"minor":8193,
		"index":1
	},
	{
		"major":12289,
		"minor":12288,
		"index":2
	},
	{
		"major":12289,
		"minor":12289,
		"index":3
	}]
}