[SOLVED] How to achieve push-button behaviour with modbus switch

Hi,

I’m running OpenHab 2 on my windows 10 machine. Java version “1.8.0_221”. I’m connecting to an Allen Bradley PLC with a modbus binding.

My goal is to let the light switch behave as a push button. Based on the state of the light (which I can read from discreteInput[0]) the switch is either ON or OFF. Toggling the button should always immediately turn on or off my light.

Expected behaviour:

  • State living_state == ON => Switch is ON => Toggle switch => Lights go off => State living_state == OFF
  • State living_state == OFF => Switch is OFF => Toggle switch => Lights go on => State living_state == ON

Actual behaviour:

  • Toggle switch => Lights go on
  • Toggle switch => Nothing
  • Toggle switch => Lights go off
  • Toggle switch => Nothing

I always have to toggle double. I’ve tried several things so far, but can’t get it to work. And I’m also not able to take the state of the light into account.

I’ve been able to setup my configuration based on the documentation and some posts I’ve read here:

I’ve ran out of ideas and my last resort is coming here… So if anyone can help me, that’ll be greatly appreciated. If you need more information, let me know.

.things

Bridge modbus:tcp:plc [ host="x.x.x.x", port=x, id=2 ] {
   Bridge poller coils [ start=0, length=25, refresh=1000, type="coil" ] {
      // Note the zero based indexing: first coil is index 0.
      Thing data living "Living" [ readStart="0", readValueType="bit", writeStart="0", writeValueType="bit", writeType="coil" ]
    }	

    Bridge poller discreteInputs [ start=0, length=25, type="discrete" ]{
	Thing data living_state [readStart=0, readValueType="bit"]
    }
}

.items

Switch living            "Digital Output index 0 [%d]"    { channel="modbus:tcp:plc:coils:living:switch", autoupdate="false"}
Switch living_state            "Digital Output index 0 [%d]"    { channel="modbus:tcp:plc:discreteInputs:living_state:contact"}

.sitemap

sitemap modbus_verlichting label="Verlichting"
{
    Frame {
        Switch item=living label="Living" mappings=[ON="ON"] icon="switch-off" visibility=[living_state == OFF]
        Switch item=living label="Living" mappings=[ON="OFF"] icon="switch-on" visibility=[living_state == ON]
    }
}

I think what you are saying is that you want to intercept an openHAB command that may be ON or OFF and convert it always to 1 to be written to modbus.

Use a writeTransform, a simple MAP will do it.

I edited my question a bit to clarify it a bit more. I also want to know the state of the light at all times. So the state of the switch is based on the state of the light.

Using the discrete “living_state” I know whether the light is on or off. If I send an ON command to the coil “living” then the “living_state” gets updated and the light goes on or off.

How do I get this behaviour in the switch?

Set autoupdate to false for your Item.

Take the coil write part of your data thing, use the writeTransform, put this in the same data thing as your discrete read.

Discard coil poller and data thing.

Link your combined data thing to your switch Item.

Discard the other switch Item.

The switch Item state will reflect the PLC status. When you issue an OH command, the state will be unaffected and a 1 sent to PLC. When the PLC changes its status, the next read poll will pick it up. If the PLC does something independently, the next read poll will pick it up.

Your light switch is it a push button or a switch?

Physically it’s a push button.

When I push it, a value is incremented within the PLC and that value decides if the light is on or off. 0 is off, 1 is on, 2 gets reset to 0.

That is why I need to be able to send an “ON” or “1” to the PLC each time.

This is the behaviour on the PLC itself:

  1. Starting point ==> light off
    image

  2. Turn the switch on ==> light on
    image

  3. Turn the switch off ==> light on

image

  1. Turn the switch on ==> light off
    image

  2. Turn the switch off ==> Starting point

Described how to do what you ask.

I suspect however that you may forgotten a bit - is anything supposed to happen in betwéen sending 1’s ?

Thanks. I’m trying to implement that.

I now have:

.things

Bridge modbus:tcp:plc [ host="x.x.x.x", port=x, id=2 ] {	
    Bridge poller discreteInputs [ start=0, length=25, type="discrete" ] {
	    Thing data do_living "Living status" [ readStart="0", readValueType="bit", writeTransform="JS(switch.js)", writeStart="0", writeValueType="bit", writeType="coil"]
    }
}

.items

Switch light_living_switch            "Digital Output index 0 [%d]"    { channel="modbus:tcp:plc:discreteInputs:do_living:switch"}

switch.js

// function to invert Modbus binary states
// variable "input" contains data passed by OpenHAB binding
(function(inputData) {
    return "1" ;
})(input)

Is this correct? Or do I need to send another value from the transformation?

What are you trying tot do with this config?Do you switch your lights with puls relay’s en push buttons in your home?And do you want to automate this with openhab And a plc?

My goal with the config is to be able to control the lights from within OH and from within the physical push buttons at my home.

The PLC has been configured by my electrician and I also have an app to control everything, but I can’t modify that app myself. That is why I have decided to try and set this up with OH so that I do not need that other app anymore.

But OH still needs to be able to function alongside the push buttons at my home.

PLC is not my cup of tea, but as far as I understand it:

In my home I click on push button => A puls is sent to the PLC which connects the circuit and immediately disconnects it again => Due to the connection the counter in my relais is updated => The updated counter updates the status of my light and output.

That relais works something like this:

  • Starting point => Count 0 => Light off
  • push button => Count 1 => Light on
  • push button => Count 2 => Light off => Reset Count to 0

This way a lot of inputs can be connected and the output is always updated in the same manner.

However, within OH if you have a switch, he keeps the circuit connected. Which is not what I want. I want to have that if I toggle the switch, then a puls (so connect and disconnect) is sent. The switch is toggled “ON” and stays like that. If I toggle the switch again, then again a puls (connect and disconnect) is sent and the switch is toggled “OFF”.

Ok you have to mimic the puls from your push buttons with a rule i have the full config at home.But i am on vacation wright now i come home tomorrow evening.Then i can send you sitemap,modbus config,puls rules.

Thanks! Can’t wait to see what the answer is.

The unspoken thing with push-on, push-off buttons is that you have to release them in between. I guess this is the bit you are missing.

Need two Items so that within openHAB we can send ON OFF commands in the usual way to one, from rules or UI, but we can send different commands to the PLC via the other Item.

Switch living "my light [%s]" { channel="modbus:tcp:plc:coils:discrete00:switch", autoupdate="false"}
Switch living_pulse "Digital Output coil0 [%s]" { channel="modbus:tcp:plc:discreteInputs:coil00:switch"}

living is the Item to treat as your light, put on sitemap, command from rules etc.
It has autoupdate false so that we use only polled data from PLC to update its state.
living_pulse is just to route commands to the PLC, and here we have autoupdate enabled so that commands will change Item state

We will have two data things, separate read and write, because we want one to be read-only and ignore commands from Item

    Bridge poller discreteInputs [ start=0, length=25, type="discrete" ]{
	Thing data discrete00 [readStart=0, readValueType="bit"]
        Thing data coil00 [ writeStart="0", writeValueType="bit", writeType="coil" ]
     }

The poller only needs to cover read data things.
We don’t need to read back the coil.

Now rules to make it happen

rule "simulate push button"
when
   living received command
then
      // act if we need to change
   if (receivedCommand != living.state) {
         // push the simulated button
      living_pulse.sendCommand(ON)
   }   // else already as commanded
end

rule "simulate button release"
when
   Item living changed
then
   if (living_pulse.state == ON) {
         // release simulated button
      living_pulse.sendCommand(OFF)
   } else {
      // bonus feature, detect external intervention
      // use to signal human occupation, chnage lights delays etc.
   }
end

So we can send Item living an ON or OFF command. The rule will push the modbus “button” if needed. The PLC should do its thing and change the actual light state, that gets polled and updates Item living. That Item chnage releases the modbus “button”.
No timing or transforms needed.

Thanks Rosko,

I’ve tried it. But it doesn’t work.

.Things:

Bridge modbus:tcp:plc [ host="x.x.x.x", port=x, id=2 ] {
    Bridge poller discreteInputs [ start=0, length=25, type="discrete" ] {
		Thing data discrete00 [ readStart="0", readValueType="bit" ]
        Thing data coil00 [ writeStart="0", writeValueType="bit", writeType="coil" ]
	}
}

.Items: (I’ve updated Living_Pulse to use the correct channel. As in your example he referenced the coils poller which doesn’t exist anymore. Am I correct?)

Switch Living "my light [%s]" { channel="modbus:tcp:plc:discreteInputs:discrete00:switch"}
Switch Living_Pulse "Digital Output coil0 [%s]" { channel="modbus:tcp:plc:discreteInputs:coil00:switch", autoupdate="false"}

.Sitemap:

sitemap modbus label="Verlichting"
{
    Frame label="Test" {
        Switch item=Living icon="light"
    }
}

.Rules:

rule "Simulate push button"
when
   Item Living received command
then
      // act if we need to change
   if (receivedCommand != Living.state) {
         // push the simulated button
      Living_Pulse.sendCommand(ON)
   }   // else already as commanded
end

rule "Simulate button release"
when
   Item Living changed
then
   if (Living_Pulse.state == ON) {
         // release simulated button
      Living_Pulse.sendCommand(OFF)
   } else {
      // bonus feature, detect external intervention
      // use to signal human occupation, chnage lights delays etc.
   }
end

This is my log file when toggling the switch in “Basic UI”:

2019-08-17 09:30:30.766 [ome.event.ItemCommandEvent] - Item 'Living' received command ON
2019-08-17 09:30:30.773 [nt.ItemStatePredictedEvent] - Living predicted to become NULL
2019-08-17 09:30:30.843 [ome.event.ItemCommandEvent] - Item 'Living_Pulse' received command ON
2019-08-17 09:30:32.301 [ome.event.ItemCommandEvent] - Item 'Living' received command OFF
2019-08-17 09:30:32.302 [nt.ItemStatePredictedEvent] - Living predicted to become NULL
2019-08-17 09:30:32.305 [ome.event.ItemCommandEvent] - Item 'Living_Pulse' received command ON

Hi,

Small update. I got it working :slight_smile:

This is my config:

Things:

Bridge modbus:tcp:plc [ host="192.168.1.50", port=502, id=2 ] {
    Bridge poller coils [start=0, length=25, refresh=1000, type="coil"] {
        Thing data coil00 [ writeStart="0", writeValueType="bit", writeType="coil" ]
    }
    Bridge poller discreteInputs [ start=0, length=25, type="discrete" ] {
		Thing data discrete00 [ readStart="0", readValueType="bit" ]        
	}
}

Items:

Switch Living "my light [%s]" { channel="modbus:tcp:plc:discreteInputs:discrete00:switch", autoupdate="false"}
Switch Living_Pulse "Digital Output coil0 [%s]" { channel="modbus:tcp:plc:coils:coil00:switch"}

Rules:

rule "Simulate push button"
when
   Item Living received command
then
      // act if we need to change
   if (receivedCommand != Living.state) {
         // push the simulated button
      Living_Pulse.sendCommand(ON)
   }   // else already as commanded
end

rule "Simulate button release"
when
   Item Living changed
then
   if (Living_Pulse.state == ON) {
         // release simulated button
      Living_Pulse.sendCommand(OFF)
   } else {
      // bonus feature, detect external intervention
      // use to signal human occupation, chnage lights delays etc.
   }
end

Sitemap:

sitemap modbus label="Verlichting"
{
    Frame label="Test" {
        Switch item=Living icon="light"
    }
}

You still don’t need to read poll the coils for this to work (but you might have other reasons to do that).

There is a complicated reason I chose to have the “pulse” coil setup as write only, which I should explain.
Remember, data is read from the PLC only by scheduled polls; it could be up to a second out-of-date at any time.
Modbus writes are ad-hoc though, and get sent when the OH command happens.

Well, nearly. Writes get added to a queue of things to happen on the Modbus.
There is a small but real chance of a read-poll being in the queue ahead of our write.
The effect from openHABs viewpoint is that we can issue a command and afterwards get a poll update to “old” values (because the command is still working its way through the Modbus queue just behind).

That’s the way Modbus works and it is normally no problem at all - the next poll will catch up.

The little rules here though, rely on the correct sequence of events inside openHAB. We need the pulse Item to be up to date reflecting the coil state when a read poll (of the light status) comes in with a change. So we don’t rely on the coil read poll, we allow autoupdate to do its thing which is internal and much quicker.

I do it like this

Things:

Bridge modbus:tcp:plc [ host="192.168.1.50", port=502, id=2  ]{
      Bridge poller coils [start=0, length=25, refresh=0, type="coil"] {
            Thing data coil00 [ writeStart="0", writeValueType="bit", writeType="coil" ]
    }
   Bridge poller discreteInputs [ start=0, length=25,refresh=1000 type="discrete" ] {
		Thing data discrete00 [ readStart="0", readValueType="bit" ]        
	}
}

Items:

Contact Living  "[MAP(en.map):%s]"   { channel="modbus:tcp:plc:discreteInputs:discrete00:contact"}
Switch Living_Pulse "My light"  { channel="modbus:tcp:plc:coils:coil00:switch"}

Rules:

rule "Living_Pulse is a switch"
      when
       Item Living_Pulse received command
          then
           if (receivedCommand == ON)     
              createTimer(now.plusMillis(250))[
              sendCommand(Living_Pulse,OFF)
              ]
              end  

en.map:

CLOSED=UIT
OPEN=AAN
NULL=unknown

Sitemap:

Frame label="Verlichting" {
        Switch item=Living_Pulse mappings=[ON="AAN"]
        Text item=Living  icon="light" }
1 Like