Motorised blind. Remember position - how?

How do I remember the position of a blind through power cycles? I have tried various attempts using global variables but haven’t managed to get it working yet so any ideas based on the code below would be very welcome, please.

The blind is driven by a DC motor, controlled by an H-bridge and uses an optical endstop to detect fully open, with a rotary encoder to detect position and to provide a fully closed endstop function.

At the moment I have the blind do an initialise sequence to determine the fully open position when power is restored but I’d like it to remember the position and resume normal operation from there.

esphome:
  name: blind_utility_room
  platform: ESP8266
  board: d1_mini

  on_boot:
    priority: -10
    then:
# the next few lines force the blind to a known state by first closing the blind for 1s, then opening it until the optical endstop is reached
      - switch.turn_off: blind_s1
      - switch.turn_on: blind_s2
      - delay: 1s
      - switch.turn_off: blind_s2
      - cover.open: utility_blind

globals:
   - id: closed_steps # how many encoder steps when fully closed
     type: int
     restore_value: no
     initial_value: '100'
   - id: manual_action # used to allow the binary touch switch to raise or lower the blind
     type: int
     restore_value: no
     initial_value: '0'

wifi:
  ssid: !secret wifi_name
  password: !secret wifi_password
  domain: !secret wifi_domain

  manual_ip:
    static_ip: A.B.C.D
    gateway: A.B.C.X
    subnet: 255.255.255.0

# Enable logging
logger:
  level: DEBUG
  logs:
    light: NONE

# Enable Home Assistant API
api:

ota:

sensor:
  - platform: rotary_encoder
    name: "Rotary Encoder"
    pin_b: D1 #GPIO05
    pin_a: D2 #GPIO04
    id: encoder
    on_value:   
      then:
        - if: 
            condition: #used to stop the blind when it gets to fully closed position
              lambda: return id(encoder).state >= id(closed_steps) ; 
            then: 
              cover.stop: utility_blind

  - platform: wifi_signal
    name: "WiFi - Utility Room Blind"
    update_interval: 60s

binary_sensor:
  - platform: gpio # for optical endstop at fully open position
    pin:
      number: D5 #GPIO14
      mode: INPUT_PULLUP
      inverted: FALSE
    id: endstop
    name: "Endstop"
    internal: True 
    on_press: # used to light the LED when the endstop is reached (just visual, not used programatically)
      then:
        - light.turn_on: endstop_led
        - cover.stop: utility_blind
        - sensor.rotary_encoder.set_value:
            id: encoder
            value: 0
    on_release:
      then:
        - light.turn_off: endstop_led

  - platform: gpio
    pin:
      number: D0 #GPIO16
      mode: INPUT_PULLUP
      inverted: FALSE
    id: touch_switch
    name: "Touch_Switch"
    internal: True
    on_press: # used to allow manual movement of the blind (either Open or Close)
      then:
        - if:
            condition:
              lambda: return id(manual_action) == 0 ;
            then:
              - cover.close: utility_blind
              - globals.set:
                  id: manual_action
                  value: '1'
            else:
              - cover.open: utility_blind
              - globals.set:
                  id: manual_action
                  value: '0'

output:
# two outputs to H-bridge for direction and movement
  - platform: gpio
    id: 'blind1'
    pin: D6 #GPIO12
  - platform: gpio
    id: 'blind2'
    pin: D7 #GPIO#13

# for LED indicator
  - platform: esp8266_pwm
    pin: D3 #GPIO00
    inverted: TRUE
    frequency: 1000 Hz
    id: led

light:
# LED indicator light
  - platform: monochromatic
    output: led
    name: "Endstop_Reached"   
    id: endstop_led
    internal: True
    default_transition_length: 0s

switch:
# This is to restart the ESPHome device remotely
  - platform: restart
    name: "Restart ESPHome - Utility Room Blind"

# For the H-bridge outputs
  - platform: output
    name: "blind1"
    output: 'blind1'
    id: blind_s1
    internal: true 
  - platform: output
    name: "blind2"
    output: 'blind2'
    id: blind_s2
    internal: true 

cover:
  - platform: template
    name: "Utility Room Blind"
    id: utility_blind
    assumed_state: true
    optimistic: true
    device_class: shade

    open_action:
      - switch.turn_off: blind_s2
      - switch.turn_on: blind_s1
      - globals.set: # reverses the manual direction for next manual action
          id: manual_action
          value: '0'
      - delay: 30s
      - switch.turn_off: blind_s1

    close_action:
      - switch.turn_off: blind_s1
      - switch.turn_on: blind_s2
      - globals.set: # reverses the manual direction for next manual action
          id: manual_action
          value: '1'
      - delay: 30s
      - switch.turn_off: blind_s2

    stop_action:
      - switch.turn_off: blind_s1
      - switch.turn_off: blind_s2    
1 Like

You may be better to read the existing thread and if it doesn’t answer your questions post there :slight_smile: Motor on a roller blind - ESPHome version?

Thanks, @nickrout - I’ve read through that thread and don’t see a similar solution (mostly stepper based, which I tried but gave up on due to the large blind I have that rolled down under gravity).

Thinking further, this isn’t so much a blind-specific question as a question about how to store the position of a rotary encoder in a way that persisted through power cycles.

This is the closest I could find on the thread you referenced but I’ve not yet been able to get it to work with the rotary encoder Motor on a roller blind - ESPHome version?

I’ll leave the code here - it works very well as it is and someone may find it helpful. It initialises itself every time the power restores - and the primary benefit over steppers is the worm gear drive that allows it to manage heavy blinds without worrying about them dropping when the power is off.

The blind is over 2m tall and on an exit door so it needs to open quickly (desired time is sub 10 Seconds). I currently have a 100RPM motor and it takes about 12s so I have a 200RPM version on order to see if I can improve that opening speed!

I’m not an expert, so there might be a better way to achieve this, but here’s the solution I came up with:

  1. Set a global variable to store the cover position:
globals:
  - id: cover_pos # Cover Position
    type: float
    restore_value: true
  1. Enable ESP8266 to restore from flash and make it set the “cover position property” to the value stored in the global variable on boot:
esphome:
  esp8266_restore_from_flash: true
  on_boot:
    priority: 700.0
    then:
      - lambda:
          |-
          id(my_cover).position = id(cover_pos); // Change "my_cover" to your cover id.
  1. Save the “cover position property” in the global variable every time the the cover stops by adding these lines to your open and close switches:
switch:
  - platform: gpio # Open Switch
    (...) # Put your switch code in here
    on_turn_off:
      - lambda:
          |-
          id(cover_pos) = id(my_cover).position; // Change "my_cover" to your cover id.
  - platform: gpio # Close Switch
    (...) # Put your switch code in here
    on_turn_off:
      - lambda:
          |-
          id(cover_pos) = id(my_cover).position; // Change "my_cover" to your cover id.

I hope it works for you!
If you find any better way to do it, please let me know!

Thank you, @andrepia - I’ll try that and report back.

@RGN01 did you ever get this working? I am intrigued on the outcome. I am building and trying to set something similar.

No, I went with a simpler option - it homes when the power is restored and every time it is fully retracted. Works well.

Cool thanks. I have implemented the reverse of what you have. It homes to 0 (fully closed) on power restore. Granted power failures are rare so I think I can deal with opening it manually.

Plus I plan to set automations up to open the blind automatically anyways so worst case it will just close and then immediately open again. Reason why I didnt have it at the top was because my motor drives the chain link so all my electronics and mechanics are on the windowsill.

Your code combined with anothers have massively helped me implement what I want so thanks for posting it. I plan to do the same when its done to help others.

One thing I have never managed to get working, but wondering if you did, was get the blind to go to a set position defined by position slider within home assistant. I see there is a ‘position_action’ but I am unsure how to make use of it. I see in your code you haven’t used it so are you able to select a percentage to move the blind to or have you omitted that functionality?

Don’t worry ignore this. I managed to get it working. Very happy with it all now.

Glad you got it working. No, I never tried it. My blind is on a door and is for privacy, not shade, so I need it either open or closed and have no need for intermediate positions.

Richard

How do you sense when the blinds are completely retracted? Did you put an additional external sensor or do you just run the motor until it can’t move anymore?

I have an optical sensor at fully retracted (up) and count pulses from the rotary encoder from there for fully deployed (down)

Hi, do you have your finished code to share please, im looking to do something similar.

I would like a dc motor with attached encoder to open a chicken coop door and close it again at set times.

Thanks