Position Controll Rollershutter via Relay

  • Platform information:
    • Hardware: _ Raspberry Pi 3 B+, 4x 1,4 GHz, 1 GB RAM_
    • openHAB version: 3.1

Hello all,

I am still at the very beginning regarding my programming as well as Openhab knowledge. I have searched the whole forum. However, I have not found a solution that I can get to work :slight_smile:

Here is what I have in mind:
My goal is that I can control my blinds with a slider. That is, if the slider is on 50%, the blinds also drive in real on 50%. (later I would like to control the blinds via Apple HomeKit and the Openhab App).

Also, the value should be stored where the blinds currently stand. This is important if the blinds are at 50% and then, for example, should be driven to 70%. That is, it should be stored that the blinds are already X sec down / up.

I have a relay module which is controlled via MQTT (the relays themselves via CAN bus). I would like to control the blinds with this relay module. (The relays are interlocked and therefore electrically safe).

My module has no timer etc. the module understands only relay on/off.
The roller blinds need 25 sec (travel) to be completely closed.

Currently I can only control the relays. but must deactivate them Manuel via Stop command…

I hope it is understandable described and one can understand my goal.
Is such a thing in principle possible to illustrate? If so, I would be very happy about your help :slight_smile:

Many thanks in advance.
Kind regards
Marvin

Item

Rollershutter    Rollo_Kueche              "Rollo Küche"                            ["Rollershutter"]
Switch           Rollo_Kueche_auf          "Rollo 1 auf"                                                 {channel="mqtt:topic:mqtt:E6V_41:Ausgang2"}
Switch           Rollo_Kueche_zu           "Rollo 1 zu"                                                  {channel="mqtt:topic:mqtt:E6V_41:Ausgang1"}

Thing

Bridge mqtt:broker:mqtt
{
Thing topic E6V_41 "E6V ID41" {
    Channels:
	Type dimmer : Ausgang1 [stateTopic="Relais/41/state/201", commandTopic="Relais/41/command/201", min=0, max=65535, step=1, on="65535", off="0"]
	Type dimmer : Ausgang2 [stateTopic="Relais/41/state/202", commandTopic="Relais/41/command/202", min=0, max=65535, step=1, on="65535", off="0"]
}

Rule

var Timer timer = null

rule "Rollo Küche Steuerung"
when
  Item Rollo_Kueche received command
then
    if (receivedCommand == UP){
        sendCommand(Rollo_Kueche_zu, OFF)
        sendCommand(Rollo_Kueche_auf, ON)
        }
    else if (receivedCommand == DOWN){
       sendCommand(Rollo_Kueche_auf, OFF)
       sendCommand(Rollo_Kueche_zu, ON)
       }
    else if (receivedCommand == STOP){
       sendCommand(Rollo_Kueche_auf, OFF)
       sendCommand(Rollo_Kueche_zu, OFF)
       }

end

Ideas

Hi Rossko,

thank you very much for the links. They have helped me.
However, I still have one question.

All calculated values always refer to one item. In this case to my Rollo_1 item.

But now I have two channels for my relays (one for UP one for DOWN).
So far I have created two further items with the respective channel.
Is it possible to define two channels in one item? I want to describe which channel is for UP and which for DOWN.
Thanks for the help.
BR
Marvin

Rollershutter    Rollo_Kueche              "Rollo Küche"                            ["Rollershutter"]
Switch           Rollo_Kueche_auf          "Rollo 1 auf"                                                 {channel="mqtt:topic:mqtt:E6V_41:Ausgang2"}
Switch           Rollo_Kueche_zu           "Rollo 1 zu"                                                  {channel="mqtt:topic:mqtt:E6V_41:Ausgang1"}

That’s okay, it doesn’t cost you anything.

Sure.
But
If either channel issues state updates, the shared Item updates.
If you send a command to the Item, both channels get the command. You’re likely to find that part a problem.

As an alternative, don’t forget you can issue MQTT Actions to publish to topics direct from a rule.

So you only have the ability to drive the Blind UP and DOWN?

You have no feedback as to the state of the blind like limit switch or position detection?

You want to use a slider to drive the blind to a set position from where it is?

Can you share what hardware you have used?

I hope the Blind has internal limit switches so you can’t damage the blind.

Having no feedback you have to guess where it is and when your guess is out it will get worse every time you change it. One way to overcome this is to drive the blind to a known position and start again. You can also put a pulse encoder on it to track where it is.

Yes exactly the module understands only UP and DOWN. There is no feedback where the blinds are located.

The blinds have an internal limit switch. So nothing can break here.
I would like to control the blinds as you say with a slider (or digital buttons 10% 50% etc.).

An idea to calibrate the position is that the blinds open at full opening (0%) one second longer than necessary. This way you can guarantee that the blinds are really at 0%.

From the hardware I have only a Raspberry where via OpenCAN on the CAN bus go. From then on I control the CAN Relay (Know Name) directly.

The hardware and the wiring etc. works and is also fully functional. Only concerning the software I have no idea how to implement it.

I’m glad about any help because I’m currently unfortunately not getting anywhere… :slight_smile:

I try it this way but it does not work… Please can say where my failure is?

Rollershutter    Rollo_1        "Rollo 1"                                                    ["Rollershutter"]    {channel="[UP:mqtt:topic:mqtt:E6V_3:Ausgang2],[DOWN:mqtt:topic:mqtt:E6V_3:Ausgang1]"}

You’ve just made up imaginary syntax

You don’t have any one channel named
[UP:mqtt:topic:mqtt:E6V_3:Ausgang2],[DOWN:mqtt:topic:mqtt:E6V_3:Ausgang1]

You may link two channels to one Item

Rollershutter Rollo_1 "Rollo 1"  ["Rollershutter"] {channel="mqtt:topic:mqtt:E6V_3:Ausgang2", channel="mqtt:topic:mqtt:E6V_3:Ausgang1"}

but as already said, it is not going to work how you want it to work. All commands go to both channels.

Stick with your separate up/down switch Items, worked by rule from a ‘master’ rollershutter Item, that’s fine.
It’s quite normal to have worker Items behind the scenes, never meant to be seen in the User facing UI.

So you have an UP and DOWN command working from a sitemap yeah

What are the MQTT topic and Commands you have available to you.

I hope that I understand this right… because i not know what that mean MQTT Topic and Command…
I´m so sorry for my noob coding skills… :roll_eyes: i realy try to learn and understand it…
My Relay just know two commands. (ON = 65535, OFF=0)

Bridge mqtt:broker:mqtt
{
Thing topic E6V_3 "E6V ID3" {
    Channels:
	Type dimmer : Ausgang1 [stateTopic="Relay/3/state/201", commandTopic="Relay/3/command/201", min=0, max=65535, step=1, on="65535", off="0"]
	Type dimmer : Ausgang2 [stateTopic="Relay/3/state/202", commandTopic="Relay/3/command/202", min=0, max=65535, step=1, on="65535", off="0"]

Right now i work with this:

    rule "Rollo Küche Steuerung"
when
  Item Rollo_1 received command
then
    if (receivedCommand == UP){
        sendCommand(Rollo_1_zu, OFF)
        sendCommand(Rollo_1_auf, ON)
        }
    else if (receivedCommand == DOWN){
       sendCommand(Rollo_1_auf, OFF)
       sendCommand(Rollo_1_zu, ON)
       }
    else if (receivedCommand == STOP){
       sendCommand(Rollo_1_auf, OFF)
       sendCommand(Rollo_1_zu, OFF)
       }

end

And the rule controlls this Items:

Rollershutter    Rollo_1        "Rollo 1"                                                    ["Rollershutter"]   
Switch           Rollo_1_auf    "Rollo 1 auf"                                                                     {channel="mqtt:topic:mqtt:E6V_3:Ausgang2"}
Switch           Rollo_1_zu     "Rollo 1 zu"                                                                      {channel="mqtt:topic:mqtt:E6V_3:Ausgang1"}

And my Sidemap looks like:

Frame label="Rollo" {
    Switch item=Rollo_1 label="Küche" icon="rollershutter"

Thank you so much for your help.

That’s all fine. It doesn’t affect you calculating an estimated position to put into your Rollo_1 Item state. (though you would perhaps want to disable autoupdate when you do that, to stop commands to Rollo_1 interfering.)

@MarveyHAB

So you can send switch states to roller shutter and you can send mqtt commands from a rule.

So you don’t need things or extra items they will get in the way. Work with one item.

Rollershutter Rollo_1 "Rollo 1"  ["Rollershutter"]

Add these to sitemap

       Default item=Rollo_1
       Slider item=Rollo_1 label="Position [%s]"
       Switch item=Rollo_1 mappings=[100="Closed", 50="Half", 0="Open"]

You can edit to suit hight and name

Create a rule making sure the var at the very top of page. I have not tested this it just a educated guess so let me know the problems. I have also not put in a homeing routine.

var Timer UpTimer = null
var Timer DownTimer = null
var Timer ManUpTimer = null
var Timer ManDownTimer = null
var current_Position = 0

rule "Rollo Küche Steuerung"
when
  Item Rollo_1 received command
then
    logInfo("Command", receivedCommand.toString )
    val mqttActions = getActions("mqtt","mqtt:broker:mqtt")
       switch receivedCommand  {
        case UP: {
          ManDownTimer = null
          mqttActions.publishMQTT("Relay/3/state/202","0")
          mqttActions.publishMQTT("Relay/3/state/201","65535")
          ManUpTimer = createTimer(now, [|
          if(receivedCommand == UP ) {
            current_Position = current_Position + 1 
            ManUpTimer.reschedule(now.plusMillis(260))
            logInfo("current_Position", "MAN UP" + current_Position)
          }
          else {
            ManUpTimer = null
          }
          ])
          }
        case DOWN: {
          ManUpTimer = null
          mqttActions.publishMQTT("Relay/3/state/201","0")
          mqttActions.publishMQTT("Relay/3/state/202","65535")
          ManDownTimer = createTimer(now, [|
          if(receivedCommand == DOWN ) {
            current_Position = current_Position - 1 
            ManDownTimer.reschedule(now.plusMillis(260))
            logInfo("current_Position", "MAN DOWN" + current_Position)
          }
          else {
            ManDownTimer = null
          }
          ])
          }
        case STOP: {
          Rollo_1.postUpdate(current_Position) //On stop give item the guessed position
          mqttActions.publishMQTT("Relay/3/state/201","0")
          mqttActions.publishMQTT("Relay/3/state/202","0")
          ManUpTimer = null
          ManDownTimer = null
          UpTimer = null
          DownTimer = null
          }
        default: {
          mqttActions.publishMQTT("Relay/3/state/201","0") // Make Sure its not moving when change is requested
          mqttActions.publishMQTT("Relay/3/state/202","0")
          logInfo("Shutter", "Changing to " + receivedCommand) 
          UpTimer = createTimer(now, [|
          if(receivedCommand > current_Position) {
          mqttActions.publishMQTT("Relay/3/state/201","65535")
          current_Position = current_Position + 1 
          UpTimer.reschedule(now.plusMillis(260))
          logInfo("current_Position", "Changing to " + current_Position)
          }
          else {
              mqttActions.publishMQTT("Relay/3/state/201","0")
            UpTimer = null
          }
          ])

          DownTimer = createTimer(now, [|
          if(receivedCommand < current_Position) {
            mqttActions.publishMQTT("Relay/3/state/202","65535")
            current_Position = current_Position - 1 
            DownTimer.reschedule(now.plusMillis(260))
            logInfo("current_Position", "Changing to " + current_Position)
          }
          else {
            mqttActions.publishMQTT("Relay/3/state/202","0")
            DownTimer = null
          }
          ])

        }
      
    }
end

oh wow Thank you so much for your help and time :slight_smile:

this is how it work for me:

var Timer UpTimer = null
var Timer DownTimer = null
var Timer ManUpTimer = null
var Timer ManDownTimer = null
var current_Position = 0

rule "Rollo_1"
when
  Item Rollo_1 received command
then
    logInfo("Command", receivedCommand.toString )
    val mqttActions = getActions("mqtt","mqtt:broker:mqtt")
       switch receivedCommand  {
        case UP: {
          ManDownTimer = null
          mqttActions.publishMQTT("Relay/3/command/202","0")
          mqttActions.publishMQTT("Relay/3/command/201","65535")
          ManUpTimer = createTimer(now, [|
          if(receivedCommand == UP ) {
            current_Position = current_Position + 1 
            ManUpTimer.reschedule(now.plusNanos(1000000000))
            logInfo("current_Position", "MAN UP" + current_Position)
          }
          else {
            ManUpTimer = null
          }
          ])
          }
        case DOWN: {
          ManUpTimer = null
          mqttActions.publishMQTT("Relay/3/command/201","0")
          mqttActions.publishMQTT("Relay/3/command/202","65535")
          ManDownTimer = createTimer(now, [|
          if(receivedCommand == DOWN ) {
            current_Position = current_Position - 1 
            ManDownTimer.reschedule(now.plusNanos(1000000000))
            logInfo("current_Position", "MAN DOWN" + current_Position)
          }
          else {
            ManDownTimer = null
          }
          ])
          }
        case STOP: {
          Rollo_1.postUpdate(current_Position) //On stop give item the guessed position
          mqttActions.publishMQTT("Relay/3/command/201","0")
          mqttActions.publishMQTT("Relay/3/command/202","0")
          ManUpTimer = null
          ManDownTimer = null
          UpTimer = null
          DownTimer = null
          }
        default: {
          mqttActions.publishMQTT("Relay/3/command/201","0") // Make Sure its not moving when change is requested
          mqttActions.publishMQTT("Relay/3/command/202","0")
          logInfo("Shutter", "Changing to " + receivedCommand) 
          UpTimer = createTimer(now, [|
          if(receivedCommand > current_Position) {
          mqttActions.publishMQTT("Relay/3/command/201","65535")
          current_Position = current_Position + 1 
          UpTimer.reschedule(now.plusNanos(1000000000))
          logInfo("current_Position", "Changing to " + current_Position)
          }
          else {
              mqttActions.publishMQTT("Relay/3/command/201","0")
            UpTimer = null
          }
          ])

          DownTimer = createTimer(now, [|
          if(receivedCommand < current_Position) {
            mqttActions.publishMQTT("Relay/3/command/202","65535")
            current_Position = current_Position - 1 
            DownTimer.reschedule(now.plusNanos(1000000000))
            logInfo("current_Position", "Changing to " + current_Position)
          }
          else {
            mqttActions.publishMQTT("Relay/3/command/202","0")
            DownTimer = null
          }
          ])

        }
      
    }
end

I have two more Questions:

    Frame label="test" {
    Default item=Rollo_1
    Slider item=Rollo_1 label="Position [%s]"
    Switch item=Rollo_1 mappings=[10="Closed", 7.5="75%", 5="Half", 2.5="25%", 0="Open"]

when I am using the buttons the blinds run perfect. But when I use the slider then it does not work correctly. For example, when I put the slider from 0% to 35% percent the relay stays on for 35 seconds. When I set the slider from 35% to 50% the Relay stays on for another 15 seconds…
Is it possible to set it that way that the Relay stays on only for (for example) 10 seconds instead of 100 seconds when putting the slider to 100 %?

My second question. When the blinds are moving for 10 Seconds then the MQTT command will resent the whole 10 seconds. But my relay will stay ON unless i will send the OFF command. I tried to delete the (now.plusNanos(1000000000)) but it didn´t work well :slight_smile:

Again thank you so much for your help. I cheered out loud earlier when the relay worked so well for the first time :slight_smile:

You might have missed -

When you can separate commands from state - “where I want to go” from “where it is now” - you have something to work from to calculate the direction and running time needed.

It’s a really good idea to look in your events.log to see what is happening, and if it is not what you expect, share it with us too.

1 Like

Roller shutter items are percent which is why slider doesn’t work

mappings=[100=“Closed”, 75=“75%”, 50=“Half”, 25=“25%”, 0=“Open”]

You need to set that time to a meaningful value as this is your resolution for step value

I guess that is the full travel time up to down

so 25sec represents 100 steps so 2.5e+8 Nanos

should have been reschedule(now.plusMillis(250))
That donates how often the loop runs to increment the value for tracking

I added some lines to turn the relay off when it thinks its at the end of travel in manual mode.

Make sure the shutter is in the 0 open position when you save the rule and let me know how you go

var Timer UpTimer = null
var Timer DownTimer = null
var Timer ManUpTimer = null
var Timer ManDownTimer = null
var current_Position = 0

rule "Rollo Küche Steuerung"
when
  Item Rollo_1 received command
then
    logInfo("Command", receivedCommand.toString )
    val mqttActions = getActions("mqtt","mqtt:broker:mqtt")
       switch receivedCommand  {
        case UP: {
          ManDownTimer = null
          mqttActions.publishMQTT("Relay/3/state/202","0")
          mqttActions.publishMQTT("Relay/3/state/201","65535")
          ManUpTimer = createTimer(now, [|
          if(receivedCommand == UP ) {
            current_Position = current_Position + 1 
            ManUpTimer.reschedule(now.plusMillis(250))
            logInfo("current_Position", "MAN UP" + current_Position)
          }
          else {
            mqttActions.publishMQTT("Relay/3/state/201","0")
            ManUpTimer = null
          }
          ])
          }
        case DOWN: {
          ManUpTimer = null
          mqttActions.publishMQTT("Relay/3/state/201","0")
          mqttActions.publishMQTT("Relay/3/state/202","65535")
          ManDownTimer = createTimer(now, [|
          if(receivedCommand == DOWN ) {
            current_Position = current_Position - 1 
            ManDownTimer.reschedule(now.plusMillis(250))
            logInfo("current_Position", "MAN DOWN" + current_Position)
          }
          else {
            mqttActions.publishMQTT("Relay/3/state/202","0")
            ManDownTimer = null
          }
          ])
          }
        case STOP: {
          Rollo_1.postUpdate(current_Position) //On stop give item the guessed position
          mqttActions.publishMQTT("Relay/3/state/201","0")
          mqttActions.publishMQTT("Relay/3/state/202","0")
          ManUpTimer = null
          ManDownTimer = null
          UpTimer = null
          DownTimer = null
          }
        default: {
          mqttActions.publishMQTT("Relay/3/state/201","0") // Make Sure its not moving when change is requested
          mqttActions.publishMQTT("Relay/3/state/202","0")
          logInfo("Shutter", "Changing to " + receivedCommand) 
          UpTimer = createTimer(now, [|
          if(receivedCommand > current_Position) {
          mqttActions.publishMQTT("Relay/3/state/201","65535")
          current_Position = current_Position + 1 
          UpTimer.reschedule(now.plusMillis(250))
          logInfo("current_Position", "Changing to " + current_Position)
          }
          else {
              mqttActions.publishMQTT("Relay/3/state/201","0")
            UpTimer = null
          }
          ])

          DownTimer = createTimer(now, [|
          if(receivedCommand < current_Position) {
            mqttActions.publishMQTT("Relay/3/state/202","65535")
            current_Position = current_Position - 1 
            DownTimer.reschedule(now.plusMillis(250))
            logInfo("current_Position", "Changing to " + current_Position)
          }
          else {
            mqttActions.publishMQTT("Relay/3/state/202","0")
            DownTimer = null
          }
          ])

        }
      
    }
end

Yeah its important to RTFM and know what is going on.

Delete these Items so you cant change the state unless it goes through the rule.

Switch           Rollo_1_auf    "Rollo 1 auf"                                                                     {channel="mqtt:topic:mqtt:E6V_3:Ausgang2"}
Switch           Rollo_1_zu     "Rollo 1 zu" 

I almost did the same rule for my blinds with GPIO Remote.
Full close and open will work fine and a % position too. But keep in mind, if you set it by hand lets say 3 times to get to a desired posiition, it will get messed up. The motor has inertia and to compensate for it you need to measure it exactly which i think is almost impossible. At least my motorblinds inertia differs depending on the outside temperature a tiny bit.
My blinds dont have an “end position switch”, the controller measures the motor amps and shuts itself down. If the calculated position is at 100 % or 0% i just let the Relay switch for a bit longer on to compensate for it. Without any Kind of a Endswitch, you shouldn`t do this !!!

Yes, once the core calculated runtime is in place, you will need some self recalibration too. You “know” the position when at the extremes, and can reset your position indicator. But how do you know when you are at the extremes? More rules can detect when you are requesting a move to 0 or 100 position and deliberately extend the runtime a little to make sure the roller physically gets to the end stop.

Hi All,

I have now found a solution how the code works for me.
Thanks a lot for all your support!!! This has helped me a lot.
The blinds drive super accurate which makes me very happy.
The blinds have a real travel time of about 24.5 seconds. Thus, the blinds calibrate themselves when these 25 sec upward drive. This is of course only because my roller blinds have a limit switch in both positions.

Thanks again :slight_smile:

var Timer UpTimer = null
var Timer DownTimer = null
var Timer ManUpTimer = null
var Timer ManDownTimer = null
var current_Position = 0
var move_up = 0
var move_down = 0

rule "Rollo Küche Steuerung"
when
  Item Rollo_1 received command
then
    logInfo("Command", receivedCommand.toString )
    val mqttActions = getActions("mqtt","mqtt:broker:mqtt")
       switch receivedCommand  {
        case UP: {
          ManDownTimer = null
          mqttActions.publishMQTT("Relay/3/command/202","0")
          mqttActions.publishMQTT("Relay/3/command/201","65535")
          ManUpTimer = createTimer(now, [|
          if(receivedCommand == UP ) {
            current_Position = current_Position + 1 
            ManUpTimer.reschedule(now.plusNanos(10000000))
            logInfo("current_Position", "MAN UP" + current_Position)
          }
          else {
            mqttActions.publishMQTT("Relay/3/command/201","0")
            ManUpTimer = null
          }
          ])
          }
        case DOWN: {
          ManUpTimer = null
          mqttActions.publishMQTT("Relay/3/command/201","0")
          mqttActions.publishMQTT("Relay/3/command/202","65535")
          ManDownTimer = createTimer(now, [|
          if(receivedCommand == DOWN ) {
            current_Position = current_Position - 1 
            ManDownTimer.reschedule(now.plusNanos(10000000))
            logInfo("current_Position", "MAN DOWN" + current_Position)
          }
          else {
            mqttActions.publishMQTT("Relay/3/command/202","0")
            ManDownTimer = null
          }
          ])
          }
        case STOP: {
          Rollo_1.postUpdate(current_Position) //On stop give item the guessed position
          mqttActions.publishMQTT("Relay/3/command/201","0")
          mqttActions.publishMQTT("Relay/3/command/202","0")
          ManUpTimer = null
          ManDownTimer = null
          UpTimer = null
          DownTimer = null
          }
        default: {
          //mqttActions.publishMQTT("Relay/3/command/201","0") // Make Sure its not moving when change is requested
          //mqttActions.publishMQTT("Relay/3/command/202","0")
          logInfo("Shutter", "Changing to " + receivedCommand) 
          UpTimer = createTimer(now, [|
          if(receivedCommand > current_Position) {
            if(move_down == 0) {
          mqttActions.publishMQTT("Relay/3/command/201","65535")
            }
             move_down = 1
          current_Position = current_Position + 1 
          UpTimer.reschedule(now.plusNanos(250000000))
          logInfo("current_Position", "Changing to " + current_Position)
          }
          else {
             if(move_down == 1) {
              mqttActions.publishMQTT("Relay/3/command/201","0")
            UpTimer = null
            move_down = 0
            }
          }
          ])

          DownTimer = createTimer(now, [|
          if(receivedCommand < current_Position) {
            if(move_up == 0) {  
              mqttActions.publishMQTT("Relay/3/command/202","65535")
            }
          move_up = 1
            current_Position = current_Position - 1 
            DownTimer.reschedule(now.plusNanos(250000000))
            logInfo("current_Position", "Changing to " + current_Position)
          }
          else {
            if(move_up == 1) {
              mqttActions.publishMQTT("Relay/3/command/202","0")
              DownTimer = null
              move_up = 0
            }
          }
          ])

        }
      
    }
end

now.plusNanos value should be the same for all of them