Lights Control Hardware (PLC)

Alright, let’s have a go. One step at a time -get the reading working first.

If I understand right, you need to read the DO register 40303 for light circuit states, and we’ll eventually want to write Vlag at 40305, but we’ll need to read too for binding bitwise operations.
The 40xxx is an odd Modbus convention that tells us its holding register type.
Looking at your screenshots of your QMod utility, the Adam names register 40305 at protocol address 304 (hex 0130) so we need to be aware of the “-1 address” issue.

I advise naming Things and channels relating to the Modbus slave device e.g. DO01 and leaving Items to have the meaningful names like CowshedLights.

So, based on your original things and assuming you want to use a xxx.things file (as I would myself)

   // ADAM device 1
Bridge modbus:tcp:wohnhaus01 [ host="192.168.0.152", port=502, id=1 ] {
    
         // poll regs DI,DO,GCL
    Bridge poller wh01 [ start=300, length=6, refresh=500, type="holding" ]
    {
            // read DO as bits
        Thing data do01 [  readStart="302.0", readValueType="uint16" ]
        Thing data do02 [  readStart="302.1", readValueType="uint16" ]
        ... etc
        Thing data do16 [  readStart="302.15", readValueType="uint16" ]

            // read Vlag as register, we will write bits using profiles
        Thing data vlags [  readStart="304", readValueType="uint16", writeStart="304", writeValueType="uint16", writeType="holding" ]
    } // end of poller
} // end of device

Will you be defining Items using files, or with the UI? UI probably advisable for OH3, but I will give example in old xxx.items file mode and you should be able to see how to create it in UI
Names of your choice.

   // Item representing a lighting zone
Switch Cowshed "Cowshed lights" {channel="modbus:data:wohnhaus01:wh01:do01:switch"}
   // each zone partnered with a simulated pushbutton
Switch Cowshed_pulse "Cowshed push" { channel="modbus:data:wohnhaus01:wh01:vlags:switch" [ profile="modbus:bit", bit-index="0" ], autoupdate="false", expire="1s,command=OFF" }
   // Item representing next lighting zone
Switch Yard "Yard lights" {channel="modbus:data:wohnhaus01:wh01:do02:switch"}
   // partner pushbutton
Switch Yard_pulse "Yard push" { channel="modbus:data:wohnhaus01:wh01:vlags:switch" [ profile="modbus:bit", bit-index="1" ], autoupdate="false", expire="1s,command=OFF" }
... etc

Note that all the pulse Items are linked to the same data Thing, but using the brand new bit profile feature to select write bit. I hope the binding version enabling this feature is in the OH snapshot!

The Item names are deliberately related in zone-pulse pairs, because we’ll make use of that in rules later.

I’ve suggested disabling autoupdate on the pulse Items, so that we manage the Item state separately from any commands in rules.
Autoupdate can stay by default on the light zone Items for a quickUI response.

The 1-second expire will “un-push” the simulated pushbutton when we begin to use it. I’m pretty confident this will work satisfactorily without the short mS pulse, but we can revise later if needed (without using expire)

Try all this, see if it configures error-free, and see if you can monitor the actual lights. (there shouldn’t be much to see in pulse yet).
We’ll add rules for control when confident that we can read.

Thank you so much rossko57!

I´ll try it later (i can´t reach openHab at work) and give you a detailed feedback :slight_smile :slight_smile:

Hi rossko57,

ok here the result of testing…

I´ve modified the xxx.thing to the register of the digital outputs

   // ADAM 6052 Wohnhaus01
Bridge modbus:tcp:wohnhaus01 [ host="192.168.0.152", port=502, id=1 ] {
    
         // poll reg Do
   Bridge poller Thing_Do [ start=0016, length=8, refresh=1000, type="coil" ] {
       //Flutlicht Innenhof
       Thing data Thing_Do00 [ readStart="0016", readValueType="bit", writeStart="0016", writeValueType="bit", writeType="coil"]
       //Flutlicht Pferde
       Thing data Thing_Do01 [ readStart="0017", readValueType="bit", writeStart="0017", writeValueType="bit", writeType="coil"]
       //Ambientebeleuchtung
       Thing data Thing_Do02 [ readStart="0018", readValueType="bit", writeStart="0018", writeValueType="bit", writeType="coil"]
       //Nicht in Gebrauch
       //Thing data Thing_Do03 [ readStart="0019", readValueType="bit", writeStart="0019", writeValueType="bit", writeType="coil"]
       //Nicht in Gebrauch
       //Thing data Thing_Do04 [ readStart="0020", readValueType="bit", writeStart="0020", writeValueType="bit", writeType="coil"]
       //Nicht in Gebrauch
       //Thing data Thing_Do05 [ readStart="0021", readValueType="bit", writeStart="0021", writeValueType="bit", writeType="coil"]
       //Nicht in Gebrauch
       //Thing data Thing_Do06 [ readStart="0022", readValueType="bit", writeStart="0022", writeValueType="bit", writeType="coil"]
       //Nicht in Gebrauch
       //Thing data Thing_Do07 [ readStart="0023", readValueType="bit", writeStart="0023", writeValueType="bit", writeType="coil"]
    } // end of poller Do

         // poll reg Vlag
   Bridge poller wh01 [ start=300, length=6, refresh=500, type="holding" ] {
       // read Vlag as register, we will write bits using profiles
       Thing data vlags [  readStart="304", readValueType="uint16", writeStart="304", writeValueType="uint16", writeType="holding" ]
    } // end of poller Vlag

} // end of device

I named the swiches in items by correct names in xxx.items:

   // Item representing a lighting zone
Switch FlutInnenh "Flutlicht Innenhof" {channel="modbus:data:wohnhaus01:Thing_Do:Thing_Do00:switch"}
   // each zone partnered with a simulated pushbutton
Switch FlutInnenh_pulse "FlutInnenh push" { channel="modbus:data:wohnhaus01:wh01:vlags:switch" [ profile="modbus:bit", bit-index="0" ], autoupdate="false", expire="1s,command=OFF" }
   // Item representing next lighting zone
Switch FlutPferd "Flutlicht Pferde" {channel="modbus:data:wohnhaus01:Thing_Do:Thing_Do01:switch"}
   // partner pushbutton
Switch FlutPferd_pulse "FlutPferd push" { channel="modbus:data:wohnhaus01:wh01:vlags:switch" [ profile="modbus:bit", bit-index="1" ], autoupdate="false", expire="1s,command=OFF" }
   // Item representing next lighting zone
Switch Ambiente "Ambiente Beleuchtung" {channel="modbus:data:wohnhaus01:Thing_Do:Thing_Do02:switch"}
   // partner pushbutton
Switch Ambiente_pulse "Ambiente push" { channel="modbus:data:wohnhaus01:wh01:vlags:switch" [ profile="modbus:bit", bit-index="2" ], autoupdate="false", expire="1s,command=OFF" }

After data migration i added a “value as a Number” item to check if the read poll of register 305 is working.

The result is:
normel switches are showing the live status of light on/off
the pulse switches just showing the value null
the value as a number shows the correct value in dezimal ( i used the polling tool to figure it out

Okay, that’s what we hoped for, good start. :smiley:

Upon discussing with Sami how the new bit-write mechanism in the binding works, I don’t think it’s going to work well here. It seems it is not going to work well where you might write 2 or 3 “at the same time” (very quickly consecutively really) as you might well do with lighting zones. The safer course is to manage our own register cache and bit-write via rules.

It’s a different approach, requires different settings,but the same Item light-pulse paired structure. I’ll have a think and come back on this.

EDIT -

Aha, I did not realise your DO outputs were available as coil-type registers. Doesn’t matter here, so long as you know what is going on. These are just “mirrors” of outputs on other devices, right? So we cannot use for control, only monitoring.

I assume that the Vlag bits we want to write are only available packed into a holding register, though?

Hi rossko57,

Yes that was pretty simple with your help :wink: but i if i had to to do that for my own i´m not able to.

Uhh that sounds strange and i hope you will continue to support me please.

That´s correct you can just see the status…
You can see in the picture that the Do0 from the adam box is just a part from the construction…
…in this case 3 x Do for the lights
AdamBox2Rule2

Yes, those Vlag´s are only available in the holding register… :frowning:

thank you very much rossko57 that you invest your time to helping me !!!

Alright, had a think and a play.

Change of method means change of infrastructure.

Revised Things

         // poll reg Do
   Bridge poller Thing_Do [ start=0016, length=8, refresh=500, type="coil" ] {
       //Flutlicht Innenhof
       Thing data Thing_Do00 [ readStart="0016", readValueType="bit"]
etc.

Mostly okay as it was, I think you can afford to poll more often.
but
Do change these DO to read-only, we want to ignore commands here.

         // poll reg Vlag
   Bridge poller wh01 [ start=300, length=6, refresh=0, type="holding" ] {
       // read/write Vlag as whole register, we will write bits from rule
       Thing data vlags [  writeStart="304", writeValueType="uint16", writeType="holding" ]
    } // end of poller Vlag

In contrast, we need not poll at all here - unless you want to look at any of the other holding registers.
Nothing else is changing vlag but us, so we need never read it back.
Change the data Thing to write-only.

Revised Items

We need to introduce Group Items, which allows us to easily use one rule to deal with a group of similar Items. All light zones in one group, all pulse-pushbuttons in another

We’re going to use one Item to keep an image of the 16-bit Vlag register. This gets updated by a rule as bits are written.

A big change in the naming pattern. The methods used here are the same as the “Modbus bitwise” posting linked to earlier. But the rule there is just a demo, not really suitable for expanding to 16 Items with the switch/case, and it would require hardcoding Item names into the rule.
We can avoid that by hardcoding the bit number used by this button into the name of the pulse-pushbutton Item. Much easier to add/amend Items.

   // modbus lighting items
Group gAdamLights "for light zones"
Group gVlags "for pulses"
Number VlagReg "for vlag register image" {channel="modbus:data:wohnhaus01:wh01:vlags:number"}

   // Item representing a lighting zone
Switch FlutInnenh "Flutlicht Innenhof" (gAdamLights) {channel="modbus:data:wohnhaus01:Thing_Do:Thing_Do00:switch", autoupdate="false"}
   // each zone partnered with a simulated pushbutton
Switch FlutInnenh_pulse_00 "FlutInnenh push" (gVlags) {autoupdate="false", expire="1s,command=OFF"}
Switch FlutPferd "Flutlicht Pferde" (gAdamLights) {channel="modbus:data:wohnhaus01:Thing_Do:Thing_Do01:switch", autoupdate="false"}
Switch FlutPferd_pulse_01 "FlutPferd push" (gVlags) {autoupdate="false", expire="1s,command=OFF"}
etc.

So, Light Items are linked to read-only modbus channels to monitor light states…
Note that autoupdate is disabled - we don’t want commands interfering with Item state, because our rule needs accurate state.
These are the Items to put on your UI with on/off switches, and to command from other rules like motion activated lighting.

Simulated pushbutton Items are not linked to anything, all managed by rule.
We don’t care about Item state here, it is all about commands. So we can disable autoupdate to reduce workload.
Expire is used to automatically “un-push” a button after 1 second. I think that will be fast enough for you (it’s as fast as expire goes). If it isn’t, we can change something else later.

Rules

Everything is done by two rules.
I’ve written in file-based DSL, it should be possible to convert to other languages and/or UI.

One rule manages the pushing of the buttons, writing bits to vlag register while preserving state of all other bits.

The other rule manages if and when to push a button at all, to toggle a light on or off.

import java.math.BigInteger // we need this for set/clearBit()

rule "simulate vlag pushbutton"
    // this rule pushes and releases buttons
when
    Member of gVlags received command
then
    var register = BigInteger.valueOf(0)  // we need BigInteger type to use setBit
    if ( VlagReg.state != NULL && VlagReg.state != UNDEF) { // avoid using invalid register image
        register = (VlagReg.state as DecimalType).toBigDecimal.toBigInteger // use existing image
    }
        // relies on Item name like FlutPferd_pulse_01
    val bitnum = Integer.parseInt(triggeringItem.name.split("_").get(2))  // get the 01 part
    if (receivedCommand == ON) {
        register = register.setBit(bitnum)
    } else if (receivedCommand == OFF) { // by testing both, we will ignore REFRESH commands etc.
        register = register.clearBit(bitnum)
    }
        // at last, write image to Modbus
    VlagReg.sendCommand(register)
end

rule "control pushbutton Items"
    // this rule converts on/off commands to buttons
when
    Member of gAdamLights received command
then
        // check if action needed first
    if ((receivedCommand ==ON && triggeringItem.state != ON) ||
        (receivedCommand == OFF && triggeringItem.state != OFF)) {
        val template = triggeringItem.name + "_pulse_"
            // find partner pushbutton in group, but we don't know suffix like _01
        val button = gVlags.members.findFirst[i | i.name.contains(template)]
        if (button === null) {
            logWarn("ERROR", "config missing " + template)
        } else {
            button.sendCommand(ON) // push the button for change
            // relying on expire to un-push later, but we could use timer here
        }
    } // else no change needed
end
1 Like

Hey rossko57,

this is amazing!!!

your config works perfect, oh thank you so much i never were able to do this for my own.

i have changed the length from 6 to 1

thank you so much

OOPS - just noticed an error. The image register Item VlagReg should not have autoupdate disabled. This error would not impact most usage,but I’ve amended the previous post.

Doesn’t matter much with zero polling :wink:

We can enhance this set up to detect when someone manually operates the lights i.e.lights change but openHAB didn’t request that.

I use this for presence detection, alongside PIRs, door openings etc. - “Someone is there at the wall switch”.
I also use it to modify automated lighting behaviour e.g.if someone chooses to manually turn the lights off, we don’t want to immediately turn them back on from a motion detection…

Any interest in detection? (What you do with it is up to you!)

Oh yes that sounds very intresting!
I have doorcontact´s at the most front doors so I would be able to switch the Outdoor Light automatically :slight_smile:

Alright, this is a mash together of the Adam code and something I use myself with pulse relays.

The problem is that a transient openHAB command causes a state change - with a delay. Monitoring a state change does not tell us who did it, unless we “remember” that OH recently sent a command.

In the Adam setup, the write-only simulated pushbutton Items are working just by commands. The Item state is unused, so we are going to hijack that to use as our “command rememberer”.

So a small change to all pushbutton Items -

Switch FlutInnenh_pulse_00 "FlutInnenh push" (gVlags) {autoupdate="false"}

Stopping using expire here at all. (In theory we could use it to cancel the remember with state=OFF, but that doesn’t work properly in OH2 with preceding command OFF. Maybe it would in OH3,but timers work)

The rule that handles light Items changes, timers added -
One to un-push the button.
A longer one to allow for a response from commanded light.

rule "control pushbutton Items"
    // this rule converts on/off commands to buttons
when
    Member of gAdamLights received command
then
        // check if action needed first
    if((receivedCommand ==ON && triggeringItem.state != ON) ||
        (receivedCommand == OFF && triggeringItem.state != OFF)) {
        val template = triggeringItem.name + "_pulse_"
            // find partner pushbutton in group, but we don't know suffix like _01
        val button = gVlags.members.findFirst[i | i.name.contains(template)]
        if (button === null) {
            logWarn("ERROR", "config missing " + template)
        } else {
            button.sendCommand(ON) // push the button for change
            button.postUpdate(ON) // remember command
                // set up to un-push after 300mS
            createTimer(now.plusNanos(300000)) [ |
                button.sendCommand(OFF) // un-push button
                   // leave state ON and spawn new timer
                createTimer(now.plusSeconds(2)) [ |
                    button.postUpdate(OFF) // forget the command
               ]
            ]
        }
    } // else no change needed
end

The button bits-to-register rule stays unchanged.

Add a new rule to detect light changes

rule "detect light switching"
when
    Member of gAdamLights changed from ON to OFF or
    Member of gAdamLights changed from OFF to ON
        // ignore NULL or UNDEF changes
then
    val template = triggeringItem.name + "_pulse_"
    val button = gVlags.members.findFirst[i | i.name.contains(template)]
    if (button.state != ON) {  // it was not openHAB
        // then it was a manual change
        // someone is stood at the wallswitch!
        logInfo("test", "Who is there?")
        // code whatever you want, like updating a presence Item
    }
end

Hi rossko57,

thank you again for those nice settings!

I added both of your rules to the rules folder and changed the xxx.items with the push switches…

Unfortunately i´m not able to switch the light´s by openHab anymore but the status of the lights is shown correctly when i use the hardware buttons…

Maybe i did a mistake and i will check all configs again!

There is a timing error, I got micros for nanos. (I tested in OH2 where this is different)

Fixed for 300mS -

                // set up to un-push after 300mS
          createTimer(now.plusNanos(300000000)) [ |
                button.sendCommand(OFF) // un-push button

Okay, i have changed it to 300000000 nanoseconds but it still doesn´t work.

I noticed that you have done an import in the first rule that you created…

Do i need that again in my “new” first rule, maybe?

The import goes just once at the beginning of the file.
It’s needed to use the x.setBit() method, if it went wrong there would be errors in your openhab.log. You’ve looked in there?
What does happen when you command a light? Your events.log will show us some idea of how far it gets.

In the log there is named a error in the first rule (Taster01.rules). It is missing EOF at ‘Die’ what ever that means?

Maybe you are able to understand that?openhab.log (46.3 KB)

It was ‘missing etc.’ previously. Since that time, the same rules file has loaded successfully, due to edits I guess. ‘Missing EOF’ is generally about mismatched brackets, missing rule end statements, or suchlike.

Most likely there is a small error crept in during copy-paste somewhere. An editor like VSCode helps to find mistakes like that,otherwise just look carefully.
No-obvious errors like quotemarks placement or type are hard to see.

Hi rossko57,

thats a good hint i will try that and give a feedback!
thank you again!

Ok i created a new xxx.rules and opened it in notepad++

It looks okay for me ?!

but the log saying the same:

2021-01-30 12:17:58.981 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Wohnhaus01.items'
2021-01-30 12:20:59.713 [WARN ] [ab.core.service.AbstractWatchService] - Error while opening file during update: C:\Program Files\OpenHab\conf\rules\Taster01.rules
2021-01-30 12:21:01.261 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Taster01.rules'
2021-01-30 12:21:01.885 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'Taster01.rules' has errors, therefore ignoring it: [28,1]: missing EOF at 'Die'

2021-01-30 12:21:02.917 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Taster01.rules'
2021-01-30 12:21:03.043 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'Taster01.rules' has errors, therefore ignoring it: [28,1]: missing EOF at 'Die'

2021-01-30 12:21:51.354 [WARN ] [ab.core.service.AbstractWatchService] - Error while opening file during update: C:\Program Files\OpenHab\conf\rules\Taster02.rules
2021-01-30 12:21:52.823 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Taster02.rules'
2021-01-30 12:21:55.105 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Taster02.rules'
2021-01-30 12:30:26.448 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Wohnhaus01.items'
2021-01-30 12:32:46.340 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Taster02-1' failed: cannot invoke method public abstract org.openhab.core.types.State org.openhab.core.items.Item.getState() on null in Taster02
2021-01-30 12:32:47.178 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Taster02-1' failed: cannot invoke method public abstract org.openhab.core.types.State org.openhab.core.items.Item.getState() on null in Taster02
2021-01-30 12:32:48.699 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Taster02-1' failed: cannot invoke method public abstract org.openhab.core.types.State org.openhab.core.items.Item.getState() on null in Taster02
2021-01-30 12:34:22.885 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Taster02-1' failed: cannot invoke method public abstract org.openhab.core.types.State org.openhab.core.items.Item.getState() on null in Taster02
2021-01-30 12:34:24.448 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Taster02-1' failed: cannot invoke method public abstract org.openhab.core.types.State org.openhab.core.items.Item.getState() on null in Taster02
2021-01-30 12:34:27.042 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Taster02-1' failed: cannot invoke method public abstract org.openhab.core.types.State org.openhab.core.items.Item.getState() on null in Taster02
2021-01-30 13:15:27.700 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Taster01.rules'
2021-01-30 13:15:27.854 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'Taster01.rules' has errors, therefore ignoring it: [28,1]: missing EOF at 'Die'

2021-01-30 13:19:54.166 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Taster01.rules'
2021-01-30 13:19:54.260 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'Taster01.rules' has errors, therefore ignoring it: [30,1]: missing EOF at 'Die'

2021-01-30 13:22:59.744 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Wohnhaus01.items'
2021-01-30 13:24:35.328 [WARN ] [ab.core.service.AbstractWatchService] - Error while opening file during update: C:\Program Files\OpenHab\conf\items\Wohnhaus01.items
2021-01-30 13:24:36.354 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Wohnhaus01.items'
2021-01-30 13:25:53.058 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Wohnhaus01.items'
2021-01-30 13:28:24.137 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Taster01.rules'
2021-01-30 13:28:24.231 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'Taster01.rules' has errors, therefore ignoring it: [28,1]: missing EOF at 'Die'

2021-01-30 13:29:39.323 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Taster01.rules'
2021-01-30 17:18:51.512 [INFO ] [org.openhab.core.model.script.test  ] - Who is there?
2021-01-30 17:18:55.465 [INFO ] [org.openhab.core.model.script.test  ] - Who is there?
2021-01-30 17:50:31.215 [INFO ] [org.openhab.core.model.script.test  ] - Who is there?
2021-01-30 17:50:32.246 [INFO ] [org.openhab.core.model.script.test  ] - Who is there?
2021-01-30 17:50:40.543 [INFO ] [org.openhab.core.model.script.test  ] - Who is there?
2021-01-30 21:17:35.512 [INFO ] [org.openhab.core.model.script.test  ] - Who is there?
2021-01-31 09:41:19.376 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Taster01.rules'
2021-01-31 09:41:20.229 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'Taster01.rules', using it anyway:
The import 'java.math.BigInteger' is never used.
2021-01-31 12:07:06.276 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'Taster01.rules'
2021-01-31 12:07:06.931 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'Taster01.rules', using it anyway:
The import 'java.math.BigInteger' is never used.

I cannot figure out what is wrong :frowning:

Without knowing the name or time there is no way for us to know what logs have anything to do with this one.

Please note that every rule must have a system-wide unique “name”. Copying rules from one file to another is a good quick way to narrow down - if you avoid that trap.

Please note that screenshots cannot be pasted into other people’s editors to check for weird characters etc. If your editor allows “make whitespace visible” or similar, worth a try.

Oh ok, the two rules which you have posted are named in Taster01.rules and Taster02.rules.
I just wanted to show that in the Editor are no fault are shown…

here are the two rule which i have added:

Taster01.rules.txt (1.2 KB) Taster02.rules.txt (573 Bytes)

Ok will check out if there is such a option