Motor on a roller blind - ESPHome version?

Thanks!
What kind of power supply (V/A) are you using @FF-Fox ?

12V/2A. Connected to the A4998/stepper and to a buck converter (cheap LM2596 module) for the Wemos D1 mini :wink:

I also used a 12V/2A power supply with a A4988 driver and NEMA 17 but it failed to lift the roller blind. With a luggage scale I measured that about 2.1/2.2kg is needed to lift the roller blind and I assume the NEMA should be able to do this? I put a custom made wheel directly on the NEMA which is driving the robe of the roller blind. Should this work without additional gears you think?

Depending on your blind and speed, maybe.

I needed the gearbox for my blind as the NEMA wasn’t able to lift the blind without it…

I’m trying this github project;

When i flash the NEMA_ROLLER_BLIND.ino to my WEMOS D1 Mini the AP is working but after connecting to my own wifi reconnecting al the time (looks like reboot).

When i flash the motor_on_a_roller_blind-ws_060918_Working.ino nothing works…
This project looks exact what i need, anyone the setup with the NEMA17 and L298N H Bridge?

Hello. I was trying to use this code, together with this example from the doc:

But I get an error from Cover Template:
position_action is an invalid option [cover.template]

What is going on?Was this option removed?I also don’t see it in the ESPHome documentation, which really sucks…

You need the 1.15-dev version of ESPHome for that.

1 Like

So everything works almost perfectly, here is my code if anybody wants it. It also keeps track of the current position, so when the power is out or anything unexpected happens, the stepper position will be reset to the saved one. I also have different speeds for closing/opening, since I can close the blinds faster that opening due to them being quite heavy. I also have a relay connected to my heating, but that is another point.

esphome:
  name: kitchen_blind
  platform: ESP8266
  board: nodemcuv2
  on_boot:
    priority: -100
    then:
      - stepper.report_position:
          id: blind_stepper
          position: !lambda "return id(current_position);"
      - stepper.set_target:
          id: blind_stepper
          target: !lambda "return id(current_position);"
      - stepper.set_speed:
          id: blind_stepper
          speed: 500 steps/s
      - cover.template.publish:
          id: kitchen_blind
          current_operation: IDLE
          position: !lambda 'return (float(float(id(blind_stepper).current_position) / float(id(open_position))));'
      - output.turn_on: builtin_led
  esp8266_restore_from_flash: true

wifi:
  ssid: ""
  password: ""
  manual_ip:
    static_ip: 192.168.1.3
    gateway: 192.168.1.1
    subnet: 192.168.1.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Kitchen Blind Fallback Hotspot"
    password: !secret esphome_password
    ap_timeout: 1min
  reboot_timeout: 2min
  use_address: 192.168.1.3

captive_portal:

# web_server:
#   port: 80

# Enable logging
logger:

ota:
  password: !secret esphome_password

# Enable Home Assistant API
api:
  services:
    - service: control_stepper
      variables:
        target: int
      then:
        - stepper.set_target:
            id: blind_stepper
            target: !lambda 'return target;'
        - globals.set:
            id: current_position
            value: !lambda 'return target;'
        - output.turn_on: builtin_led
    - service: set_speed
      variables:
        speed: int
      then:
        - stepper.set_speed:
            id: blind_stepper
            speed: !lambda 'return speed;'
    - service: middle
      then:
        - stepper.set_speed:
            id: blind_stepper
            speed: !lambda |-
              if (id(middle_position) >= id(blind_stepper).current_position) {
                return 500;
              } else {
                return 800;
              }
        - stepper.set_target:
            id: blind_stepper
            target: !lambda 'return id(middle_position);'
        - while:
            condition:
              lambda: |-
                return id(blind_stepper).current_position != id(middle_position);
            then:
              - cover.template.publish:
                  id: kitchen_blind
                  current_operation: !lambda |-
                      if(id(middle_position) >= id(blind_stepper).current_position) {
                        return COVER_OPERATION_OPENING;
                      } else {
                        return COVER_OPERATION_CLOSING;
                      }
                  position: !lambda 'return (float(float(id(blind_stepper).current_position) / float(id(open_position))));'
              - delay: 1000 ms
        - cover.template.publish:
            id: kitchen_blind
            current_operation: IDLE
            position: !lambda 'return (float(float(id(blind_stepper).current_position) / float(id(open_position))));'
        - globals.set:
            id: current_position
            value: !lambda 'return id(middle_position);'
        - output.turn_on: builtin_led

globals:
  - id: open_position
    type: int
    initial_value: '9400'
  - id: middle_position
    type: int
    initial_value: '3760'
  - id: current_position
    type: int
    initial_value: '9400'
    restore_value: true

output:
  - platform: gpio
    pin: GPIO2
    id: builtin_led

switch:
  - platform: gpio
    name: "Heating Relay"
    pin:
      number: D6
      inverted: yes
    restore_mode: ALWAYS_OFF

stepper:
  - platform: a4988
    id: blind_stepper
    step_pin: D3
    dir_pin: D4
    max_speed: 800 steps/s
    sleep_pin:
      number: D2
      inverted: yes
    acceleration: inf
    deceleration: inf

status_led:
  pin: D1
  
cover:
  - platform: template
    device_class: shade
    name: Kitchen Blind
    id: kitchen_blind
    open_action:
      - stepper.set_speed:
          id: blind_stepper
          speed: 500 steps/s
      - stepper.set_target:
          id: blind_stepper
          target: !lambda "return id(open_position);"
      - while:
          condition:
            lambda: |-
              return id(kitchen_blind).position != 1;
          then:
            - cover.template.publish:
                id: kitchen_blind
                current_operation: !lambda |-
                    return COVER_OPERATION_OPENING;
                position: !lambda 'return (float(float(id(blind_stepper).current_position) / float(id(open_position))));'
            - delay: 1000 ms
      - cover.template.publish:
          id: kitchen_blind
          current_operation: IDLE
          position: !lambda 'return 1;'
      - globals.set:
          id: current_position
          value: !lambda 'return id(open_position);'
      - output.turn_on: builtin_led
    close_action:
      - stepper.set_speed:
          id: blind_stepper
          speed: 800 steps/s
      - stepper.set_target:
          id: blind_stepper
          target: 0
      - while:
          condition:
            lambda: |-
              return id(kitchen_blind).position != 0;
          then:
            - cover.template.publish:
                id: kitchen_blind
                current_operation: !lambda |-
                    return COVER_OPERATION_CLOSING;
                position: !lambda 'return (float(float(id(blind_stepper).current_position) / float(id(open_position))));'
            - delay: 1000 ms
      - cover.template.publish:
          id: kitchen_blind
          current_operation: IDLE
          position: !lambda 'return 0;'
      - globals.set:
          id: current_position
          value: !lambda 'return 0;'
      - output.turn_on: builtin_led
    stop_action:
      - stepper.set_target:
          id: blind_stepper
          target: !lambda return id(blind_stepper).current_position;
      - cover.template.publish:
          id: kitchen_blind
          current_operation: IDLE
          position: !lambda 'return (float(float(id(blind_stepper).current_position) / float(id(open_position))));'
      - globals.set:
          id: current_position
          value: !lambda 'return id(blind_stepper).current_position;'
      - output.turn_on: builtin_led
    has_position: true

I have a problem though. I don’t know why, but it seems like the stepper goes a full rotation, and then pauses for a fraction of a second, and this cycle repeats until the stepper gets to the desired position. Not sure what stepper library it is used, but I am guessing that my loop function takes to long to execute maybe?Or it might be because I use the a4988 driver with the sleep function in ESPHome, but the sleep pin is actually connected to the enable pin of the driver. But this worked well with my custom code, I woke the driver before I need to change position, and after the stepper was in the desired position, the driver was disabled, not sure how ESPHome does it.

1 Like

How long are the wires between your a4988 and the motor? I’m seeing similar issues, still figuring out of I can extend the wires between the a4988 and my 28byj-48.

Quite short, it worked fine without Esphome before, so it has to be something to do with this. Also, I think that when I first started testing with only the a4988 esp home integration, it worked fine, so I am guessing the esp8266 is too slow to handle everything.

First of all this project is awesome! I followed this guide here: Motorized WiFi IKEA Roller Blind : 11 Steps (with Pictures) - Instructables

But it ended up being to weak, so I got a nema 17 stepper but it is capped at 350mAH…
I would like to mount the nema directly to the window ceiling and put on a cap on the nema to be inserted directly into the tube. (I have cut the blind according to the link above…)

I see a lot of people are doing the gear box version, is that the only way to get this to work?

Im just wondering if I again need to order a new stepper motor or if that will work to pull a 180x180 cm blind(https://cdn-shop.adafruit.com/product-files/324/C140-A+datasheet.jpg)

I plan to use a DRV8825 and a wemos d1 mini.

Hi @JBS,

I am using DRV8825 drivers with NEMA 17HS4401 in ESPHome (although not officially supported.) The DRV8825 is almost plug and play with the A4988 - there is just a minor difference in connections:

  • EN(able) pin is an active low input - the DRV8825 is enabled by default. To disable, you can pull it high. I leave mine detached.

  • SL(ee)P pin is also an active low input - the DRV8825 is in sleep mode by default. I’m wiring this to ESPHome’s sleep_pin for the A4988

  • R(e)S(e)T is again an active low input - the DRV8825 will ignore signalling on the ST(e)P pin unless you pull it high. Now here’s the part I’m not sure about: this pin is also used to reset the driver to HOME, but seeing as there is no VDD on the DRV8825 as there is on the A4988, I’m attaching this to 3.3V from the ESP8266, so keeping it high. Anything from 2.5V to 5V should be okay.

So far, so good. When SLP is low, there doesn’t appear to be any power going to the motor. It appears to be fine to leave RST on 3.3V.

I haven’t played with the F(au)LT pin at all, which is in the place of the VDD on the A4988. Everything else is pretty much the same.

I wound up with a few NEMA 17HS4401’s and the DRV8825 gets pretty hot even after just 15-20 revolutions - heat sinks are a must! This may be because my power supply is not rated for the 1.7A 17HS4401’s though. I’m just starting out with steppers, so still learning.

The 1.7A NEMA 17HS4401 is pretty strong when it pulls, but freewheels without power. Probably good for a stiff blind that is sprung to remain in position once moved.

Anyone else experimented with ESPHome and the DRV8825? Basic config is:

stepper:

  - platform: a4988
    id: stepper_one
    step_pin: D1
    dir_pin: D2
    max_speed: 200 steps/s
    sleep_pin: D5
    acceleration: inf
    deceleration: inf

  - platform: a4988
    id: stepper_two
    step_pin: D6
    dir_pin: D7
    max_speed: 200 steps/s
    sleep_pin: D8
    acceleration: inf
    deceleration: inf

Kind regards, Dave.

@dhartsmith Thanks for your explanation :slight_smile:
Got my NEMA 17 and A4988 also wokring with ESPHome. Will try it with the DRV8825 when it is arrived from China.
Also need to print some new gear/wheels with a better balance between torque and grip.

1 Like

Hi all,

Great great topic. I am planing to do a DIY project based on the NEMA 17 + A4988, i.e. a 1-axis solar tracker based on magnetic compass measurements and the sun azithmut course among the day.
I just compiled some first example found here in this topic with a cover… but my motor don’t move at all.
Since this project is an outdoor one, I am powering the electronic with a 3S LiPo battery (up to 12.4V). A 5V stepdown regulator is used to power the ESP8266+ QMC5883 compass.
Anyone can confirm the exact pinout I have to follow ? From my current ESPHome model, the SLP pin is D2. No need to use the RST pin too ? or the ENABLE ?

Thanks in advance…

EDIT: I think my A4988 is smoked :frowning:

To be more precise … Since nothing worked with the pin D2 connected to SLP. I tried then connect RST & SLP togather as depicted in many blogs. It worked from HA with the cover object. then;… I decided to connected also D2 to SLP with still SLP & RST connected… then no more mouvements :(… I tried everything. Recheck 100x time connexion :(. I am afraid the A4988 is gone. There is a way to test it wiithout oscilloscope ?

Hi all, I’ve been reading this thread for a while and I though I’d give the blinds a go. I originally used the Motor on a roller blind and have 4 blinds running on it, however, I do prefer ESPhome.

THIS IS NOT A DIRECT REPLACEMENT FOR THE SAME WIRING AS THE ‘Motor on a roller blind’, the next post is.

The following sketch is complete, other than the passwords. It has one button and can be set up in situ. Hold the button down to enter setup mode, press again to start moving to the bottom, press at the bottom and it starts moving up, press again at the top and the blind is calibrated.
Location and action are stored in memory and it will remember where it should be, even after power loss.

I’ve have done a little testing and so far, so good. It could probably do with an LED to display setup mode, but it’ll be fine.

I wrote this and didn’t think of the 4 blinds I already have that have NO button, but I’ll sort it out :slight_smile:

This setup uses a Robotdyne ESP8266 Wifi D1 Mini and a 28BYJ-48 stepper motor + ULN2003 driver board.

# Press button for > 1 second to enter setup mode
# Press button again to start the blind closing
# Press button again when closed and blind starts to open (actually resets the stepper position to 0)
# Press button again when blind is fully open
# Job Done

# Button is also used to open/close the blind (must be fully open/closed first)

# NOTE:  If you find that your shades are going the wrong way, you can change the pin
#        settings below or reverse the + and – wires for each of the A and B motor
#        pairs on your driver and the motor will spin in the opposite direction.

substitutions:
  devicename: bathroomleft
  upper_devicename: Bathroom Left Blind
  mystepper: my_stepper
  pina: D5
  pinb: D1
  pinc: D2
  pind: D3
# Wrong direction? Comment the previous 4 lines and uncomment the following 4 
#  pina: D1
#  pinb: D5
#  pinc: D3
#  pind: D2

esphome:
  name: $devicename
  platform: ESP8266
  board: d1_mini
  esp8266_restore_from_flash: True
  on_boot:
    - priority: -200.0
      then:
      - stepper.report_position: # Set stepper to global variable
          id: $mystepper
          position: !lambda return id(${mystepper}_global);
      - stepper.set_target: # Set stepper to global variable
          id: $mystepper
          target: !lambda return id(${mystepper}_global);

wifi:
  networks: 
    - ssid: !secret wifi
      password: !secret wifi_password
    - ssid: !secret wifi2
      password: !secret wifi_password2

  ap:
    ssid: $upper_devicename
    password: !secret wifi_password

web_server:
  port: 80

logger:

api:
  password: !secret api_password

ota:
  password: !secret ota_password

captive_portal:

stepper:
  - platform: uln2003
    id: $mystepper
    pin_a: $pina
    pin_b: $pinb
    pin_c: $pinc
    pin_d: $pind
    max_speed: 300 steps/s # Speed of the motor
    sleep_when_done: True
    acceleration: inf
    deceleration: inf

globals:
  - id: ${mystepper}_global # Integer for storing the stepper position in case of reboot
    type: int
    restore_value: True
    initial_value: '0'

  - id: openclosed # Boolean to store OPEN/CLOSED state
    type: bool
    restore_value: True
    initial_value: '0'

  - id: endstop # Variable for storing OPEN (how far to move stepper)
    type: int
    restore_value: True
    initial_value: '1000'

  - id: settingmode # Integer for Setup Mode
    type: int
    restore_value: no
    initial_value: '0'

  - id: settingup # Boolean for Setup Active
    type: bool
    restore_value: no
    initial_value: '0'

binary_sensor:
  - platform: gpio
    pin:
      number: D7
      mode: INPUT_PULLUP
      inverted: True
    name: Button
    internal: True
    on_click:
    - min_length: 50ms
      max_length: 500ms
      then: # Short press to OPEN/CLOSE blinds and also for setting up
        - script.execute: moveit
    - min_length: 1000ms
      max_length: 3000ms
      then: # Long press to Enter Setting Mode
        - script.execute: setting

cover:
  - platform: template
    name: $upper_devicename
    lambda: |-
      if (id(openclosed)) {
        return COVER_OPEN;
      } else {
        return COVER_CLOSED;
      }
    open_action:
      - script.execute: openscript
    close_action:
      - script.execute: closescript
    device_class: blind

script:
  - id: moveit
    then:
      - if: # If settings variable is on
          condition:
            - lambda: 'return id(settingup);'
          then: # Enter Setting Mode
            - script.execute: setupbutton
          else:
            - if: # If blind is closed
                condition:
                  - lambda: 'return id(openclosed) == 0;'
                then: # Open blind
                  - script.execute: openscript
                else: # Close blind
                  - script.execute: closescript

  - id: openscript
    then:
      - logger.log: "Opening"
      - stepper.set_target: # Send stepper to endstop
          id: $mystepper
          target: !lambda return id(endstop);
      - wait_until: # Wait until endstop reached
          lambda: 'return (id($mystepper).current_position == id(endstop));'
      - globals.set: # Set global to current position
          id: ${mystepper}_global
          value: !lambda return id($mystepper).current_position; 
      - globals.set: # Set toggle to OPEN (No need for 'optimistic mode')
          id: openclosed
          value: '1'

  - id: closescript
    then:
      - logger.log: "Closing"
      - stepper.set_target: # Send stepper to 0
          id: $mystepper
          target: '0'
      - wait_until: # Wait until 0 reached
          lambda: 'return (id($mystepper).current_position == 0);'
      - globals.set: # Set global to current position
          id: ${mystepper}_global
          value: !lambda return id($mystepper).current_position; 
      - globals.set: # Set toggle to CLOSED (No need for 'optimistic mode')
          id: openclosed
          value: '0'

  - id: setting
    then:
      - logger.log: "Entered Settings Mode"
      - globals.set:
          id: settingup
          value:  '1'
      - globals.set:
          id: settingmode
          value:  '1'

  - id: setupbutton
    then:
      - if:
          condition:
            - lambda: 'return (id(settingmode) == 3);'
          then:
            - logger.log: "Pressed Setup Button: Mode 3"
            - stepper.set_target: # Set Stepper position
                id: $mystepper
                target: !lambda return id($mystepper).current_position;
            - globals.set: # Set Endstop Variable
                id: endstop
                value: !lambda return id($mystepper).current_position;
            - globals.set: # Set Global stepper position
                id: ${mystepper}_global
                value: !lambda return id($mystepper).current_position;
            - globals.set: # Reset Setting Mode
                id: settingmode
                value:  '0'
            - globals.set: # Reset Setting Mode
                id: settingup
                value:  '0'
            - globals.set: # Set toggle to Open
                id: openclosed
                value: '1'
            - logger.log: "Exiting Setting Mode"
      - if:
          condition:
            - lambda: 'return (id(settingmode) == 2);'
          then:
            - logger.log: "Pressed Setup Button: Mode 2"
            - stepper.report_position: # Reset Stepper position to 0
                id: $mystepper
                position: '0'
            - stepper.set_target: # Reset Stepper position to 0
                id: $mystepper
                target: '0'
            - globals.set: # Move stepper to 0 (doesn't move it's already there!)
                id: ${mystepper}_global
                value: '0'
            - stepper.set_target: # Reset Stepper position to 72000
                id: $mystepper
                target: '72000'
            - globals.set: # Advance setup to next mode
                id: settingmode
                value:  '3'
      - if:
          condition:
            - lambda: 'return (id(settingmode) == 1);'
          then:
            - logger.log: "Pressed Setup Button: Mode 1"
            - stepper.report_position: # Set Stepper position to 72000, makes it move to 0 (Closed)
                id: $mystepper
                position: '72000'
            - globals.set: # Advance setup to next mode
                id: settingmode
                value:  '2'
2 Likes

This IS a working replacement for the ‘Motor on a roller blind’ sketch. I have it set up on my kitchen blind that has been running the original ‘moarb’ sketch for a year or so.

I couldn’t find a way of sending it OTA, maybe someone else knows a way.
To upload via WIFI, go to this page and follow the instructions, took less than one minute to upload and configure!

Here it is for your scrutinisation :slight_smile:

Also, On GitHub

EDIT: Positioning now works.

# This sketch will add 2 switches named <upper_devicename> Setup Switch and Setup Button
# Use your mobile or tablet and connect to http://<devicename>.local to set up the blind
#   Turn on the Setup Switch to enter Setup Mode and use the Setup Button as shown below to setup blinds.
# 1) Turn on the Setup Switch to enter setup mode
# 2) Press Setup button to start the blind closing
# 3) Press Setup button again when closed and blind starts to open (actually resets the stepper position to 0)
# 4) Press Setup button again when blind is fully open
# 5) Job Done

# This sketch also includes a momentary button on D7 which can be used in the following way
# 1) Press button for > 1 second to enter setup mode
# 2) Press button again to start the blind closing
# 3) Press button again when closed and blind starts to open (actually resets the stepper position to 0)
# 4) Press button again when blind is fully open
# 5) Job Done

# Button is also used to open/close the blind (must be fully open/closed first)

# NOTE:  If you find that your shades are going the wrong way, you can change the pin
#        settings below or reverse the + and – wires for each of the A and B motor
#        pairs on your driver and the motor will spin in the opposite direction.

# The following pins are for the original 'Motor on a Roller Blind'

substitutions:
  devicename: kitchen_blind
  upper_devicename: Kitchen Blind
  mystepper: my_stepper
  pina: D1
  pinb: D3
  pinc: D2
  pind: D4
# Wrong direction? Comment the previous 4 lines and uncomment the following 4 
#  pina: D3
#  pinb: D1
#  pinc: D4
#  pind: D2

esphome:
  name: $devicename
  platform: ESP8266
  board: d1_mini
  esp8266_restore_from_flash: True
  on_boot:
    - priority: -200.0
      then:
      - stepper.report_position: # Set stepper to global variable
          id: $mystepper
          position: !lambda return id(${mystepper}_global);
      - stepper.set_target: # Set stepper to global variable
          id: $mystepper
          target: !lambda return id(${mystepper}_global);

wifi:
  networks: 
    - ssid: !secret wifi
      password: !secret wifi_password

  ap:
    ssid: $upper_devicename
    password: !secret wifi_password

web_server:
  port: 80

logger:

api:
  password: !secret api_password

ota:
  password: !secret ota_password

captive_portal:

stepper:
  - platform: uln2003
    id: $mystepper
    pin_a: $pina
    pin_b: $pinb
    pin_c: $pinc
    pin_d: $pind
    max_speed: 600 steps/s # Speed of the motor
    sleep_when_done: True
    acceleration: inf
    deceleration: inf

globals:
  - id: ${mystepper}_global # Integer for storing the stepper position in case of reboot
    type: int
    restore_value: True
    initial_value: '0'

  - id: openclosed # Boolean to store OPEN/CLOSED state
    type: bool
    restore_value: True
    initial_value: '0'

  - id: endstop # Variable for storing ENDSTOP (how far to move stepper)
    type: int
    restore_value: True
    initial_value: '1000'

  - id: settingmode # Integer for Setup Mode
    type: int
    restore_value: no
    initial_value: '0'

  - id: settingup # Boolean for Setup Active
    type: bool
    restore_value: no
    initial_value: '0'

binary_sensor:
  - platform: gpio
    pin:
      number: D7 # Connect Button to D7 and GND
      mode: INPUT_PULLUP
      inverted: True
    name: Button
    internal: True
    on_click:
    - min_length: 50ms
      max_length: 500ms
      then: # Short press to OPEN/CLOSE blinds and also for setting up
        - script.execute: moveit
    - min_length: 1000ms
      max_length: 3000ms
      then: # Long press to Enter Setting Mode
        - script.execute: setting

switch:
  - platform: template
    name: ${upper_devicename} Setup Switch # Switch to enter Setup Mode
    id: setupswitch
    lambda: |-
      if (id(settingup)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - script.execute: setting
    turn_off_action:
      - logger.log: "Exiting Settings Mode"
      - globals.set:
          id: settingup
          value:  '0'
      - globals.set:
          id: settingmode
          value:  '0'
  - platform: template
    name: ${upper_devicename} Setup Button # Switch to replicate the Physical Button
    id: hasetup
    turn_on_action:
      - if: # If settings variable is on
          condition:
            - lambda: 'return id(settingup);'
          then: # Enter Setting Mode
            - script.execute: setupbutton
            - switch.turn_off: hasetup

cover:
  - platform: template
    name: $upper_devicename
    id: blinded
    lambda: |-
      if (id(openclosed)) {
        return COVER_OPEN;
      } else {
        return COVER_CLOSED;
      }
    open_action:
      - script.execute: openscript
    close_action:
      - script.execute: closescript
    position_action:
#      - script.execute: positional
      then:
        - stepper.set_target:
            id: $mystepper
            target: !lambda return id(endstop) * pos;
        - wait_until: # Wait until 0 reached
            lambda: 'return id($mystepper).current_position == (id(endstop) * pos);'
        - globals.set: # Set global to current position
            id: ${mystepper}_global
            value: !lambda return id($mystepper).current_position; 
    has_position: true
    device_class: blind

script:
  - id: moveit
    then:
      - if: # If settings variable is on
          condition:
            - lambda: 'return id(settingup);'
          then: # Enter Setting Mode
            - script.execute: setupbutton
          else:
            - if: # If blind is closed
                condition:
                  - lambda: 'return id(openclosed) == 0;'
                then: # Open blind
                  - script.execute: openscript
                else: # Close blind
                  - script.execute: closescript

  - id: openscript
    then:
      - logger.log: "Opening"
      - stepper.set_target: # Send stepper to endstop
          id: $mystepper
          target: !lambda return id(endstop);
      - wait_until: # Wait until endstop reached
          lambda: 'return (id($mystepper).current_position == id(endstop));'
      - globals.set: # Set global to current position
          id: ${mystepper}_global
          value: !lambda return id($mystepper).current_position; 
      - globals.set: # Set toggle to OPEN (No need for 'optimistic mode')
          id: openclosed
          value: '1'
      - logger.log:
          format: "The position is being reported as %d, endstop is %d"
          args: [ 'id($mystepper).current_position','id(endstop)' ]

  - id: closescript
    then:
      - logger.log: "Closing"
      - stepper.set_target: # Send stepper to 0
          id: $mystepper
          target: '0'
      - wait_until: # Wait until 0 reached
          lambda: 'return (id($mystepper).current_position == 0);'
      - globals.set: # Set global to current position
          id: ${mystepper}_global
          value: !lambda return id($mystepper).current_position; 
      - globals.set: # Set toggle to CLOSED (No need for 'optimistic mode')
          id: openclosed
          value: '0'

  - id: setting
    then:
      - logger.log: "Entered Settings Mode"
      - globals.set:
          id: settingup
          value:  '1'
      - globals.set:
          id: settingmode
          value:  '1'

  - id: setupbutton
    then:
      - if:
          condition:
            - lambda: 'return (id(settingmode) == 3);'
          then:
            - logger.log: "Pressed Setup Button: Mode 3"
            - stepper.set_target: # Set Stepper position
                id: $mystepper
                target: !lambda return id($mystepper).current_position;
            - globals.set: # Set Endstop Variable
                id: endstop
                value: !lambda return id($mystepper).current_position;
            - globals.set: # Set Global stepper position
                id: ${mystepper}_global
                value: !lambda return id($mystepper).current_position;
            - globals.set: # Reset Setting Mode
                id: settingmode
                value:  '0'
            - globals.set: # Reset Setting Mode
                id: settingup
                value:  '0'
            - globals.set: # Set toggle to Open
                id: openclosed
                value: '1'
            - logger.log: "Exiting Setting Mode"
      - if:
          condition:
            - lambda: 'return (id(settingmode) == 2);'
          then:
            - logger.log: "Pressed Setup Button: Mode 2"
            - stepper.report_position: # Reset Stepper position to 0
                id: $mystepper
                position: '0'
            - stepper.set_target: # Reset Stepper position to 0
                id: $mystepper
                target: '0'
            - globals.set: # Move stepper to 0 (doesn't move it's already there!)
                id: ${mystepper}_global
                value: '0'
            - stepper.set_target: # Reset Stepper position to 72000
                id: $mystepper
                target: '72000'
            - globals.set: # Advance setup to next mode
                id: settingmode
                value:  '3'
      - if:
          condition:
            - lambda: 'return (id(settingmode) == 1);'
          then:
            - logger.log: "Pressed Setup Button: Mode 1"
            - stepper.report_position: # Set Stepper position to 72000, makes it move to 0 (Closed)
                id: $mystepper
                position: '72000'
            - globals.set: # Advance setup to next mode
                id: settingmode
                value:  '2'

6 Likes

Hey @RoadkillUK
Awesome work! Thank you so much for sharing!
I tested this out and wanted to report a few things and ask a few questions :slight_smile:

  1. ESPHome is concerned about the ‘position_action’ of the cover and says is not supported. I had to comment the whole section out to get it validated.
  2. During the setup stepper starts moving the blind up instead of down in the first place in both pin configurations (readme says the opposite). The only problem with this is that 0 is always set for a fully open position from what I understand and so HA reverts open and close buttons.
  3. Is there no stop action? Unless ‘position_action’ is meant to achieve the same thing.
  4. Is there a way to configure open and closed positions steps manually?
  5. And is there a way to adjust this for two blinds? :wink:
  1. Previously it would only compile on the ESPHome 1.15 Dev version, however there has just been an updated version of ESPHome Beta which is what I usually use.

  2. Oh, I’m surprised, I’ve tested it and changing the pins should work, can anyone else confirm this please? (silly question, but have you wired the motor the same way as in the original MOARB?)

  3. I’ve just added one, use the Github link.

  4. Position now works, I have added the following to Lovelace and it shows a slider to adjust the blind, you will also need Lovelace Slider Entity.

  - type: entities
    name: Roller Blind
    min: 0
    max: 100
    step: 10
    entities:
      - cover.roller_blind
      - type: custom:slider-entity-row
        entity: cover.roller_blind
  1. I’m sure there’s a way, but I’m not doing it :wink:

Regarding the positioning, it’s not 100%. When you press the stop button, the slider doesn’t change to the correct position. When not fully open or closed, it will only allow either up or down, this is to do with the single button function as it needs to know which way to go.

If anyone wants to take a look and improve it then please do :slight_smile:

I think I’ll have to rewrite some of this as it’s mainly ‘functional’

2 Likes

Thanks @RoadkillUK

I’m using ESPhome 1.14, please disregard the rotation direction, I think it was late night and I messed up the connection after all experimenting. Thank you for everything else but I guess I’ll be moving on :wink: Need to configure 4 blinds (2 x 1 ESP). Will try to figure out your code for the parts I’m struggling with if you don’t mind :wink:

Right, I’ve done a big rewrite and it took me ages :slight_smile: but at least I’m learning.

It still only controls one blind but it does it well, has positioning etc. and is a straight swap for MOARB.

Github Repository

1 Like