[Modbus] Testing needed: scaling values to Quantity-aware Numbers and writing individual bits

Hi all,

asking for community testing effort with some new functionality of the modbus binding.

cc @rossko57

I’ve developed two new features with the OH3 modbus binding:

1) QuantityType-aware numbers

Modbus binding currently supports only plain Number channels. This makes it incompatible with excellent UoM system of openHAB.
Typically decimal numbers are scaled to integers with Modbus. Having support for scaling the integers and attaching the unit with a convenience profile would streamline configuration UoM aware numbers.

This allows you scale bare Number like 58 (from Modbus) to be converted to QuantityType 5.8 Celsius (item). Also command works when writing data to Modbus.

See e.g. updated Scaling Example

Number:Temperature TemperatureItem "Temperature [%.1f °C]" { channel="modbus:data:localhostTCP3:holdingPoller:temperatureDeciCelsius:number"[ profile="modbus:gainOffset", gain="0.1 °C", pre-gain-offset="0" ] }

2) Commanding individual bits of modbus register

Command individual bits of modbus registers is currently hard, and additional rules are required. See e.g. @Rossko57 's post in the forum Bitwise operations in a modbus register
We can use the new profile to command individual bits.

See new example of commanding of individual bits

Bridge modbus:tcp:localhostTCP3 [ host="127.0.0.1", port=502 ] {
    Bridge poller holdingPoller [ start=5, length=1, refresh=5000, type="holding" ] {
        Thing data register5 [ readStart="5.1", readValueType="bit", writeStart="5.1", writeValueType="bit", writeType="holding" ]
        Thing data register5Bit1 [ readStart="5.1", readValueType="bit" ]
    }
}

Documentation: openhab-addons/README.md at b09d7515d30330c59565da47749c54c383461ef2 · openhab/openhab-addons · GitHub

Download jars from [modbus] Gain-offset profile (QuantityType support) and writing of individual bits of holding registers by ssalonen · Pull Request #9980 · openhab/openhab-addons · GitHub
Should be compatible with OH 3.0 release.

1 Like

(sorry Sami, I have no viable OH3 system to play with)

1 Like

Bit of a late thought, but writeable modbus bit values are most likely to be wanted to be used with Switch type openHAB Items.

That would in turn raise the spectre of whether “0” means ON or OFF :crazy_face:
Could have a selection by
profile="modbus:bitinvert" ,,,
or maybe
profile="modbus:bit", bit-index="1-invert"

1 Like

Good idea! I decided to introduce one more optional parameter, inverted, to control this behaviour: [modbus] Introducing Modbus bit and gainOffset profiles by ssalonen · Pull Request #9469 · openhab/openhab-addons · GitHub

I also tested that the profile works as expected with Contact, Switch and Number types.

1 Like

Excellent, I remembered afterwards that they recently changed the profile environment so we could mix and match Item types.

Question: what happens if we use modbus:bit profile with a write-only uint16 data Thing?
Is the “register image cache” still maintained according to command/write activity, and so will affect the data written to modbus e.g we might write a pattern like 00100011 depending on history?

I’m looking at a PLC use case, where it might work better if the “register image cache” was always zero, e.g. we only wrote a single bit 000100 style. I’m still working out whether it would really be better or not!
I don’t think that’s very common, and can easily be achieved if needed by other means, but it led me to think about the cache workings.

A device having a write-only register that we just cannot read may be more common, some PLCs are weird like that, so it would be good to understand.
I’m sure maintaining the cache image would be the correct way to do it, in general.

Related - does the profile update the state of the linked Item according to the cache? I think it should not - autoupdate does that job, if wanted. Only real read poll data should get passed to Item state?

Currently commands are ignored if there is no value read

So having write-only items with this bit profile is not supported atm. I think it’s ok, perhaps a rare case in the end?

The commands do update the internal cache as well. Actually, this does not matter since the same bit is always overwritten with commands, and the cache is not shared with other instances of the profile (other items). Real reads the clear the whole cache and extract the bit from the 16bits

Write only items with always-zero-cache could be implemented with simple MAP transformations, wouldn’t they? ON mapping to 2^n and OFF mapping to 0

Related - does the profile update the state of the linked Item according to the cache? I think it should not - autoupdate does that job, if wanted. Only real read poll data should get passed to Item state?

Profile only 1) passes state updates from binding, towards the item; 2) passes commands from item towards the binding. Indeed it does not update item state based on commands received by the item.

So your understanding is correct, only real poll data gets passed to item state.

In general, I am not sure if auto-update is working with profiles in general… I have not tried.

Yeah it is pretty neat. Behind the scenes OH converts DecimalType to Open Closed Type for example

“Must be read/write” solves the question :smiley:

Yes, I would go along with that. The rare case can still be met with other means, like a MAP.

Yes,it does, because it operates on the Item.

1 Like

Thanks for the new version of the binding.

Do you also have example of using gainOffset from Main UI / yaml?

You can configure the profile using the Main UI yes. Logic of the profile is explained here: openhab-addons/README.md at bf9ba983ca4f7604087623ad7f856ed993849979 · openhab/openhab-addons · GitHub

1 Like

Did some quick testing. Looks nice, thanks for your work.

What I saw is that there is no field to define the bit index number in mainUI?

With Gain-Offset Correction there two fields for gain and pre-offset, which is good…

I hopefully have time to do some more testing at the weekend.

It strikes me that this “scale and add units” profile is generally useful throughout openHAB. A great many bindings still supply number-only channels because they cannot “know” much about the target device e.g. an HTTP fetch. While the user often knows exactly what he’s getting, and fiddles about with state presentations or scripted transforms to get it looking right.

I think it should be promoted to be one of the default system profiles, not a part of Modbus particularly. Well done Sami :smiley:

2 Likes

Yes correct on the bit profile. The precompiled jars are slightly outdated, UI part was fixed later.

There are certain problems with the bit profile due to constraints imposed by the openHAB profile mechanism. Actually I will probably remove the bit profile in the end in its current form.

Thank you @rossko57 . Indeed it is very general in that sense and it crossed my mind as well. It can be also used with gain of one but simply “attaching” the unit. This would be convenient with temperature values for example.

Only part that is not general there is the limitation that it only works with bindings that output raw values. I initially tried to cover the fully general use case as well, accepting quantitype as input, (with the thinking of bringing it to core/outside modbus) but there are quite many surprising edges and troubles with the unit conversions. In the end I think it is much more rare use case but could be used in some cases, e.g. to fix units.

That’s reasonable.
If the binding is already sending Quantity type channel that is because it thinks has it all correct already, it needs no further massaging.

I have now reimplemented the way one can command individual bits of a holding register: now it is part of a data thing, not a profile.

This has a benefit that many commands play nice together as they all share the same internal cache. It is possible to fire many commands in succession each referring to different bits of the same register.

Links in the first post have been updated, precompiled jar is behind the link. The gainOffset profile should work the same way as before, no major changes.

Appreciate if you can test this out.

I would like to test. I am on OH3.1.0 Bulid 2175. Followed the instructions on the github page and got this error when installing openhab-transport-serial:

openhab> feature:install openhab-transport-serial

org.apache.felix.resolver.reason.ReasonException: Unable to resolve org.openhab.binding.modbus.sunspec/3.1.0.202102020413: missing requirement [org.openhab.binding.modbus.sunspec/3.1.0.202102020413] osgi.wiring.package; filter:="(osgi.wiring.package=org.openhab.binding.modbus.discovery)"
at org.apache.felix.resolver.Candidates$MissingRequirementError.toException(Candidates.java:1343)
at org.apache.felix.resolver.ResolverImpl.doResolve(ResolverImpl.java:420)
at org.apache.felix.resolver.ResolverImpl.resolve(ResolverImpl.java:378)
at org.apache.felix.resolver.ResolverImpl.resolve(ResolverImpl.java:332)
at org.apache.karaf.features.internal.region.SubsystemResolver.resolve(SubsystemResolver.java:257)
at org.apache.karaf.features.internal.service.Deployer.deploy(Deployer.java:393)
at org.apache.karaf.features.internal.service.FeaturesServiceImpl.doProvision(FeaturesServiceImpl.java:1062)
at org.apache.karaf.features.internal.service.FeaturesServiceImpl.lambda$doProvisionInThread$13(FeaturesServiceImpl.java:998)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
    Error executing command: Unable to resolve org.openhab.binding.modbus.sunspec/3.1.0.202102020413: missing requirement [org.openhab.binding.modbus.sunspec/3.1.0.202102020413] osgi.wiring.package; filter:="(osgi.wiring.package=org.openhab.binding.modbus.discovery)"

I have installed SunSpec Bundle in this version:
230 x Active x 80 x 3.1.0.202101310352 x openHAB Add-ons :: Bundles :: SunSpec Bundle

1 Like

Really appreciate the testing effort!

I am not sure what that error means… What if you just skip the installation of the serial?

Does the modbus bundle install and start?

Since you already have sunspec installed, you must have some version of modbus binding installed. You might need to call bundle:update Xx to make sure the jar from the addons folder is used.

1 Like

It is working. I only use Modbus TCP.

The first testing results are very positive. I was able to read and write data with following thing configuration:
Thing data EG_Bad [ readStart="0.0", readValueType="bit", writeStart="0.0", writeValueType="bit", writeType="holding" ]

The part to write at the end is new:
writeStart=“0.0”, writeValueType=“bit”, writeType=“holding”

For this part, I am now using a separate thing configuration which looks like this:
Thing data EG_Bad [ writeStart="0", writeValueType="bit", writeType="coil" ]

This I now don’t need any more… awesome! The item is only linked to one channel.
Thank you very much!

1 Like