How to decode 16 bit unsigned integer

Hi, I found a solution in this post by @Colin to decode 16 bit SIGNED integer, but I have in Modbus register voltage values as 16 bit UNSIGNED integer.

If this msg.payload[22] = (msg.payload[22] << 16) >> 16 works to 16bit SIGNED, how to convert 16bit UNSIGNED?


If it is an unsigned integer then you don't need to do anything at all.

However, for modbus buffer decoding I recommend node-red-contrib-buffer-parser which will probably do everything you need, and more.

I need to divide by 10, because decimal is missing. OK, thank you.

What about 32 bit signed? msg.payload[26] = (msg.payload[26] << 32) >> 32 ?

You should use [node-red-contrib-buffer-parser]("node-red-contrib-buffer-parser (node) - Node-RED" It does all of this and more. It was designed for this.

Search the forum for a taste of the problems it has solved.

Buffer parser is little complex and difficult for my use. I'm using modbustcp node to read 126 registers from the Solax inverter. Some registers are in 16-bit signed, some 16-bit unsigned, and some 32-bit signed.

Look at the screen, I have an array with all values. I cannot address [22] with "length" and "offset" in parser UI. Please advise.


Read the built in help and read some of the posts in the forum. It's not too difficult to get your head around, but once you do, it will greatly simplify your solution.

1 thing, attach a debug node (set to show full message) to the output of your modbustcp node. Expand the msg object until you find the buffer (msg.reponsedata.buffer or something like that), use that as the property (instead of msg.payload)

Read the buffer-parser built in help. it clearly explains what length, offset, and other fields are

Yes, I have read help, but I can't understand it. I'm sending a whole message to the parser and on the output I have a complete array (0-126) but the numbers are damaged, maybe converted, so they do not make sense.

If I display complete msg object from modbustcp on debug node, I don't have msg.reponsedata.buffer or something like that. There is only an array.

Are you certain the debug is set to show complete message?

Can you show me a screen shot please?

Hmmm odd. Normally much more in the message than that for the modbus nodes.

Anyhow, I will try to provide you some better advise & a demo in the morning, but in the mean time, could you copy that array (using the copy button that appears under your mouse when you hover over debug message) and provide a few other details...

  • What the values are supposed to be (just a few of them)
  • names
  • data types
  • expected values
    ... And I'll set it up as a demo to show you how to arrange things.

Hi Oliver,

Because you are using the node-red-contrib-modbustcp node and this node apparently doesnt reply with the buffer of the values.

Is it easy to replace that node with node-red-contrib-modbus ?
They work more or less the same way but with more features.

It will be much easier to use this set of nodes with Buffer-parser.

1 Like

Here is complete message copied from Nodered.

Let's look at Batter power in register [22] that is 16 bit signed. Expected decoded value is about -27W, because battery is discharging. If battery is charging, there will be positive value with max value about 6300W.

If you want to look on details and other registers, source is here:

I tried more nodes, but no other could connect to the IP address of the inverter.

Do these payload values look right?

Demo flow...

[{"id":"3cdc61d2.9d548e","type":"inject","z":"f77cda4d.5d7628","name":"go","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1050,"y":80,"wires":[["7d72ded.8fbf02"]]},{"id":"7d72ded.8fbf02","type":"function","z":"f77cda4d.5d7628","name":"modbus msg","func":"\nreturn { \"settings\": { \"name\": \"Solax\", \"topic\": \"Solax\", \"adr\": \"0\", \"quantity\": \"126\", \"dataType\": \"InputRegister\", \"ieeeType\": \"off\", \"ieeeBE\": true, \"timerID\": { \"_idleTimeout\": 5000, \"_idlePrev\": { \"_idleNext\": \"[Circular ~.settings.timerID]\", \"_idlePrev\": { \"_idleTimeout\": 5000, \"_idlePrev\": { \"_idleTimeout\": 5000, \"_idlePrev\": { \"_idleTimeout\": 5000, \"_idlePrev\": { \"_idleTimeout\": 5000, \"_idlePrev\": { \"_idleTimeout\": 5000, \"_idlePrev\": { \"_idleTimeout\": 5000, \"_idlePrev\": \"[Circular ~.settings.timerID]\", \"_idleNext\": \"[Circular ~.settings.timerID._idlePrev._idlePrev._idlePrev._idlePrev._idlePrev._idlePrev]\", \"_idleStart\": 43475234, \"_repeat\": null, \"_destroyed\": false }, \"_idleNext\": \"[Circular ~.settings.timerID._idlePrev._idlePrev._idlePrev._idlePrev._idlePrev]\", \"_idleStart\": 43475233, \"_repeat\": null, \"_destroyed\": false }, \"_idleNext\": \"[Circular ~.settings.timerID._idlePrev._idlePrev._idlePrev._idlePrev]\", \"_idleStart\": 43475232, \"_repeat\": null, \"_destroyed\": false }, \"_idleNext\": \"[Circular ~.settings.timerID._idlePrev._idlePrev._idlePrev]\", \"_idleStart\": 43475231, \"_repeat\": null, \"_destroyed\": false }, \"_idleNext\": \"[Circular ~.settings.timerID._idlePrev._idlePrev]\", \"_idleStart\": 43475230, \"_repeat\": null, \"_destroyed\": false }, \"_idleNext\": \"[Circular ~.settings.timerID._idlePrev]\", \"_idleStart\": 43475229, \"_repeat\": null, \"_destroyed\": false }, \"expiry\": 43480229, \"id\": -9007199254620531, \"msecs\": 5000, \"priorityQueuePosition\": 17 }, \"_idleNext\": { \"_idleTimeout\": 5000, \"_idlePrev\": \"[Circular ~.settings.timerID]\", \"_idleNext\": { \"_idleTimeout\": 5000, \"_idlePrev\": \"[Circular ~.settings.timerID._idleNext]\", \"_idleNext\": { \"_idleTimeout\": 5000, \"_idlePrev\": \"[Circular ~.settings.timerID._idleNext._idleNext]\", \"_idleNext\": { \"_idleTimeout\": 5000, \"_idlePrev\": \"[Circular ~.settings.timerID._idleNext._idleNext._idleNext]\", \"_idleNext\": { \"_idleTimeout\": 5000, \"_idlePrev\": \"[Circular ~.settings.timerID._idleNext._idleNext._idleNext._idleNext]\", \"_idleNext\": { \"_idleTimeout\": 5000, \"_idlePrev\": \"[Circular ~.settings.timerID._idleNext._idleNext._idleNext._idleNext._idleNext]\", \"_idleNext\": { \"_idleNext\": \"[Circular ~.settings.timerID]\", \"_idlePrev\": \"[Circular ~.settings.timerID._idleNext._idleNext._idleNext._idleNext._idleNext._idleNext]\", \"expiry\": 43480229, \"id\": -9007199254620531, \"msecs\": 5000, \"priorityQueuePosition\": 17 }, \"_idleStart\": 43475229, \"_repeat\": null, \"_destroyed\": false }, \"_idleStart\": 43475230, \"_repeat\": null, \"_destroyed\": false }, \"_idleStart\": 43475231, \"_repeat\": null, \"_destroyed\": false }, \"_idleStart\": 43475232, \"_repeat\": null, \"_destroyed\": false }, \"_idleStart\": 43475233, \"_repeat\": null, \"_destroyed\": false }, \"_idleStart\": 43475234, \"_repeat\": null, \"_destroyed\": false }, \"_idleStart\": 43479503, \"_repeat\": 5000, \"_destroyed\": false } }, \"topic\": \"Solax\", \"payload\": [0, 0, 0, 2841, 2239, 0, 0, 0, 40, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4781, 0, 65509, 1, 18, 0, 0, 0, 96, 3465, 0, 0, 8, 4598, 0, 0, 124, 350, 0, 0, 5, 124, 0, 96, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 0, 56055, 0, 0, 0, 0, 5000, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 7506, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2312, 0, 1, 17, 2310, 0, 0, 18], \"_msgid\": \"55713a82.52b914\" };","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1050,"y":140,"wires":[["f5192783.22df58","ac96e26c.05cc4"]]},{"id":"f5192783.22df58","type":"buffer-parser","z":"f77cda4d.5d7628","name":"","data":"payload","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"uint16be","name":"Grid_Voltage","offset":0,"length":1,"offsetbit":0,"scale":"/ 10.0","mask":""},{"type":"int16be","name":"Grid_Current","offset":2,"length":1,"offsetbit":0,"scale":"/ 10.0","mask":""},{"type":"int16be","name":"Inverter_Power","offset":4,"length":1,"offsetbit":0,"scale":"1","mask":""},{"type":"uint16be","name":"PV1_Voltage","offset":6,"length":1,"offsetbit":0,"scale":"/ 10.0","mask":""},{"type":"uint16be","name":"PV2_Voltage","offset":8,"length":1,"offsetbit":0,"scale":"/ 10.0","mask":""},{"type":"uint16be","name":"PV3_Voltage","offset":10,"length":1,"offsetbit":0,"scale":"/ 10.0","mask":""},{"type":"uint16be","name":"PV4_Voltage","offset":12,"length":1,"offsetbit":0,"scale":"/ 10.0","mask":""},{"type":"uint16be","name":"Grid_Frequency","offset":14,"length":1,"offsetbit":0,"scale":"/ 10.0","mask":""},{"type":"int16be","name":"Battery_Voltage","offset":40,"length":1,"offsetbit":0,"scale":"/ 100.0","mask":""},{"type":"int16be","name":"Battery_Current","offset":42,"length":1,"offsetbit":0,"scale":"/ 100.0","mask":""},{"type":"int16be","name":"Battery_Power","offset":44,"length":1,"offsetbit":0,"scale":"1","mask":""}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","resultType":"keyvalue","resultTypeType":"output","multipleResult":false,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":1060,"y":200,"wires":[["34d018b4.528008"]]},{"id":"ac96e26c.05cc4","type":"debug","z":"f77cda4d.5d7628","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1230,"y":140,"wires":[]},{"id":"34d018b4.528008","type":"debug","z":"f77cda4d.5d7628","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1230,"y":200,"wires":[]}]

that reminds me of my Modbus beginnings :grinning:
At some point I stopped and got it right with Steve-Mcl Buffer Parser, it took a while for me to drop the penny :wink: but after it I was (am) very satisfied.

1 Like

Excellent. It works. I'm trying to understand offset. Every register have 2 bytes length message? How I can determine how length is?

Your input data is a 16bit integer array so every value is 2 bytes long.

The length is property relates to how many of the specified type you want to create from your input data. The built in help describes it better.

16 bit integer array is not the same as 16 bit integer registry value? Because some registry values are 32 bit.

The modbus node gives you back an array reply for the range of modbus registers you requested.
Its up to you to make calculations later and with the use of Buffer-parser to convert them to the correct type depending on the manufacturer.

See a post from Steve regarding the calculations he made in that example as to what registers to read and what offset to use.

1 register = 2 bytes = 16bits
32bit values need 4 bytes and from the device docs you'll see that you need to read two registers to calc. them.

There are no 32but values in the data you provided - only 16bit values that you can coerce/convert into 32 bit values.

But if there were actually values > 65535 then this would not work, and that is why we suggest using other modbus nodes as they provide a byte buffer in the output.