简体   繁体   中英

TTN decoder - Node-red

In TTN they are no longer supporting large decoders.

I know what the decoder needs to be in TTN, it is in my DECODER function, but dont know how to execute it in the function node.

If you use inject Payload [1,2,3] RAW, it injects the raw payload that is msg.payload.payload.uplink_message.frm_payload into the decoder.

The DECODER needs to decode the raw payload and output it in msg.payload.uplink_message.decoded_payload

If you use inject Payload [1,2,3] Decoded in the flow you see how the end result needs to look like and the decoded msg.payload.uplink_message.decoded_payload

I am still learning JavaScript.

The code in the function node

function Decoder(bytes, port) {

  // nbits: number of bits used to encode
  // lo: min value that can be encoded
  // hi: max value that can be encoded
  // nresv: number of reserved values, not used for the encoding
  function step_size(lo, hi, nbits, nresv) {
    return 1.0 / ((((1<<nbits) - 1) - nresv) / (hi - lo));
  }

  function mt_value_decode(value, lo, hi, nbits, nresv) {
    return (value - nresv / 2) * step_size(lo, hi, nbits, nresv) + lo;
  }

  // Gets the zero-based unsigned numeric value of the given bit(s)
  function bits(value, lsb, msb) {
    var len = msb - lsb + 1;
    var mask = (1<<len) - 1;
    return value>>lsb & mask;
  }

  // Gets the boolean value of the given bit
  function bit(value, bit) {
    return (value & (1<<bit)) > 0;
  }

  // Gets a hexadecimal representation ensuring a leading zero for each byte
  function hex(bytes, separator) {
    return bytes.map(function (b) {
      return ("0" + b.toString(16)).substr(-2);
    }).join(separator || "");
  }

  // Decodes 4 bytes into a signed integer, MSB
  function int32(bytes) {
    // JavaScript bitwise operators always work with 32 bits signed integers
    return bytes[0]<<24 | bytes[1]<<16 | bytes[2]<<8 | bytes[3];
  }

  // Decodes 4 bytes into an unsigned integer, MSB
  function uint32(bytes) {
    // Or, same result:
    //   return bytes[0] * 0x1000000 + bytes[1] * 0x10000 + bytes[2] * 0x100 + bytes[3];
    //   return bytes[0] * 0x1000000 + (bytes[1]<<16 | bytes[2]<<8 | bytes[3]);
    //   return bytes[0] * (1<<24) + (bytes[1]<<16 | bytes[2]<<8 | bytes[3]);
    //   return int32(bytes)>>>0;

    // JavaScript bitwise operators always work with 32 bits signed integers;
    // force conversion to unsigned 32 bits value using zero-fill right shift
    return (bytes[0]<<24 | bytes[1]<<16 | bytes[2]<<8 | bytes[3])>>>0;    
  }

  // Decodes 1 to 4 MAC addresses and their RSSI
  function mac_rssi(bytes) {
    var items = [];
    for (var offset = 0; offset < bytes.length; offset += 7) {
      items.push({
        mac_address: hex(bytes.slice(offset, offset + 6), ":"),
        // Sign-extend to 32 bits to support negative values; dBm
        rssi: bytes[offset + 6]<<24>>24,
      });
    }
    return items;
  }

  function message(code, descriptions) {
    return {
      code: code,
      description: code < 0 || code >= descriptions.length ? "UNKNOWN" : descriptions[code]
    };
  }

  var decoded = {};
  var i;

  var type = bytes[0];

  // All message types, except for Frame pending messages, share the same header
  if (type !== 0x00) {
    // Note: the Data Storage Integration stores nested objects as text
    decoded.status = {
      mode: message(bits(bytes[1], 5, 7), ["Standby", "Motion tracking", "Permanent tracking",
        "Motion start/end tracking", "Activity tracking", "OFF"]),
      sos: bit(bytes[1], 4),
      tracking: bit(bytes[1], 3),
      moving: bit(bytes[1], 2),
      periodic: bit(bytes[1], 1),
      on_demand: bit(bytes[1], 0)
    };

    // Trackers with a rechargeable battery:the percentage reflects the actual value
    decoded.batteryPersentage = bytes[2];
    
    decoded.temperature = Math.round(100 * mt_value_decode(bytes[3], -44, 85, 8, 0)) / 100;
    
    decoded.ack = bits(bytes[4], 4, 7);
    
    decoded.data = bits(bytes[4], 0, 3);
    
    decoded.lastResetCause = "lastResetCause: " + bytes[5];


  }

  switch (type) {
    case 0x00:
      decoded.type = "FRAME PENDING";
      decoded.token = bytes[1];
      break;

    case 0x03:
      decoded.type = "POSITION";
      switch (decoded.data) {
        case 0:
          decoded.position_type = "GPS fix";
          decoded.age = mt_value_decode(bytes[5], 0, 2040, 8, 0);
          // Signed 32 bits integers; LSB is always zero
          decoded.latitude = (bytes[6]<<24 | bytes[7]<<16 | bytes[8]<<8) / 1e7;
          decoded.longitude = (bytes[9]<<24 | bytes[10]<<16 | bytes[11]<<8) / 1e7;
          // Estimated Horizontal Position Error
          decoded.ehpe = mt_value_decode(bytes[12], 0, 1000, 8, 0);
          break;

        case 1:
          decoded.position_type = "GPS timeout";
          decoded.timeout_cause = message(bytes[5], ["User timeout cause"]);
          for (i = 0; i < 4; i++) {
            // Carrier over noise (dBm) for the i-th satellite seen
            decoded["cn" + i] = mt_value_decode(bytes[6 + i], 0, 2040, 8, 0);
          }
          break;

        case 2:
          // Documented as obsolete
          decoded.error = message(0, ["UNSUPPORTED POSITION TYPE " + decoded.data]);
          break;

        case 3:
          decoded.position_type = "WIFI timeout";
          for (i = 0; i < 6; i++) {
            decoded["v_bat" + (i + 1)] = mt_value_decode(bytes[5 + i], 2.8, 4.2, 8, 2);
          }
          break;

        case 4:
          decoded.position_type = "WIFI failure";
          for (i = 0; i < 6; i++) {
            // Most of time a WIFI timeout occurs due to a low battery condition
            decoded["v_bat" + (i + 1)] = mt_value_decode(bytes[5 + i], 2.8, 4.2, 8, 2);
          }
          decoded.error = message(bytes[11], ["WIFI connection failure", "Scan failure",
            "Antenna unavailable", "WIFI not supported on this device"]);
          break;

        case 5:
        case 6:
          decoded.position_type = "LP-GPS data";
          // Encrypted; not described in the documentation
          decoded.error = message(0, ["UNSUPPORTED POSITION TYPE " + decoded.data]);
          break;

        case 7:
          decoded.position_type = "BLE beacon scan";
          decoded.age = mt_value_decode(bytes[5], 0, 2040, 8, 0);
          // Remaining data: up to 4 beacons
          decoded.beacons = mac_rssi(bytes.slice(6));
          break;

        case 8:
          decoded.position_type = "BLE beacon failure";
          decoded.error = message(bytes[5], ["BLE is not responding", "Internal error", "Shared antenna not available",
            "Scan already on going", "No beacon detected", "Hardware incompatibility"]);
          break;

        // Test with: 0358D895090EC46E1FF44B9EB76466B3B87454AD500959CA1ED4AD525E67DA14A1AC
        // or 032CD1890900C46E1FF44B9EC5C83A355A3898A6
        case 9:
          decoded.position_type = "WIFI BSSIDs";
          decoded.age = mt_value_decode(bytes[5], 0, 2040, 8, 0);
          // Remaining data: up to 4 WiFi BSSIDs
          decoded.stations = mac_rssi(bytes.slice(6));
          break;

        default:
          decoded.error = message(0, ["UNSUPPORTED POSITION TYPE " + decoded.data]);
      }
      break;

    case 0x04:
      decoded.type = "ENERGY STATUS";
      break;

    case 0x05:
      decoded.type = "HEARTBEAT";
      decoded.mcuFirmware = bytes[6] + "." + bytes[7] + "." + bytes[8];
      decoded.bleFirmware = bytes[9] + "." + bytes[10] + "." + bytes[11];

      break;

    case 0x07:
      // Activity status message and configuration message share the same identifier
      var tag = bytes[5];
      switch (tag) {
        case 1:
          decoded.type = "ACTIVITY STATUS";
          decoded.activity_counter = uint32(bytes.slice(6, 10));
          break;

        case 2:
          decoded.type = "CONFIGURATION";
          for (i = 0; i < 5; i++) {
            var offset = 6 + 5 * i;
            decoded["param" + i] = {
            type: bytes[offset],
            value: uint32(bytes.slice(offset + 1, offset + 5))
            };
          }
            break;

        case 3:
          decoded.type = " SHOCK DETECTION";
            decoded.axisX = "fwVersion: " + bytes[7] + "." + bytes[8];
            decoded.axisY = "fwVersion: " + bytes[9] + "." + bytes[10];
            decoded.axisX = "fwVersion: " + bytes[11] + "." + bytes[12];
            //Byte 7-8 - X Axis
            //Byte 9-10 - Y Axis
            //Byte 11-12 - Z Axis
            break;

        default:
          decoded.error = message(0, ["UNSUPPORTED POSITION TYPE " + decoded.data + "/" + tag]);
      }
      break;
   
  

    case 0x09:
      decoded.type = "SHUTDOWN";
      break;

    case 0xFF:
      decoded.type = "DEBUG";
      break;

    default:
      decoded.error = message(0, ["UNSUPPORTED MESSAGE TYPE " + type]);
  }

  // Just some redundant debug info
  decoded.debug = {
    payload: hex(bytes),
    length: bytes.length,
    port: port,
    server_time: new Date().toISOString()
  };

  return decoded;
}

在此处输入图像描述

The question still really isn't clear, but if you want to use that code in a function node then I suggest the following:

Put that code into the "On Start" tab of the function node, but change the first line to the following:

context.Decoder = function(bytes, port) {

// nbits: number of bits used to encode

Then in the "On Message" tab you use the function as follows:

var decoded = contex.Decoder(msg.payload.payload.uplink_message.frm_payload, port)
msg.payload.uplink_message.decoded_payload = decoded
return msg

You've not said where port comes from so I'll leave it up to you to work out what value it should have.

Thank you for you help I needed to add:

    bytes = new Buffer(data,'Base64');

var decoded = context.Decoder(bytes, msg.payload.uplink_message.f_port);
msg.payload.uplink_message.decoded_payload = decoded;
return msg;```

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM