Decoder for LoRa device profiles

For LoRa device profiles, the decode function accepts the following parameters:

function decode(bytes, port, date) {
  var decoded = [];
  // Place your code here and push the results to 'decoded'.
  return decoded;
}
bytes

The message as a byte array.

port

The port number on which the message is transferred. This can be used on some devices, for example, to identify the message type.

date

A date object of the time at which the message was sent from the IoT device. If this information is not available, the receipt date of the message is assigned to this parameter instead.

The decode function must return an array of objects in the following form:

Measurements

{
  type: 'measurement',
  key: 'data_point_key_xyz',
  datetime: 1539093694, // milliseconds since Jan 1, 1970 or Date.prototype.toISOString()
  value: 23.5
}
type

Must be set to 'measurement' for normal measurements.

key

The key of the corresponding data point as defined above in the Data Points table in the Key column.

../../../_images/edit-iot-profiles-keys.png
datetime

Date and time of the measurement. Can be set to Date.now() or derived from the function parameter date. Valid values are either the number of milliseconds since the 1st of January 1970 (Date.prototype.getTime()) or a string according to ISO 8601 (Date.prototype.toISOString()).

value

The actually recorded measurement as an integer, floating point or truth value.

Alarms

{
  type: 'alarm',
  key: 'data_point_key_xyz',
  datetime: 1539093694, // milliseconds since Jan 1, 1970 or Date.prototype.toISOString()
  value: 23.5,
  toState: 'offNormal', // or 'normal' if alarm is gone
  message: 'The alarm message'
}
type

Must be set to 'alarm' for alarms.

key

The key of the corresponding data point as defined above in the Data Points table in the Key column.

../../../_images/edit-iot-profiles-keys.png
datetime

Date and time of the alarm. Can be set to Date.now() or derived from the function parameter date. Valid values are either the number of milliseconds since the 1st of January 1970 (Date.prototype.getTime()) or a string according to ISO 8601 (Date.prototype.toISOString()).

value

The pending alarm value.

toState

'offNormal' In the event of an alarm or 'normal' in the event of an acknowledgement. If the alarm does not refer to the current measurement, but to another problem (e.g. that the value could not be read out correctly), you can also use the status 'fault'.

message

The alarm text as it should be displayed in an alarm ticket and in any notifications.

Auxiliary functions

In this section you will find some auxiliary functions that can simplify the manipulation of byte arrays and the creation of output objects.

Create output object for measurements

// Helper function to generate a measurement object.
function createMeasurement(key, datetime, value) {
  return {
    type: 'measurement',
    key: key,
    datetime: datetime,
    value: value
  };
}

Create measurement object:

decoded.push(createMeasurement('data_point_key_xyz', 1539093694, 24.5));

Create output object for alarms

// Helper function to generate an alarm object.
function createAlarm(key, datetime, value, isGone, message) {
  return {
    type: 'alarm',
    key: key,
    datetime: datetime,
    value: value,
    toState: isGone ? 'normal' : 'offNormal',
    message: message
  };
}

Create alarm object:

decoded.push(createAlarm('data_point_key_xyz', 1539093694, 24.5, false, 'Alarm'));

Parse Float32

// Helper function to generate a float32 from an array of bytes at the given offset.
function getFloat32(bytes, offset, littleEndian) {
  var buffer = new ArrayBuffer(4);
  var view = new DataView(buffer);
  for (var i = 0; i < 4; i++) {
    view.setUint8(i, bytes[offset + i]);
  }
  return Number(view.getFloat32(0, littleEndian));
}

Parse Int16

// Helper function to generate an int16 from an array of bytes at the given offset.
function getInt16(bytes, offset, littleEndian) {
  var buffer = new ArrayBuffer(2);
  var view = new DataView(buffer);
  for (var i = 0; i < 2; i++) {
    view.setUint8(i, bytes[offset + i]);
  }
  return Number(view.getInt16(0, littleEndian));
}

Parse Uint16

// Helper function to generate a uint16 from an array of bytes at the given offset.
function getUint16(bytes, offset, littleEndian) {
  var buffer = new ArrayBuffer(2);
  var view = new DataView(buffer);
  for (var i = 0; i < 2; i++) {
    view.setUint8(i, bytes[offset + i]);
    }
  return Number(view.getUint16(0, littleEndian));
}

Parse Uint8

// Helper function to generate a uint8 from an array of bytes at the given offset.
function getUint8(bytes, offset, littleEndian) {
  var buffer = new ArrayBuffer(1);
  var view = new DataView(buffer);
  view.setUint8(0, bytes[offset]);
  return Number(view.getUint8(0));
}

Parse single bit

// Helper function to extract a single bit from a byte in an array of bytes.
// Bit index 0 is the least significant bit, 7 is the most significant bit.
function getBit(bytes, offset, bitIndex) {
  return (bytes[offset] & (1 << bitIndex)) >> bitIndex;
}

Define number of decimal places of a number

// Helper function to set the number of decimals of a number.
function getFixed(number, decimals) {
  return Number(number.toFixed(decimals));
}

Test Payload

To test the decoding algorithm, enter the expected payload in hexadecimal format (e.g. 007B42F600003039) in the Payload field and then click on the Test button.

If no errors occur, the decoded output object appears in the Result field. In the event of an error, toast notifications appear with a short description of the problem and, if necessary, the incorrect line in the source code.

Complete example

function decode(bytes, port, date) {
  // Decode an uplink message from a buffer (array) of bytes to an object.

  // Helper function to generate a measurement object.
  function createMeasurement(key, datetime, value) {
    return {
      type: 'measurement',
      key: key,
      datetime: datetime,
      value: value
    };
  }

  // Helper function to generate an alarm object.
  function createAlarm(key, datetime, value, isGone, message) {
    return {
      type: 'alarm',
      key: key,
      datetime: datetime,
      value: value,
      toState: isGone ? 'normal' : 'offNormal',
      message: message
    };
  }

  // Helper function to generate a float32 from an array of bytes at the given offset.
  function getFloat32(bytes, offset, littleEndian) {
    var buffer = new ArrayBuffer(4);
    var view = new DataView(buffer);
    for (var i = 0; i < 4; i++) {
      view.setUint8(i, bytes[offset + i]);
    }
    return Number(view.getFloat32(0, littleEndian));
  }

  // Helper function to generate an int16 from an array of bytes at the given offset.
  function getInt16(bytes, offset, littleEndian) {
    var buffer = new ArrayBuffer(2);
    var view = new DataView(buffer);
    for (var i = 0; i < 2; i++) {
      view.setUint8(i, bytes[offset + i]);
    }
    return Number(view.getInt16(0, littleEndian));
  }

  // Helper function to generate a uint16 from an array of bytes at the given offset.
  function getUint16(bytes, offset, littleEndian) {
    var buffer = new ArrayBuffer(2);
    var view = new DataView(buffer);
    for (var i = 0; i < 2; i++) {
      view.setUint8(i, bytes[offset + i]);
    }
    return Number(view.getUint16(0, littleEndian));
  }

  // Helper function to generate a uint8 from an array of bytes at the given offset.
  function getUint8(bytes, offset) {
    var buffer = new ArrayBuffer(1);
    var view = new DataView(buffer);
    view.setUint8(0, bytes[offset]);
    return Number(view.getUint8(0));
  }

  // Helper function to extract a single bit from a byte in an array of bytes.
  // Bit index 0 is the least significant bit, 7 is the most significant bit.
  function getBit(bytes, offset, bitIndex) {
    return (bytes[offset] & (1 << bitIndex)) >> bitIndex;
  }

  // Helper function to set the number of decimals of a number.
  function getFixed(number, decimals) {
    return Number(number.toFixed(decimals));
  }

  var decoded = [];
  var now = Date.now();

  // Add the decoded values to an array.
  // Example:

  // Decode byte 1 (at index 0) as a battery capacity with a value between 0 and 100.
  decoded.push(createMeasurement('battery', now, getFixed(getUint8(bytes, 0) * 100 / 255, 2)));

  // Decode bytes 2 to 3 (starting at index 1) as an unsigned 16-bit integer
  // for the data point with key 'key_1'.
  decoded.push(createMeasurement('key_1', now, getUint16(bytes, 1)));

  // Decode bytes 4 to 7 (starting at index 3) as a 32-bit float
  // for the data point with key 'key_2'.
  decoded.push(createMeasurement('key_2', now, getFloat32(bytes, 3)));

  // Decode bytes 8 to 9 (starting at index 7) as a signed 16-bit integer
  // for the data point with key 'key_3'.
  decoded.push(createMeasurement('key_3', now, getInt16(bytes, 7)));

  // Decode the least significant bit in byte 10 as a binary value (0 or 1).
  // for the data point with key 'key_4'.
  decoded.push(createMeasurement('key_4', now, getBit(bytes, 9, 0)));

  // Decode byte 11 (at index 10) as a binary alarm value (0 or 1)
  // for the data point with key 'key_1' and provide a specific alarm message.
  decoded.push(createAlarm('key_1', now, getBit(bytes, 10, 0), false, 'Add alarm text here'));

  return decoded;
}