How to Decode 16 Bit signed Integer

Hi Guys, I working to get temperature from a smart thermometer and I need to decode bytes w/ 16 bit signed integer to ascii.
somebody have a code or node that can decode that can share w/ me?

Could you see bellow the bytes from my flow:
image

the bytes 1C 17 stands for 30,95 as you can see below as get from protocol analyzer:
image

thanks a lot
Alex

If you look carefully you will see that the values that represents 30.95 are 17 0c not 1c 17. Now 0x0c17 is 3095 decimal, so it appears the value is a sixteen bit integer in 1/100ths degree. So the first problem is that it appears you have not identified the right place in your buffer. Once you identify that then you need to convert it, I am not sure of the easiest way of doing that, hopefully someone will suggest the best method.

Hi Colin thanks you are right!
But my buffer sent it in wrong position, as you can see in the log below:

As the correctly value is 0x720b (29,30Âş) and not 0b72 (02 1b 72)!

image

I see that I have two challenges here 1st understanding wy the position ir wrong and 2st to decode 16bit signed integer to decimal/ascii.

Anybody have an idea to fix it?

Best Regards
Alex

I don't see any correlation between but payload buffer and the data from the protocol analyser.
Once you work that out I think you can use msg.payload.readInt16LE(offset) to get it as an integer. It might be BE but I think it is LE.
https://www.w3schools.com/nodejs/ref_buffer.asp

Hi Colin, thanks!
I already test the ReadIn16LE or BE and it´s not work! As you can see below:

image

I included decode at Sliced according the code below:

image

I tried to include, part of bytes at position 29 or 30, on offset 29 or offset 29,32, but doesn´t work!

readInt16LE only needs the start offset (it knows to pick up just two bytes). This simple example works for me. Try it and check it works for you then you could put the node.warn statements in for your offset in your code to see what is going wrong.

[{"id":"6f6689a6.6e9758","type":"debug","z":"514a90a5.c7bae8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":630,"y":341,"wires":[]},{"id":"1e40f3d0.fa8e3c","type":"inject","z":"514a90a5.c7bae8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":102.5,"y":340,"wires":[["c59edf46.f69bc"]]},{"id":"c59edf46.f69bc","type":"function","z":"514a90a5.c7bae8","name":"Create buffer","func":"msg.payload = new Buffer([0,0x72,0x0b,3,4])\nreturn msg;","outputs":1,"noerr":0,"x":275.5,"y":340,"wires":[["be665ce8.75a46"]]},{"id":"be665ce8.75a46","type":"function","z":"514a90a5.c7bae8","name":"Decode","func":"node.warn(\"Bytes are \" + msg.payload[1].toString(16) + \" \" + msg.payload[2].toString(16))\nmsg.payload = msg.payload.readInt16LE(1)\nreturn msg;","outputs":1,"noerr":0,"x":449.5,"y":341,"wires":[["6f6689a6.6e9758"]]}]

Hi Colin, thanks I got success partially.
As you see in my first post, I have two bytes that represent temperature, but the 2st byte I need to decode to real byte because the serial transmission method (for ex. 02 1b represents 0x0b)

to extract a real byte, in the 2st temperature byte, I using the code below:

let a = msg.payload[30];
let temp2stByte = (a^0x10).toString(16).padStart(2,"0");

in the example below I have tried to using the temp2stByte,readInt16LE, but doesn´t work!

let a = msg.payload[30];																				        let temp2stByte = (a^0x10).toString(16).padStart(2,"0");																								node.warn("Bytes are " + msg.payload[31].toString(16) + " " + temp2stByte )
msg.payload = msg.payload.readInt16LE(31) + **temp2stByte.readInt16LE** 
return msg;

Note: the 1st byte (msg.payload.readInt16LE(31)) is running well!
Please, do you can advise me how to make a working around to fix the 2st Byte?

Hi. You do realise that readInt16LE means read a 16 bit integer Little Endian and thus will read 2 bytes? (16 = 2x8 bits per byte).

If you experiment with the read functions you will figure this out.

Hint: there are read int, read float, 8 bit, 16 bit, 32 bit variations etc. Just check the documentation for nodejs buffer you'll figure it out & you'll learn/remember if you manage to do this yourself

Best of luck.

Hi Steve, You are right. But to read 16 bits I need before to convert the serial sequence bytes to real byte ( 0x02 0x10 0x1c -> 0x0c) and after that I can read the 2 bytes (0x0c 0x17).

image

I had success to read line using offset (0x17) using msg.payload.readInt16LE(31) but the 2st byte i lost the offset reference, after I have used the code below:

let temp2stByte = (a^0x10).toString(16).padStart(2,"0"); 

if I using temp2stByte.readline(), but I have this error:
image

I can´t find reference how to use readInt16LE in the 2 byte.
Do you can advise me how to fix it?

not really sure where your going with this???

So, in an effort to put you on the right track I will try to explain what I think you are attempting to do.

If i'm not mistaken, the data you see in the payload is a NodeJS Buffer.

Looking at the decoded image you attached in your first message, the data is formatted like a protocol (I don't know what protocol) however, the picture gives some clues to the end part of the message.

Based on what I can see, you should really decode the whole msp.payload reply (since the parts of data before the "measured value" are probably important)

That said, the below should get you started....

testcode...

//fake the msg.payload (simulate serial reply)
var msg = {};
msg.payload = new Buffer.from([0x40, 0x1, 0x2, 0x4, 0x4, 0x1, 0xa, 0xa6, 0x18, 0x7e, 0x1, 0x0, 0x0, 0x0, 0x29, 0x17, 0x0c]);

//decode the status record
var statusRecord = {};
statusRecord.attribute = msg.payload.readInt16LE(11);
statusRecord.success = msg.payload.readInt8(13);
statusRecord.dataType = msg.payload.readInt8(14);
statusRecord.measuredValue = msg.payload.readInt16LE(15);

//todo: Check the success flag is 0
//todo: Check dataType before calling the next buffer.readXXXX
//      e.g if dataType is byte then call readInt8() to get measuredValue

if(statusRecord.success === 0){
    console.log(`Read was successful :)`);
    console.log(statusRecord);
    console.log(`Measured Value: ${statusRecord.measuredValue / 100.0} [*C]`);
} else {
    console.log(`Read was unsuccessful! :(`);
}

output...

Read was successful :)
{ attribute: 0, success: 0, dataType: 41, measuredValue: 3095 }
Measured Value: 30.95 [*C]

You can play with it here >> Compile and Execute C++0x Onlinej

Hope that helps

1 Like

Hi Steve, firstly I would like to say thanks a lot!
Your code is perfect, but as you see bellow the bytes that represent temperature in buffer array is [1c 17], this is happen because serial transition method that always to encode 0x0c on serial transmission represents 0x2 10 1c, according rules below:
image

In my first example (buffer bytes) I have: 29: 0x2, 30: 0x1c, 31: 0x17

To get temperature I´m using the array position at 30 (0x1c) and 30 (0x17)
Before to convert bytes above in 16 bits LE, I need to get real byte at position 30, to have a real temperature measurement (0x0c 0x17) that corresponding 3095
image

but I cant to use readInt16LE at position 30 before I have real byte (0x0c).
To convert to real byte at position 30 (from 0x1c to 0x0c) I using this code:

let a = msg.payload[30];
let temp2stByte = (a^0x10).toString(16).padStart(2,"0");

After It I cant use readInt16LE in a real byte temperature 0x0c storage in the variable temp2stByte.
That is my problem Steve!
Do you can advise me how to fix it?

Hello again @aargollo

I am guessing english is not your 1st language & that may be the reason for some confusion here.

At first you say "working to get temperature from a smart thermometer"
Then you say "But my buffer sent it in wrong position, as you can see in the log below"

It is my guess that you need to read values from a Buffer object that arrives in msg.payload

And only now you post a relevant piece of the puzzle...

it is my suspicion, the protocol is fully documented & its where where you got that piece :point_up: of info is from.

OK, so I will invent/guess the 1st 28 bytes of your reply and I will attempt to show you something that SHOULD get you moving towards a solution.

IMPORTANT
Many assumptions have been made! As I don't have the full protocol specification, many assumptions have been made. The below code is an attempt to help you help yourself...
I strongly recommend you understand what all the other values mean. For example, I am certain byte[0] (0x1) is SOH and the last byte (0x3) is ETX REF. Many of the other BYTEs and INTs in the Buffer will be relevant but since I dont have that information, I will work backwards from the last byte as your examples indicate the temperature is in the last 2 bytes (minus the ETX)

Playtime (guesswork)...

//fake the msg.payload (simulate serial reply)
var msg = {};
/*                 0     1     2     3     4     5     6     7     8     9    */
var serialData = [ 0x01, 0x81, 0x02, 0x12, 0x02, 0x10, 0x02, 0x1f, 0xde, 0x92,
                  0xd5, 0x72, 0x02, 0x1a, 0x02, 0x14, 0x02, 0x12, 0x02, 0x10,
                  0x02, 0x10, 0x02, 0x10, 0x29, 0x02, 0x10, 0x02, 0x12, 0x02,
                  0x1b, 0x72, 0x39, 0x3]
msg.payload = new Buffer.from(serialData);

//Loop through and decode any encoded values...
var decodeNext = false;
var decodedData = [];
for (var i = 0; i < msg.payload.length; i++) {
  //get this byte
  let b = msg.payload[i];
  //if last was 0x2, deduct 0x10 and store it
  if (decodeNext && b >= 0x10) {
    decodedData.push(b - 0x10);
    decodeNext = false;//reset flag
    continue;//next loop
  }
  if (b === 0x2) {
    decodeNext = true;//set flag & continue (dont store this one)
    continue;//next loop
  }
  //if we reach here - simply store it
  decodedData.push(b);
}
console.log(decodedData);

//make a buffer from new decoded data (not necessary but the read functions work nicely)
var decodedBuffer = new Buffer.from(decodedData);

//decode the status record
var statusRecord = {};
var lastByte = decodedBuffer.length - 1;
statusRecord.attribute = decodedBuffer.readInt16BE(lastByte - 7);
statusRecord.success = decodedBuffer.readInt8(lastByte - 5);
statusRecord.dataType = decodedBuffer.readInt8(lastByte - 4);
statusRecord.measuredValue = decodedBuffer.readInt16BE(lastByte - 3);


//todo: Check the success flag is 0
//todo: Check dataType before calling the next buffer.readXXXX
//      e.g if dataType is byte then call readInt8() to get measuredValue

if (statusRecord.success === 0) {
  console.log(`Read was successful :)`);
  console.log(statusRecord);
  console.log(`Measured Value: ${statusRecord.measuredValue / 100.0} [°C]`);
} else {
  console.log(`Read was unsuccessful! :(`);
}

output...

[1, 129, 2, 0, 15, 222, 146, 213, 114, 10, 4, 2, 0, 0, 0, 41, 0, 2, 11, 114, 57, 3]
Read was successful :) 
{attribute: 41, success: 0, dataType: 2, measuredValue: 2930}
Measured Value: 29.3 [°C] 

play with it here...
Edit decoding guesswork

1 Like

Hi Steve, sorry for my mistake in describing the challenge!
Thanks a lot your code is wonderful!!!!

Your are the best!!!!

Best Regards,
Alex

I've got error:
TypeError: msg.payload.readInt16LE is not a function

@farcry this thread is very old. Please start a new thread and detail what exactly is your issue. Include screenshots and debug output messages to assist us.

Thread closed