Motor on a roller blind - ESPHome version?

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