Buffer not defined anymore in the payload format's JavaScript?

I created https://github.com/thesolarnomad/lora-serialization a while back which utilizes the Buffer object from the JS spec. I got a question recently that was related to encoding messages (https://github.com/thesolarnomad/lora-serialization/issues/24#issuecomment-534107728) and it turns out that I now get Internal error: Encoder threw error: ReferenceError("'Buffer' is not defined") when using the library. Is there any documentation on why Buffer support was removed/restricted or a changelog saying when this happened?

1 Like

Within Node.js documentation, I see that several new Buffer(array) classes are deprecated. Could that point to the issue?

Last time I checked the console didn’t use node.js unfortunately, they used a custom JS implementation in Go: https://github.com/robertkrimen/otto - not sure if that changed.

Are you sure Buffer was ever supported in payload formats?

The decoder part of the LoRa Serialization library doesn’t use it, and I wonder if anyone used the library in an encoder. Buffer is simply not part of EcmaScript 5, which is what TTN/Otto uses.

It worked when I wrote the library.

I’m not sure of the Buffer issue, or from where it originates (my lack of understanding/knowing the engine under the TTN hood and the languages), but clearly it was working in the past. I will look at “Is there any documentation on payload functions?” and will see if I can find an answer there.

Below is the first block of code from encoder.js from lora-serialization with just the unixtime variable. Is the second line Buffer(byteSize) causing the issue, or is it behind the scenes in TTN?

var intToBytes = function(i, byteSize) {
  var buf = new Buffer(byteSize);
  for (var x = 0; x < byteSize; x++) {
    buf[x] = i >> (x * 8);
  }
  return buf;
};

var unixtime = function(i) {
  if (isNaN(i) || i < 0) {
    throw new Error('Unix time must be positive');
  }
  return intToBytes(i, unixtime.BYTES);
};
unixtime.BYTES = 4;

Using lora-serialization alone would be great, but might decide to use it for decode with some of the other information you provide on the “best practices” link as a work around for the encoder. Is it possible to use one for decoding and another for encoding, as long as the node is programmed for that?

Not what you’re asking, but when replacing Buffer with an untyped JavaScript [] array, one just needs to ensure that only bytes (range 0x00…0xFF) are put into such array. So, intToBytes would need an extra & 0xFF after shifting:

var intToBytes = function(i, byteSize) {
  var buf = [];
  for (var x = 0; x < byteSize; x++) {
    buf[x] = i >> (x * 8) & 0xFF;
  }
  return buf;
};

So, it seems the following will do, without the need for Buffer:

var intToBytes = function(i, byteSize) {
  var buf = [];
  for (var x = 0; x < byteSize; x++) {
    buf[x] = i >> (x * 8) & 0xFF;
  }
  return buf;
};

var unixtime = function(i) {
  if (isNaN(i) || i < 0) {
    throw new Error('Unix time must be positive');
  }
  return intToBytes(i, unixtime.BYTES);
};
unixtime.BYTES = 4;

var uint8 = function(i) {
  if (isNaN(i) || i < 0 || i > 255) {
    throw new Error('int be in range 0..255');
  }
  return intToBytes(i, uint8.BYTES);
};
uint8.BYTES = 1;

var uint16 = function(i) {
  if (isNaN(i) || i < 0 || i > 65535) {
    throw new Error('int be in range 0..65535');
  }
  return intToBytes(i, uint16.BYTES);
};
uint16.BYTES = 2;

var latLng = function(latitude, longitude) {
  if (isNaN(latitude) || latitude < -90 || latitude > 90) {
    throw new Error('Latitude must be between -90° and 90°');
  }
  if (isNaN(longitude) || longitude < -180 || longitude > 180) {
    throw new Error('Longitude must be between -180° and 180°');
  }

  return [].concat(
    intToBytes(~~(latitude * 1e6), latLng.BYTES / 2),
    intToBytes(~~(longitude * 1e6), latLng.BYTES / 2)
  );
};
latLng.BYTES = 8;

var temperature = function(i) {
  if (isNaN(i) || i < -327.68 || i > 327.67) {
    throw new Error('Temperature must be in range -327.68..327.67');
  }
  var t = ~~(Math.abs(i) * 1e2);
  var b = ('0000000000000000' + Number(t >>> 0).toString(2)).slice(-16);
  if (i < 0) {
    var arr = b.split('').map(function(x) { return !Number(x); });
    for (var o = arr.length - 1; o > 0; o--) {
      arr[o] = !arr[o];
      if (arr[o]) {
        break;
      }
    }
    b = arr.map(Number).join('');
  }
  return [
    parseInt(b.slice(-16, -8), 2),
    parseInt(b.slice(-8), 2)
  ];
};
temperature.BYTES = 2;

var humidity = function(i) {
  if (isNaN(i) || i < 0 || i > 100) {
    throw new Error('Humidity must be in range 0..100');
  }

  return intToBytes(i * 1e2, humidity.BYTES);
};
humidity.BYTES = 2;

var bitmap = function(a, b, c, d, e, f, g, h) { // eslint-disable-line no-unused-vars
  var base = [];
  for(var i = 0; i < 8; i++) {
    var bit = arguments[i];
    if (typeof bit === 'undefined') {
      base[i] = false;
    } else if (typeof bit !== 'boolean') {
      throw new TypeError('Arguments must be of type boolean');
    } else {
      base[i] = bit;
    }
  }
  var bm = parseInt(base.map(Number).join(''), 2);
  return intToBytes(bm, bitmap.BYTES);
};
bitmap.BYTES = 1;

var encode = function(values, mask) {
  if (!Array.isArray(values)) {
    throw new TypeError('Values must be an array');
  }
  if (!Array.isArray(mask)) {
    throw new TypeError('Mask must be an array');
  }
  if (values.length > mask.length) {
    throw new Error('Mask length is ' + mask.length + ' whereas input is ' + values.length);
  }

  return values
    .reduce(function(acc, args, i) {
      return acc.concat(mask[i].apply(null, Array.isArray(args) ? args : [args]));
    }, []);
};

if (typeof module === 'object' && typeof module.exports !== 'undefined') {
  module.exports = {
    unixtime: unixtime,
    uint8: uint8,
    uint16: uint16,
    temperature: temperature,
    humidity: humidity,
    latLng: latLng,
    bitmap: bitmap,
    encode: encode
  };
}

(I’ve created a pull request, also including a fix for LoraMessage.js.)

2 posts were split to a new topic: How to use “bitmap” in LoRa Serialization library?