Function doesn't work

Hi All,

I've made a small test flow and fiddling with decoding my MQTT payload and it still won't work :smirk:
My goal is to get a output like this:

{
  "protocol_version": 2,
  "device_id": 3547,
  "battery_voltage": {
    "displayName": "Battery voltage",
    "value": 2.685,
    "unit": "V"
  },
  "air_temperature": {
    "displayName": "Air temperature",
    "value": 22.74357213702602,
    "unit": "°C"
  },
  "air_humidity": {
    "displayName": "Air humidity",
    "value": 36.939040207522694,
    "unit": "%"...........etc...etc...

This is my test flow, Base64 payload is converted to a buffer:

[{"id":"d78ead4c.b75c2","type":"inject","z":"66cdaed7.72f43","name":"","topic":"","payload":"Ag3bAH8Kf2F5ZFHDSgAtABGBuAAAlcwAAAAw","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":450,"y":380,"wires":[["ec2ad8ba.e6dc88"]]},{"id":"ec2ad8ba.e6dc88","type":"base64","z":"66cdaed7.72f43","name":"","action":"","property":"payload","x":580,"y":380,"wires":[["3d41820e.8e929e","9e58a89e.c8dc78"]]},{"id":"3d41820e.8e929e","type":"function","z":"66cdaed7.72f43","name":"decode","func":"var decentlab_decoder = {\n  PROTOCOL_VERSION: 2,\n  SENSORS: [\n    {length: 1,\n     values: [{name: 'battery_voltage',\n               displayName: 'Battery voltage',\n               convert: function (x) { return x[0] / 1000; },\n               unit: 'V'}]},\n    {length: 2,\n     values: [{name: 'air_temperature',\n               displayName: 'Air temperature',\n               convert: function (x) { return 175 * x[0] / 65535 - 45; },\n               unit: '°C'},\n              {name: 'air_humidity',\n               displayName: 'Air humidity',\n               convert: function (x) { return 100 * x[1] / 65535; },\n               unit: '%'}]},\n    {length: 1,\n     values: [{name: 'barometric_pressure',\n               displayName: 'Barometric pressure',\n               convert: function (x) { return x[0] * 2; },\n               unit: 'Pa'}]},\n    {length: 2,\n     values: [{name: 'ambient_light_visible_infrared',\n               displayName: 'Ambient light (visible + infrared)',\n               convert: function (x) { return x[0]; }},\n              {name: 'ambient_light_infrared',\n               displayName: 'Ambient light (infrared)',\n               convert: function (x) { return x[1]; }},\n              {name: 'illuminance',\n               displayName: 'Illuminance',\n               convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n               unit: 'lx'}]},\n    {length: 3,\n     values: [{name: 'co2_concentration',\n               displayName: 'CO2 concentration',\n               convert: function (x) { return x[0] - 32768; },\n               unit: 'ppm'},\n              {name: 'co2_sensor_status',\n               displayName: 'CO2 sensor status',\n               convert: function (x) { return x[1]; }},\n              {name: 'raw_ir_reading',\n               displayName: 'Raw IR reading',\n               convert: function (x) { return x[2]; }}]},\n    {length: 1,\n     values: [{name: 'activity_counter',\n               displayName: 'Activity counter',\n               convert: function (x) { return x[0]; }}]},\n    {length: 1,\n     values: [{name: 'total_voc',\n               displayName: 'Total VOC',\n               convert: function (x) { return x[0]; },\n               unit: 'ppb'}]}\n  ],\n\n  read_int: function (bytes, pos) {\n    return (bytes[pos] << 8) + bytes[pos + 1];\n  },\n\n  decode: function (msg) {\n    var bytes = msg;\n    var i, j;\n    if (typeof msg === 'string') {\n      bytes = [];\n      for (i = 0; i < msg.length; i += 2) {\n        bytes.push(parseInt(msg.substring(i, i + 2), 16));\n      }\n    }\n\n    var version = bytes[0];\n    if (version != this.PROTOCOL_VERSION) {\n      return {error: \"protocol version \" + version + \" doesn't match v2\"};\n    }\n\n    var deviceId = this.read_int(bytes, 1);\n    var flags = this.read_int(bytes, 3);\n    var result = {'protocol_version': version, 'device_id': deviceId};\n    // decode payload\n    var pos = 5;\n    for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n      if ((flags & 1) !== 1)\n        continue;\n\n      var sensor = this.SENSORS[i];\n      var x = [];\n      // convert data to 16-bit integer array\n      for (j = 0; j < sensor.length; j++) {\n        x.push(this.read_int(bytes, pos));\n        pos += 2;\n      }\n\n      // decode sensor values\n      for (j = 0; j < sensor.values.length; j++) {\n        var value = sensor.values[j];\n        if ('convert' in value) {\n          result[value.name] = {displayName: value.displayName,\n                                value: value.convert.bind(this)(x)};\n          if ('unit' in value)\n            result[value.name]['unit'] = value.unit;\n        }\n      }\n    }\n    return result;\n  }\n};\n","outputs":1,"noerr":0,"x":760,"y":380,"wires":[["72cafe26.799e1"]]},{"id":"9e58a89e.c8dc78","type":"debug","z":"66cdaed7.72f43","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":750,"y":300,"wires":[]},{"id":"72cafe26.799e1","type":"debug","z":"66cdaed7.72f43","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":950,"y":380,"wires":[]}]

This is the payload from my MQTT node, which is injected into the test flow

image

Here is the function from my device:

var decentlab_decoder = {
  PROTOCOL_VERSION: 2,
  SENSORS: [
    {length: 1,
     values: [{name: 'battery_voltage',
               displayName: 'Battery voltage',
               convert: function (x) { return x[0] / 1000; },
               unit: 'V'}]},
    {length: 2,
     values: [{name: 'air_temperature',
               displayName: 'Air temperature',
               convert: function (x) { return 175 * x[0] / 65535 - 45; },
               unit: '°C'},
              {name: 'air_humidity',
               displayName: 'Air humidity',
               convert: function (x) { return 100 * x[1] / 65535; },
               unit: '%'}]},
    {length: 1,
     values: [{name: 'barometric_pressure',
               displayName: 'Barometric pressure',
               convert: function (x) { return x[0] * 2; },
               unit: 'Pa'}]},
    {length: 2,
     values: [{name: 'ambient_light_visible_infrared',
               displayName: 'Ambient light (visible + infrared)',
               convert: function (x) { return x[0]; }},
              {name: 'ambient_light_infrared',
               displayName: 'Ambient light (infrared)',
               convert: function (x) { return x[1]; }},
              {name: 'illuminance',
               displayName: 'Illuminance',
               convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },
               unit: 'lx'}]},
    {length: 3,
     values: [{name: 'co2_concentration',
               displayName: 'CO2 concentration',
               convert: function (x) { return x[0] - 32768; },
               unit: 'ppm'},
              {name: 'co2_sensor_status',
               displayName: 'CO2 sensor status',
               convert: function (x) { return x[1]; }},
              {name: 'raw_ir_reading',
               displayName: 'Raw IR reading',
               convert: function (x) { return x[2]; }}]},
    {length: 1,
     values: [{name: 'activity_counter',
               displayName: 'Activity counter',
               convert: function (x) { return x[0]; }}]},
    {length: 1,
     values: [{name: 'total_voc',
               displayName: 'Total VOC',
               convert: function (x) { return x[0]; },
               unit: 'ppb'}]}
  ],

  read_int: function (bytes, pos) {
    return (bytes[pos] << 8) + bytes[pos + 1];
  },

  decode: function (msg) {
    var bytes = msg;
    var i, j;
    if (typeof msg === 'string') {
      bytes = [];
      for (i = 0; i < msg.length; i += 2) {
        bytes.push(parseInt(msg.substring(i, i + 2), 16));
      }
    }

    var version = bytes[0];
    if (version != this.PROTOCOL_VERSION) {
      return {error: "protocol version " + version + " doesn't match v2"};
    }

    var deviceId = this.read_int(bytes, 1);
    var flags = this.read_int(bytes, 3);
    var result = {'protocol_version': version, 'device_id': deviceId};
    // decode payload
    var pos = 5;
    for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {
      if ((flags & 1) !== 1)
        continue;

      var sensor = this.SENSORS[i];
      var x = [];
      // convert data to 16-bit integer array
      for (j = 0; j < sensor.length; j++) {
        x.push(this.read_int(bytes, pos));
        pos += 2;
      }

      // decode sensor values
      for (j = 0; j < sensor.values.length; j++) {
        var value = sensor.values[j];
        if ('convert' in value) {
          result[value.name] = {displayName: value.displayName,
                                value: value.convert.bind(this)(x)};
          if ('unit' in value)
            result[value.name]['unit'] = value.unit;
        }
      }
    }
    return result;
  }
};

It would be nice if I could get some help with this.

I think this is a job for node-red-contrib-buffer-parser.

Hi Colin,

Thank you for the hint. I can't get it right with node-red-buffer-parser.

..I wonder if there are any other options

You have added some code to a function node (copy + paste I guess) but you dont call the decode function nor do you return anything

Add this to the bottom of your function...

msg.payload = decentlab_decoder.decode(msg.payload)
return msg

Proof...

1 Like

Hi Steve,

Great, I get the same results with your addition.
I did indeed make a copy and noticed after your post that I had not copied a part at the very bottom of the code.

function main() {
  console.log(decentlab_decoder.decode("020bbd007f0b926a515d48bc4e0262006981c7000093d4000b0111"));
   console.log(decentlab_decoder.decode("020bbd006f0b926a515d48bc4e02620069000b0111"));
   console.log(decentlab_decoder.decode("020bbd00010b92"));
}

main();

I added that part as a test so that the original was complete, but I didn't get any output with it.
Thanks again for the support !

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.