About eeprom or etc. memorization for roller blind

Hello, in the smart curtain system I have been using for a long time, I can only raise and lower the curtain. But when the power is cut off, I want the motor to remember where it left off and continue. Plus I want to be able to raise and lower the curtain to the desired %. Can you help me with this?

My tools in the curtain system.
Nema 17 stepper motor
A4988 motor driver board
Esp32-wroom-32 programming board
LM2596 Regulator
3 buttons
DC 12v adapter

Self-prepared coding

substitutions:
  devicename: yatakodasiperde
  mystepper: my_stepper
  speed: 1200 steps/s

esphome:
  name: $devicename
  platform: ESP32
  board: nodemcu-32s
  libraries:
    - EEPROM

# Enable logging
logger:
  level: DEBUG
  logs:
    esphome.core: DEBUG
    esphome.components: DEBUG

# Enable Home Assistant API
api:
  services:
  - service: control_stepper
    variables:
      target: int
    then:
      - stepper.set_target:
          id: my_stepper
          target: !lambda 'return target;'
ota:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: true
  manual_ip:
    static_ip: 192.168.1.69
    gateway: 192.168.1.1
    subnet: 255.255.255.0
    dns1: 192.168.1.1

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Akilli Perde"
    password: "password"
  
captive_portal:

web_server:
  port: 80

stepper:
  - platform: a4988
    id: $mystepper
    step_pin: GPIO19
    dir_pin:
      number: GPIO18
      inverted: true
    max_speed: ${speed}
    acceleration: inf
    deceleration: inf

sensor:
  - platform: wifi_signal
    name: "WiFi Sinyali yatakodasiperde"
    update_interval: 60s

text_sensor:
  - platform: wifi_info
    ip_address:
      icon: "mdi:ip"
      name: "IP yatakodasiperde"
    ssid:
      name: "SSID yatakodasiperde"
      icon: "mdi:access-point-network"
    bssid:
      name: "BSSID yatakodasiperde"
      icon: "mdi:access-point-network"

output:
  - platform: gpio
    pin: GPIO21
    inverted: true
    id: enable_pin

binary_sensor:
  - platform: gpio
    id: perde_kaldirma_button
    name: Perdeyi kaldir
    pin:
      number: GPIO12
      mode: INPUT_PULLUP
      inverted: true
    on_press:
      then:
        - output.turn_on: enable_pin
        - if:
            condition:
              lambda: 'return id($mystepper).current_position != 35000;'
            then:
              - stepper.set_target:
                  id: $mystepper
                  target: 35000
              - delay: 1ms
              - while:
                  condition:
                    lambda: 'return id($mystepper).current_position != 35000;'
                  then:
                    - delay: 1ms
        - delay: 1ms
        - output.turn_off: enable_pin

  - platform: gpio
    id: perde_indirme_button
    name: Perdeyi indir
    pin:
      number: GPIO13
      mode: INPUT_PULLUP
      inverted: true
    on_press:
      then:
        - output.turn_on: enable_pin
        - if:
            condition:
              lambda: 'return id($mystepper).current_position != 0;'
            then:
              - stepper.set_target:
                  id: $mystepper
                  target: 0
              - delay: 1ms
              - while:
                  condition:
                    lambda: 'return id($mystepper).current_position != 0;'
                  then:
                    - delay: 1ms
        - delay: 1ms
        - output.turn_off: enable_pin
        - cover.template.publish:
            id: blinded
            position: !lambda 'return id($mystepper).current_position;'

  - platform: gpio
    id: perde_durdurma_button
    name: Perdeyi durdur
    pin:
      number: GPIO14
      mode: INPUT_PULLUP
      inverted: true
    on_press:
      then:
        - output.turn_off: enable_pin
        - stepper.set_target:
            id: $mystepper
            target: !lambda 'return id($mystepper).current_position;'

cover:
  - platform: template
    name: Yatakodasi-Perde
    id: blinded

    open_action:
      - output.turn_on: enable_pin
      - if:
          condition:
            lambda: 'return id($mystepper).current_position != 35000;'
          then:
            - stepper.set_target:
                id: $mystepper
                target: 35000
            - delay: 1ms
            - while:
                condition:
                  lambda: 'return id($mystepper).current_position != 35000;'
                then:
                  - delay: 1ms
      - delay: 1ms
      - output.turn_off: enable_pin

    close_action:
      - output.turn_on: enable_pin
      - if:
          condition:
            lambda: 'return id($mystepper).current_position != 0;'
          then:
            - stepper.set_target:
                id: $mystepper
                target: 0
            - delay: 1ms
            - while:
                condition:
                  lambda: 'return id($mystepper).current_position != 0;'
                then:
                  - delay: 1ms
      - delay: 1ms
      - output.turn_off: enable_pin

    stop_action:
      - output.turn_off: enable_pin
      - stepper.set_target:
          id: $mystepper
          target: !lambda 'return id($mystepper).current_position;'
      - cover.template.publish:
          id: blinded
          position: !lambda 'return id($mystepper).current_position;'

The curtain card coding I’m trying to use.
[GitHub - tungmeister/hass-blind-card: Blind card for Home Assistant Lovelace UI]

type: 'custom:blind-card'
title: My blinds
entities:
  - entity: cover.yatakodasiperde
    name: Left blind
    buttons_position: left
    title_position: bottom
    blind_color: '#FFD580'

Take a look at my repo

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

It looks like you’ll be able to change the stepper motor and board, I’m a bit tired atm, otherwise I’ll check in tomorrow.

It should work with that card too.

1 Like

Thank you @RoadkillUK , the purpose of using nema17 motor is because my curtain is big and heavy. As a driver, I chose these components because I cannot find many types in my country.

Thank you very much for your interest. I am waiting for your return after you rest.

I meant for you to change the code, it’s pretty easy, just copy it from your code.

Hello @RoadkillUK , I have adapted your coding and the system is loading successfully. But I’m trying to raise or lower the curtain, it doesn’t work. I think I’m missing something.

Yeah, you could post your code :wink: give us a clue.

I’ll take a look. put ``` before and after the code so it formats correctly.

The way I adapted the code.

substitutions:
  devicename: yatakodasiperde
  mystepper: my_stepper
  speed: 1200 steps/s
  reportin: "0"

esphome:
  name: $devicename
  platform: ESP32
  board: nodemcu-32s
  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);
      - if: # If blind is Closed
          condition:
            - lambda: 'return id(${mystepper}_global) == 0;'
          then: # Publish state etc.
            - cover.template.publish:
                id: blinded
                state: CLOSED
                current_operation: IDLE
      - if: # If blind is Open
          condition:
            - lambda: 'return id(${mystepper}_global) == id(endstop);'
          then: # Publish state etc.
            - cover.template.publish:
                id: blinded
                state: OPEN
                current_operation: IDLE
      - if: # If blind is Neither
          condition:
            - lambda: 'return (id(${mystepper}_global) != 0) && (id(${mystepper}_global) != id(endstop));'
          then: #  # Publish state etc.
            - cover.template.publish:
                id: blinded
                position: !lambda 'return (float(float(id($mystepper).current_position) / float(id(endstop))));' 
                current_operation: IDLE

wifi:
  ssid: !secret wifi_ssid_2mi
  password: !secret wifi_password
  fast_connect: true
  manual_ip:
    static_ip: 192.168.1.69
    gateway: 192.168.1.1
    subnet: 255.255.255.0
    dns1: 192.168.1.1

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Akilli Perde"
    password: !secret wifi_password

web_server:
  port: 80

# Enable logging
logger:
#  level: DEBUG
#  logs:
#    esphome.core: DEBUG
#    esphome.components: DEBUG# Enable Home Assistant API
api:

ota:

captive_portal:

stepper:
  - platform: a4988
    id: $mystepper
    step_pin: GPIO19
    dir_pin:
      number: GPIO18
      inverted: true
    max_speed: ${speed}
    acceleration: inf
    deceleration: inf

output:
  - platform: gpio
    pin: GPIO21
    inverted: true
    id: enable_pin

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: reportin # Boolean for reporting the position of blind when moving (See testblind.yaml)
    type: bool
    restore_value: no
    initial_value: '${reportin}'

text_sensor:
  - platform: version
    name: ${devicename} ESPHome Version
    hide_timestamp: true

  - platform: wifi_info
    ip_address:
      name: "IP ${devicename}"
      icon: "mdi:ip"
    ssid:
      name: "SSID ${devicename}"
      icon: "mdi:access-point-network"
    bssid:
      name: "BSSID ${devicename}"
      icon: "mdi:access-point-network"
    mac_address:
      name: "MAC ${devicename}"
      icon: "mdi:identifier"

  - platform: template
    name: ${devicename} Friendly Name
    icon: 'mdi:information-outline'
    lambda: |-
      return {"$devicename"};

binary_sensor:
  - platform: gpio
    id: perde_kaldirma_button
    name: Perdeyi kaldir
    pin:
      number: GPIO12
      mode: INPUT_PULLUP
      inverted: true
    on_press:
      then:
        - output.turn_on: enable_pin
        - if:
            condition:
              lambda: 'return id($mystepper).current_position != 35000;'
            then:
              - stepper.set_target:
                  id: $mystepper
                  target: 35000
              - delay: 1ms
              - while:
                  condition:
                    lambda: 'return id($mystepper).current_position != 35000;'
                  then:
                    - delay: 1ms
        - delay: 1ms
        - output.turn_off: enable_pin

  - platform: gpio
    id: perde_indirme_button
    name: Perdeyi indir
    pin:
      number: GPIO13
      mode: INPUT_PULLUP
      inverted: true
    on_press:
      then:
        - output.turn_on: enable_pin
        - if:
            condition:
              lambda: 'return id($mystepper).current_position != 0;'
            then:
              - stepper.set_target:
                  id: $mystepper
                  target: 0
              - delay: 1ms
              - while:
                  condition:
                    lambda: 'return id($mystepper).current_position != 0;'
                  then:
                    - delay: 1ms
        - delay: 1ms
        - output.turn_off: enable_pin
        - cover.template.publish:
            id: blinded
            position: !lambda 'return id($mystepper).current_position;'

  - platform: gpio
    id: perde_durdurma_button
    name: Perdeyi durdur
    pin:
      number: GPIO14
      mode: INPUT_PULLUP
      inverted: true
    on_press:
      then:
        - output.turn_off: enable_pin
        - stepper.set_target:
            id: $mystepper
            target: !lambda 'return id($mystepper).current_position;'

sensor:
  - platform: wifi_signal
    name: "WiFi Sinyali ${devicename}"
    update_interval: 60s

switch:
  - platform: template
    name: ${devicename} Setup Switch # Switch to enter Setup Mode
    id: setupswitch
    lambda: |-
      if (id(settingmode) != 0) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      then:
        - logger.log: "Entered Settings Mode"
        - globals.set:
            id: settingmode
            value:  '1'
    turn_off_action:
      then:
        - logger.log: "Exiting Settings Mode"
        - globals.set:
            id: settingmode
            value:  '0'

button:
  - platform: template
    name: ${devicename} Setup Button # Software Button to replicate the Physical Button
    id: hasetup
    on_press:
      - if: # If settings variable is on
          condition:
            - lambda: 'return id(settingmode) != 0;'
          then: # Enter Setting Mode
            - script.execute: setupbutton

cover:
  - platform: template
    name: $devicename
    id: blinded
    open_action:
      then:
        - logger.log: "Opening"
        - logger.log:
            format: "Endstop is: %d"
            args: [ 'id(endstop)' ]
        - stepper.set_target: # Send stepper to endstop
            id: $mystepper
            target: !lambda return id(endstop);
        - if:
            condition: 
              lambda: 'return id(reportin) == 1;' # If Reporting is Selected
            then:
              - while:
                  condition:
                    lambda: 'return id($mystepper).current_position != id(endstop);'
                  then:
                    - cover.template.publish:
                        id: blinded
                        position: !lambda 'return (float(float(id($mystepper).current_position) / float(id(endstop))));' 
                        current_operation: OPENING
                    - delay: 1000 ms
            else:
              - cover.template.publish:
                  id: blinded
                  position: !lambda 'return (float(float(id($mystepper).current_position) / float(id(endstop))));' 
                  current_operation: OPENING
              - 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'
        - cover.template.publish:
            id: blinded
            state: OPEN 
            current_operation: IDLE
    close_action:
      then:
        - logger.log: "Closing"
        - stepper.set_target: # Send stepper to 0
            id: $mystepper
            target: '0'
        - if:
            condition: 
              lambda: 'return id(reportin) == 1;'
            then:
              - while:
                  condition:
                    lambda: 'return id($mystepper).current_position != 0;'
                  then:
                    - cover.template.publish:
                        id: blinded
                        position: !lambda 'return (float(float(id($mystepper).current_position) / float(id(endstop))));' 
                        current_operation: CLOSING
                    - delay: 1000 ms
            else:
              - cover.template.publish:
                  id: blinded
                  position: !lambda 'return (float(float(id($mystepper).current_position) / float(id(endstop))));' 
                  current_operation: CLOSING
              - wait_until: # Wait until endstop 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'
        - cover.template.publish:
            id: blinded
            state: CLOSED
            current_operation: IDLE
    position_action:
      then:
        - stepper.set_target:
            id: $mystepper
            target: !lambda return int(id(endstop) * pos);
        - if:
            condition: 
              lambda: 'return id(reportin) == 1;'
            then:
              - while:
                  condition:
                    lambda: 'return id($mystepper).current_position != int(id(endstop) * pos);'
                  then:
                    - cover.template.publish:
                        id: blinded
                        position: !lambda 'return (float(float(id($mystepper).current_position) / float(id(endstop))));' 
                    - delay: 1000 ms
            else:
              - wait_until: # Wait until endstop reached
                  lambda: 'return id($mystepper).current_position == (int(id(endstop) * pos));'
        - globals.set: # Set global to current position
            id: ${mystepper}_global
            value: !lambda return id($mystepper).current_position; 
        - cover.template.publish:
            id: blinded
            position: !lambda 'return (float(float(id($mystepper).current_position) / float(id(endstop))));' 
            current_operation: IDLE
    stop_action:
      then:
        - stepper.set_target:
            id: $mystepper
            target: !lambda return id($mystepper).current_position;
        - globals.set: # Set global to current position
            id: ${mystepper}_global
            value: !lambda return id($mystepper).current_position;
        - cover.template.publish:
            id: blinded
            position: !lambda 'return (float(float(id($mystepper).current_position) / float(id(endstop))));' 
            current_operation: IDLE
    has_position: true
    device_class: blind

script:
  - id: setupbutton
    then:
      - if:
          condition:
            - lambda: 'return (id(settingmode) == 3);'
          then:
            - logger.log: "Pressed Setup Button: Mode 3"
            - logger.log: "Blind is now set up"
            - logger.log:
                format: "Endstop is: %d"
                args: [ 'id(endstop)' ]
            - 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: # Set toggle to Open
                id: openclosed
                value: '1'
            - cover.template.publish:
                id: blinded
                state: OPEN 
                current_operation: IDLE
            - logger.log: "Exiting Setting Mode"
      - if:
          condition:
            - lambda: 'return (id(settingmode) == 2);'
          then:
            - logger.log: "Pressed Setup Button: Mode 2"
            - logger.log: "Blind is Opening, Press button when fully open"
            - 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 35000
                id: $mystepper
                target: '35000'
            - 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"
            - logger.log: "Blind is Closing, Press button when fully closed"
            - stepper.report_position: # Set Stepper position to 35000, makes it move to 0 (Closed)
                id: $mystepper
                position: '35000'
            - globals.set: # Advance setup to next mode
                id: settingmode
                value:  '2'

It would seem you have added your own version of a sleep/enable pin. Looking at this, there is a enable pin option already.

If you change your stepper config to this below and ensure you need the pins inverted. Then delete all your additional enable/disable stuff. It might work :wink:

There may be more issues, I don’t think so, but one step at a time.

Good Luck.

stepper:
  - platform: a4988
    id: $mystepper
    step_pin: GPIO19
    dir_pin:
      number: GPIO18
      inverted: true
    max_speed: ${speed}
    acceleration: inf
    deceleration: inf
    sleep_pin: GPIO21

Thank you for your help. But I pasted the stepper code you gave. I could not understand the additional enable/disable items in the coding. So I deleted the following codes. The system did not work again. Are these the codes to be deleted?

- output.turn_on: enable_pin
- output.turn_off: enable_pin