Combine two byte items to a single item

Hi all.

I’m chewing on a problem which I’m sure several of you have solved before:

My solar energy inverter sends uptime in two separate modbus channels (Things), one being the Hi-Value the other being the Lo-Value each being an uint16 (unsinged integer 16), the values come as 0.5second units.

Big question now is how to best combine the two values into a single value representing uptime in seconds.

Here’s what I did so far (but I’m sure there’s a better way):

  1. receive both values (HI and LO) in 2 Things with a transformation to convert 0.5s to seconds
  2. Have three items, two to link to the two things (one for HI, one for LO value), the third to hold the actual uptime
  3. have rule to calculate the actual uptime value based on hi and lo value items

Things file:

Bridge modbus:serial:SPH10000 "Growatt SPH10000" [ port="/dev/ttyUSB0", id=1, baud=9600, stopBits="1.0", parity="none", dataBits=8, encoding="rtu", timeBetweenTransactionsMillis=1000 ] 
{  
Bridge poller ProduktionsDaten2 "Produktions-Daten" [start=57, length=2, type="input", refresh=60000, maxTries=2]  //start=608, length=1, refresh=12000, type="holding", maxTries=2 ] 
  {
    Thing data workTimeTotalHi    "Betriebszeit Hi"    [readStart="57",  readValueType="uint16", readTransform="JS:divide_2.js"] //kommt als 0.5sec
    Thing data workTimeTotalLo    "Betriebszeit Lo"    [readStart="58",  readValueType="uint16", readTransform="JS:divide_2.js"] //kommt als 0.5sec
  } 

JS-Transformation divide_2.js:

(function(s){
    s = s / 2; 
    return s;
})(input)

Items:

Number    growattSphWorkTimeTotalHi    "Betriebsdauer Hi" 
   {channel="modbus:data:SPH10000:ProduktionsDaten2:workTimeTotalHi:number"}
Number    growattSphWorkTimeTotalLo    "Betriebsdauer Lo" 
   {channel="modbus:data:SPH10000:ProduktionsDaten2:workTimeTotalLo:number"}
Number:Time    growattSphWorkTimeTotal    "Betriebsdauer"

The rule:

rule CalcRuntime
when
	Item growattSphWorkTimeTotalHi changed
	or
	Item growattSphWorkTimeTotalLo changed 
then
	growattSphWorkTimeTotal.postUpdate(growattSphWorkTimeTotalHi.state as Number * 65536 + growattSphWorkTimeTotalLo.state as Number)
end

Does anybody have a recommendation as to how to improve and/or simplify this task (e.g. eliminating the necessity for a rule)?

There are many more channels being sent as HI and LO in two separate channels, since I don’t want to create a mass of rules to combine HI and LO for each measurement being sent I’m looking for a solution which can easily be applied to all of those (and I want to avoid a ton of rules for all these).

Looking forward hearing your thoughts!

Kind regards,
Ralph…

Hi there.. Like for everything else, I think the answer is beer :slight_smile: , or more specifically found in this comment relating to a brewing monitoring device, where it describes using transformation to calculate something which is a derived from 2 different items:

I use this approach for the same brewing device, and it works great, and at first glance, is looks like you may be able to implement a similar approach for your situation (Have a transformation on one of the items which calculates the result from both??)

No you are combining 2 different items in a rule I don’t think transformation can do them both channels at the same time.

One way could be to use different transformations for the items: leave the LO as-is, but add the multiplication by 65536 to the transformation for the HI item. Then you can use a group item with aggregation function SUM that has both as members. This would remove the need for a rule, but adds some complexity by having separate transformations, so it’s basically just a trade-off.

That is a very creative approach - thanks a lot, I’ll go with that…

Giving out one more alternative:

Since the registers are one after each other, you can have only one data thing, with uint32 readValueType, and use transformation to do the manipulation you want to do.

This makes the item update “atomic”, and would not have wrong state at any point in time. With the channels and combining two items, you always have a risk of combining old state and new state (items are not updated at the same time)

That seems even more elegant although I’m afraid I’m lacking the JS skills to do the necessary math (like splitting the single uint32 value into two uint16 values in the JS transformation).

Do you have a hint for me as to how to do the split in JS?

Thanks a lot!

guess something like this should work?

(function(val) {

  //get loValue:
  loVal = val & 0xffff; // first 16 bits

  /get hiValue:
  hiVal = (val >> 16) & 0xffff; // last 16 bits

  //and now do some additional math with loVal and hiVal (simple part)

})(input)
1 Like

Sure… I am not quite fluent with javascript bit operations but the below ought to work

Unsigned right shift Unsigned right shift (>>>) - JavaScript | MDN

The code assumes that uint32input is 32 bit unsigned number (eg uint32 channel from modbus). If it is larger number, one should be able to crop low 32 bits with trick (let uint32input = largeNumber >>> 0, see What is the JavaScript >>> operator and how do you use it? - Stack Overflow)

// TODO handle undef uint32input
let low16BitsAsUnsignedNumber = uint32input & 65535 // mask low 16 bits. 65535 is 0xFFFF
let high16BitsAsUnsignedNumber = uint32input >>> 16 

EDIT: ah I see that javascript also supports hex format, wonder why my googling did not show that :slight_smile:

1 Like

It depends on the byte order, but if both are MSB first and the HI register is just before the LO register, no transformation should be needed I think?