Payload Decoder


(Ntnu Radmil) #1

Hi,
I need help with payload function:
I send binary frame from sensors and in my TTN application I receive payload as f.e. 4A EC 51 CA 41. I know that ‘4A’ is my sensorID and ‘EC 51 CA 41’ is value of measurement expresed as 32 bits float nummber. How to translate this ‘EC51CA41’ float to decimal nummber, to get human readably value as f.e. 22,6776784576…


(Arjan) #2

You can’t; that’s not a valid example as 0xEC51CA41 does not decode to 22.68 but to 25.29…

However, a search gets you Decode float sent by Lopy as Node for an implementation of bytesToFloat, which you can use along with:

// Test with 4AEC51CA41 for 74, 25.290000915527344 and 25.290
return {
  sensorID: bytes[0],
  // Take the 4 bytes 1 to 5 (not including), and convert to float:
  value: bytesToFloat(bytes.slice(1, 5)),
  // Unary plus-operator to cast string result of toFixed to a number:
  rounded: +bytesToFloat(bytes.slice(1, 5)).toFixed(3)
};

:warning: An earlier version of bytesToFloat had a bug with its line var bits = ....

Beware when sending floats though:


(Ntnu Radmil) #3

I tried the function but I got resultat ‘0’.
Here is my Payload actually:
02 13 34 3F 4A 66 66 D2 41 4C 00 D2 07 42 4D B7 50 C2 47 where:
34 3F, battery, value of battery
4A 66 66 D2 41, temp sensor (4A), value of sensor (66 66 D2 41)
4C 00 D2 07 42 hum sensor (4C), value of sensor (00 D2 07 42)
4D B7 50 C2 47 press sensor (4D), value of sensor (B7 50 C2 47)

My function to decode only temp (bytes 5-8, 4A 66 66 D2 41):

function Decoder(bytes, port) {

  // Based on https://stackoverflow.com/a/37471538 by Ilya Bursov
  function bytesToFloat(bytes) {
   var bits = bytes[8]<<24 | bytes[7]<<16 | bytes[6]<<8 | bytes[5];
    //bitsstring:bits;
    var sign = (bits>>>31 === 0) ? 1.0 : -1.0;
    var e = bits>>>23 & 0xff;
    var m = (e === 0) ? (bits & 0x7fffff)<<1 : (bits & 0x7fffff) | 0x800000;
    var f = sign * m * Math.pow(2, e - 150);
    return f;
  }  

  // Test with 0082d241 for 26.3134765625
  return {
   
    sensorID: bytes[4],
    // Take bytes 0 to 4 (not including), and convert to float:
    //temp: bytesToFloat(bytes.slice(5, 9))
    temp: bytesToFloat(bytes.slice(5, 9)),
  };
}

Result:

{
  "sensorID": 74,
  "temp": 0
}

Where is the problem? Maybe wrong the variables ‘sign’ and ‘e’, bit position 31 and 23 ???

best…


(Arjan) #4

You should not change the bytesToFloat function; that function does not need to know how your payload is formatted (except if you change MSB/LSB), but just wants 4 bytes for its input. So, simply use the original bytesToFloat along with:

// Test with 0213343F4A6666D2414C00D207424DB750C247 for 26.299999237060547
// outputted with one decimal as 26.3:
return {
  sensorID: bytes[4],
  // Take bytes 5 to 9 (not including), convert to float, convert to a
  // string with one decimal, and use the unary plus-operator to cast
  // the string result of toFixed to a proper number:
  temp: +bytesToFloat(bytes.slice(5, 9)).toFixed(1)
};

Alternatively, showing why bytesToFloat should not be adjusted to a specific sensor, but can be used for multiple values, assuming those are floats too, and using the x++ postfixed increment operator to keep track of the bytes that have been handled:

// Decode all, for readable code.
// Just assuming some encoding here!
// Skip bytes 0 and 1
var b = 2;
var batt = (bytes[b++]<<8 | bytes[b++]) / 1000;
var tempId = bytes[b++];
var temp = +bytesToFloat(bytes.slice(b, b+=4)).toFixed(2);
var humiId = bytes[b++];
var humi = +bytesToFloat(bytes.slice(b, b+=4)).toFixed(2);
var pressId = bytes[b++];
var press = +bytesToFloat(bytes.slice(b, b+=4)).toFixed(2);

// Only return the values we're interested in:
return {
  batt: batt,
  sensorID: tempId,
  temp: temp
};

Of course, the repetitive use of toFixed could also be added to bytesToFloat instead:

return +f.toFixed(2);

Also, please see How do I format my forum post? and please adjust the comments when you change the code!