Odwrócenie działania sterownika rolet na A4988

Witam.
Znalazłem na innym forum kod sterowania silnikiem krokowym na A4988. Generalnie kod jest ok ale fizyczny endstop jest w nim umieszczony przy całkowitym otwarciu. Podczas restartu kod wykonuje test pozycji w związku z tym bez względu na aktualną pozycję wykonuje całkowite otwarcie i powrót. Autor zaprojektował go do sterowania roletami więc u niego nie stanowiło to problemu. U mnie sterownik ma służyć do automatycznego uchylania okna w szklarni w związku z tym takie rozwiązanie nie wchodzi w grę bo jeśli okno będzie fizycznie zaryglowane przy próbie otwarcia nastąpi zablokowanie silnika w najlepszym wypadku.
W związku z tym potrzebuję przenieść endstop do pozycji zamknięte. Próbowałem po prostu zamienić słowo “open” z “close”, o dziwo zadziałało ale tylko podczas pełnego otwierania/zamykania. Przy próbie ustawienia w pozycji np 10% działa jakby odwrotnie, później nie wraca do właściwej pozycji. Proszę o pomoc. Oto kod:

esphome:
  name: nemav2
  friendly_name: NemaV2
  on_boot:
    priority: 250
    then:
      - delay: 10s
      - if:
          condition:
#           shade thinks it's at 0 but endstop is not triggered
            lambda: "if ((id(stepper_position) == 0) && (id(endstop).state == 0)) { return 1; } else { return 0; }"
          then:
#             open to blind to calibrate the endstop, remember the steps to do so, put the blind back to where it was
            - globals.set:
                id: stepper_state
                value: '1'
#             set the assumed stepper position to stepper_steps + 4000 and have it travel to 0
            - stepper.report_position:
                id: my_stepper
                position: !lambda "return id(stepper_steps) + 4000;"
            - stepper.set_target:
                id: my_stepper
                target: 0
#             wait until the stepper is no longer moving
            - wait_until:
                - lambda: "if (id(my_stepper).current_position == id(my_stepper).target_position) { return 1; } else { return 0; }"
#             now move the blind back to however many steps it took to reach the endstop
            - stepper.set_target:
                id: my_stepper
                target: !lambda "return id(stepper_steps) + 4000 - id(stepper_prevpos);"
            - globals.set:
                id: stepper_state
                value: '1'
            - delay: !lambda "return ((id(my_stepper).target_position - 834) * 2) + 3334;"
            - globals.set:
                id: stepper_position
                value: !lambda "return id(my_stepper).target_position;"
            - globals.set:
                id: stepper_state
                value: '0'
esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "4SHnfj/B2NW0aPI+IRu67+Cqd9dF2Q4LyHSzgabqjz8="

ota:
  password: "84fa3c54bed2661a99656cac430ac015"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Nemav2 Fallback Hotspot"
    password: "FOR6F0xwBjBp"

captive_portal:

web_server:
  port: 80


globals:
  - id: stepper_steps
    type: int
    initial_value: '0'
    restore_value: true
  - id: stepper_prevpos
    type: float
    initial_value: '0'
  - id: stepper_position
    type: float
    initial_value: '0'
  - id: stepper_state
    type: int
    initial_value: '0'
  - id: calib_state
    type: int
    initial_value: '0'

sensor:
  - platform: uptime
    name: "Node uptime"

switch:
  - platform: restart
    name: "Restart"

binary_sensor:
  - platform: gpio
    pin:
      number: 14
      mode: INPUT_PULLUP
    name: "Open"
    filters:
        - invert:
        - delayed_on: 10ms
    on_click:
    - min_length: 50ms
      max_length: 1000ms
      then:
        - lambda: |
            if ((id(stepper_state) == 0) && (id(endstop).state == 0)) {
              //shade is stopped and not already open at endstop
              id(blind).open();
            } else {
              //shade is moving
              id(blind).stop();
            }

  - platform: gpio
    pin:
      number: 32
      mode: INPUT_PULLUP
    name: "Close"
    filters:
        - invert:
        - delayed_on: 10ms
    on_click:
    - min_length: 50ms
      max_length: 1000ms
      then:
        - lambda: |
            if ((id(stepper_state) == 0) && (id(stepper_position) < id(stepper_steps))) {
              //shade is stopped and not already closed
              id(blind).close();
            } else {
              //shade is moving
              id(blind).stop();
            }
#     a 10s press on the down button will enter calibration mode
#     shutter goes all the way up and then travels down until a button is pressed
#     where shutter is stopped is the new stepper_steps value
    - min_length: 5000ms
      max_length: 15000ms
      then:
        - lambda: |
            id(calib_state) = 1;
            if ((id(stepper_state) == 0) && (id(endstop).state == 0)) {
              //shade is stopped and not already open at endstop
              id(blind).open();
            } else if ((id(stepper_state) == 0) && (id(endstop).state == 1)) {
              //shade is stopped and already open at endstop
              id(blind).close();
            }

  - platform: gpio
    pin:
      number: 33
      mode: INPUT_PULLUP
    name: "Endstop"
    id: endstop
    filters:
      - delayed_on_off: 300ms
    on_press:
      then:
#         hit the endstop, store the current steps value and reset stepper position to 0
        - globals.set:
            id: stepper_prevpos
            value: !lambda "return id(my_stepper).current_position;"
        - stepper.report_position:
            id: my_stepper
            position: 0
#         just in case, also set the target to 0
        - stepper.set_target:
            id: my_stepper
            target: 0
        - lambda: |
            if (id(stepper_state) == 1) {
              //shade is moving
              id(blind).stop();
            }

cover:
  - platform: template
    name: "Blind"
    id: blind
    open_action:
      - globals.set:
          id: stepper_state
          value: '1'
#       overshoot past the endstop to make sure it's hit (endstop will stop blind)
      - stepper.set_target:
          id: my_stepper
          target: -4000
#       wait until the stepper is no longer moving
      - wait_until:
          - lambda: "if (id(my_stepper).current_position == id(my_stepper).target_position) { return 1; } else { return 0; }"
      - globals.set:
          id: stepper_position
          value: !lambda "return id(my_stepper).target_position;"
      - globals.set:
          id: stepper_state
          value: '0'
      - if:
          condition:
#           reached past the 'top' but still haven't triggered the endstop yet
            lambda: "if(id(stepper_position) == -4000 && id(endstop).state == 0) { return 1; } else { return 0; }"
          then:
#           reset stepper position to 0
          - stepper.report_position:
              id: my_stepper
              position: 0
#           just in case, also set the target to 0
          - stepper.set_target:
              id: my_stepper
              target: 0
    close_action:
      - globals.set:
          id: stepper_state
          value: '1'
      - stepper.set_target:
          id: my_stepper
#         for calibration the number of steps is doubled
          target: !lambda "if(id(calib_state) == 0) { return id(stepper_steps); } else { return id(stepper_steps) * 2; }"
#       wait until the stepper is no longer moving
      - wait_until:
          - lambda: "if (id(my_stepper).current_position == id(my_stepper).target_position) { return 1; } else { return 0; }"
      - globals.set:
          id: stepper_position
          value: !lambda "return id(my_stepper).target_position;"
      - globals.set:
          id: stepper_state
          value: '0'
      - lambda: |
          if (id(calib_state) == 1) {
            //reached double the current stepper distance
            id(stepper_steps) = id(stepper_position);
            //continue with calibration
            id(blind).close();
          }
    stop_action:
      - stepper.set_target:
          id: my_stepper
          target: !lambda "return id(my_stepper).current_position;" 
      - globals.set:
          id: stepper_position
          value: !lambda "return id(my_stepper).current_position;"
      - globals.set:
          id: stepper_state
          value: '0'
      - lambda: |
          if (id(calib_state) == 1 && id(endstop).state == 0) {
            //stopping at something other than the endstop
            id(calib_state) = 0;
            id(stepper_steps) = id(stepper_position);
          }
      - cover.template.publish:
          id: blind
          position: !lambda "return 1 - (id(stepper_position) / id(stepper_steps));"
    position_action:
      - stepper.set_target:
          id: my_stepper
          target: !lambda "return id(stepper_steps) * (1 - pos);"
    optimistic: true
    assumed_state: true
    has_position: true
    

stepper:
  - platform: a4988
    id: my_stepper
    dir_pin: 
      number: 26
    step_pin: 25
    max_speed: 2000

    # Optional:
    sleep_pin: 27
    acceleration: 1500
    deceleration: 3000

Podejrzewam, że słowem klucz z dokumentacji ESPHome może być inverted

inverted (Optional, boolean): If the output should be treated as inverted. Defaults to false.

Autor wspominał o tym ale jestem na tyle zielony, że nie wiem gdzie to wstawić. Jeśli odwrócę działanie silnika to teoretycznie powinno być ok tylko wtedy zamykanie jest otwieraniem, a otwieranie zamykaniem. Czy dodatkowo zamian funkcji open z close rozwiąże problem?

Przy okazji czy ktoś mógłby zerknąć w poprawność kompilacji? Wyrzuca sporo ostrzeżeń przy kompilacji.

Tak, prawdopodobnie tak będzie (zgrubna analiza zdaje się to potwierdzać, ale musisz jeszcze sprawdzić pozostałe procedury, bo ich działanie też powinno zostać zmienione), masz o wiele łatwiej posiadając sprzęt, bo możesz przetestować.
Na pełną analizę nie mam mocy przerobowych - zajęłoby mi to zbyt wiele czasu…

Wrzuć logi, to jest forum - nikt nikomu niczego nie obiecuje, bierz pod uwagę, że porady mogą być błędne, dokumentacja ESPHome jest dość dobra, ale i tak czasem trzeba przeczytać po parę razy.

W kodzie jest jakiś błąd. Poskładałem wszystko docelowo i dopiero teraz zauważyłem, że nawet na podstawowym kodzie z każdym otwarciem silnik zatrzymuje się coraz dalej od endstopa. Jedyne co zmieniłem to dodałem delay do endstopa ponieważ bez tego zmieniał co chwilę swój stan (jest do optyczny endstop w rodzaju stosowanych w drukarkach 3D DIY) oraz dodałem inverted: true do configa sterownika A4988. Jakieś pomysły?

Wklejam ostrzeżenia o których była mowa wcześniej:

/config/esphome/nemav2.yaml: In lambda function:
/config/esphome/nemav2.yaml:114:21: warning: 'void esphome::cover::Cover::open()' is deprecated: open() is deprecated, use make_call().set_command_open() instead. [-Wdeprecated-declarations]
               id(blind).open();
                     ^
In file included from src/esphome/core/controller.h:14,
                 from src/esphome/components/api/api_server.h:4,
                 from src/esphome/components/api/api_connection.h:6,
                 from src/esphome.h:4,
                 from src/main.cpp:3:
src/esphome/components/cover/cover.h:134:8: note: declared here
   void open();
        ^~~~
/config/esphome/nemav2.yaml:117:21: warning: 'void esphome::cover::Cover::stop()' is deprecated: stop() is deprecated, use make_call().set_command_stop() instead. [-Wdeprecated-declarations]
               id(blind).stop();
                     ^
In file included from src/esphome/core/controller.h:14,
                 from src/esphome/components/api/api_server.h:4,
                 from src/esphome/components/api/api_connection.h:6,
                 from src/esphome.h:4,
                 from src/main.cpp:3:
src/esphome/components/cover/cover.h:146:8: note: declared here
   void stop();
        ^~~~
/config/esphome/nemav2.yaml: In lambda function:
/config/esphome/nemav2.yaml:135:22: warning: 'void esphome::cover::Cover::close()' is deprecated: close() is deprecated, use make_call().set_command_close() instead. [-Wdeprecated-declarations]
               id(blind).close();
                      ^
In file included from src/esphome/core/controller.h:14,
                 from src/esphome/components/api/api_server.h:4,
                 from src/esphome/components/api/api_connection.h:6,
                 from src/esphome.h:4,
                 from src/main.cpp:3:
src/esphome/components/cover/cover.h:140:8: note: declared here
   void close();
        ^~~~~
/config/esphome/nemav2.yaml:138:21: warning: 'void esphome::cover::Cover::stop()' is deprecated: stop() is deprecated, use make_call().set_command_stop() instead. [-Wdeprecated-declarations]
               id(blind).stop();
                     ^
In file included from src/esphome/core/controller.h:14,
                 from src/esphome/components/api/api_server.h:4,
                 from src/esphome/components/api/api_connection.h:6,
                 from src/esphome.h:4,
                 from src/main.cpp:3:
src/esphome/components/cover/cover.h:146:8: note: declared here
   void stop();
        ^~~~
/config/esphome/nemav2.yaml: In lambda function:
/config/esphome/nemav2.yaml:150:21: warning: 'void esphome::cover::Cover::open()' is deprecated: open() is deprecated, use make_call().set_command_open() instead. [-Wdeprecated-declarations]
               id(blind).open();
                     ^
In file included from src/esphome/core/controller.h:14,
                 from src/esphome/components/api/api_server.h:4,
                 from src/esphome/components/api/api_connection.h:6,
                 from src/esphome.h:4,
                 from src/main.cpp:3:
src/esphome/components/cover/cover.h:134:8: note: declared here
   void open();
        ^~~~
/config/esphome/nemav2.yaml:153:22: warning: 'void esphome::cover::Cover::close()' is deprecated: close() is deprecated, use make_call().set_command_close() instead. [-Wdeprecated-declarations]
               id(blind).close();
                      ^
In file included from src/esphome/core/controller.h:14,
                 from src/esphome/components/api/api_server.h:4,
                 from src/esphome/components/api/api_connection.h:6,
                 from src/esphome.h:4,
                 from src/main.cpp:3:
src/esphome/components/cover/cover.h:140:8: note: declared here
   void close();
        ^~~~~
/config/esphome/nemav2.yaml: In lambda function:
/config/esphome/nemav2.yaml:180:21: warning: 'void esphome::cover::Cover::stop()' is deprecated: stop() is deprecated, use make_call().set_command_stop() instead. [-Wdeprecated-declarations]
               id(blind).stop();
                     ^
In file included from src/esphome/core/controller.h:14,
                 from src/esphome/components/api/api_server.h:4,
                 from src/esphome/components/api/api_connection.h:6,
                 from src/esphome.h:4,
                 from src/main.cpp:3:
src/esphome/components/cover/cover.h:146:8: note: declared here
   void stop();
        ^~~~
/config/esphome/nemav2.yaml: In lambda function:
/config/esphome/nemav2.yaml:239:22: warning: 'void esphome::cover::Cover::close()' is deprecated: close() is deprecated, use make_call().set_command_close() instead. [-Wdeprecated-declarations]
             id(blind).close();
                      ^
In file included from src/esphome/core/controller.h:14,
                 from src/esphome/components/api/api_server.h:4,
                 from src/esphome/components/api/api_connection.h:6,
                 from src/esphome.h:4,
                 from src/main.cpp:3:
src/esphome/components/cover/cover.h:140:8: note: declared here
   void close();

Edit:

Po różnych próbach udało mi się uzyskać efekt jaki chciałem. Endstop przy zamknięciu oraz prawidłowe wskazania poziomu zamknięcia w procentach, dodatkowo naprawiłem błąd który powodował brak dojazdu do endstopa po odwróceniu programowym obrotów silnika.
Będę jeszcze testował ale na szybko wszystko zadziałało już na oknie.
Okno w szklarni będzie uchylane automatycznie w zależności od temperatury.

Mój kod:

esphome:
  name: nemav2
  friendly_name: NemaV2
  on_boot:
    priority: 250
    then:
      - delay: 10s
      - if:
          condition:
#           shade thinks it's at 0 but endstop is not triggered
            lambda: "if ((id(stepper_position) == 0) && (id(endstop).state == 0)) { return 1; } else { return 0; }"
          then:
#             open to blind to calibrate the endstop, remember the steps to do so, put the blind back to where it was
            - globals.set:
                id: stepper_state
                value: '1'
#             set the assumed stepper position to stepper_steps + 4000 and have it travel to 0
            - stepper.report_position:
                id: my_stepper
                position: !lambda "return id(stepper_steps) + 4000;"
            - stepper.set_target:
                id: my_stepper
                target: 0
#             wait until the stepper is no longer moving
            - wait_until:
                - lambda: "if (id(my_stepper).current_position == id(my_stepper).target_position) { return 1; } else { return 0; }"
#             now move the blind back to however many steps it took to reach the endstop
            - stepper.set_target:
                id: my_stepper
                target: !lambda "return id(stepper_steps) + 4000 - id(stepper_prevpos);"
            - globals.set:
                id: stepper_state
                value: '1'
            - delay: !lambda "return ((id(my_stepper).target_position - 834) * 2) + 3334;"
            - globals.set:
                id: stepper_position
                value: !lambda "return id(my_stepper).target_position;"
            - globals.set:
                id: stepper_state
                value: '0'
esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "4SHnfj/B2NW0aPI+IRu67+Cqd9dF2Q4LyHSzgabqjz8="

ota:
  password: "84fa3c54bed2661a99656cac430ac015"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Nemav2 Fallback Hotspot"
    password: "FOR6F0xwBjBp"

captive_portal:

web_server:
  port: 80


globals:
  - id: stepper_steps
    type: int
    initial_value: '0'
    restore_value: true
  - id: stepper_prevpos
    type: float
    initial_value: '0'
  - id: stepper_position
    type: float
    initial_value: '0'
  - id: stepper_state
    type: int
    initial_value: '0'
  - id: calib_state
    type: int
    initial_value: '0'

sensor:
  - platform: uptime
    name: "Node uptime"

switch:
  - platform: restart
    name: "Restart"

binary_sensor:
  - platform: gpio
    pin:
      number: 14
      mode: INPUT_PULLUP
    name: "Open"
    filters:
        - invert:
        - delayed_on: 10ms
    on_click:
    - min_length: 50ms
      max_length: 1000ms
      then:
        - lambda: |
            if ((id(stepper_state) == 0) && (id(endstop).state == 0)) {
              //shade is stopped and not already open at endstop
              id(blind).open();
            } else {
              //shade is moving
              id(blind).stop();
            }

  - platform: gpio
    pin:
      number: 32
      mode: INPUT_PULLUP
    name: "Close"
    filters:
        - invert:
        - delayed_on: 10ms
    on_click:
    - min_length: 50ms
      max_length: 1000ms
      then:
        - lambda: |
            if ((id(stepper_state) == 0) && (id(stepper_position) < id(stepper_steps))) {
              //shade is stopped and not already closed
              id(blind).close();
            } else {
              //shade is moving
              id(blind).stop();
            }
#     a 10s press on the down button will enter calibration mode
#     shutter goes all the way up and then travels down until a button is pressed
#     where shutter is stopped is the new stepper_steps value
    - min_length: 5000ms
      max_length: 15000ms
      then:
        - lambda: |
            id(calib_state) = 1;
            if ((id(stepper_state) == 0) && (id(endstop).state == 0)) {
              //shade is stopped and not already open at endstop
              id(blind).open();
            } else if ((id(stepper_state) == 0) && (id(endstop).state == 1)) {
              //shade is stopped and already open at endstop
              id(blind).close();
            }

  - platform: gpio
    pin:
      number: 33
      mode: INPUT_PULLUP
    name: "Endstop"
    id: endstop
    filters:
      - delayed_on_off: 300ms
    on_press:
      then:
#         hit the endstop, store the current steps value and reset stepper position to 0
        - globals.set:
            id: stepper_prevpos
            value: !lambda "return id(my_stepper).current_position;"
        - stepper.report_position:
            id: my_stepper
            position: 0
#         just in case, also set the target to 0
        - stepper.set_target:
            id: my_stepper
            target: 0
        - lambda: |
            if (id(stepper_state) == 1) {
              //shade is moving
              id(blind).stop();
            }

cover:
  - platform: template
    name: "Blind"
    id: blind
    close_action:
      - globals.set:
          id: stepper_state
          value: '1'
#       overshoot past the endstop to make sure it's hit (endstop will stop blind)
      - stepper.set_target:
          id: my_stepper
          target: 4000
#       wait until the stepper is no longer moving
      - wait_until:
          - lambda: "if (id(my_stepper).current_position == id(my_stepper).target_position) { return 0; } else { return 1; }"
      - globals.set:
          id: stepper_position
          value: !lambda "return id(my_stepper).target_position;"
      - globals.set:
          id: stepper_state
          value: '0'
      - if:
          condition:
#           reached past the 'top' but still haven't triggered the endstop yet
            lambda: "if(id(stepper_position) == 4000 && id(endstop).state == 0) { return 0; } else { return 1; }"
          then:
#           reset stepper position to 0
          - stepper.report_position:
              id: my_stepper
              position: 0
#           just in case, also set the target to 0
          - stepper.set_target:
              id: my_stepper
              target: 0
    open_action:
      - globals.set:
          id: stepper_state
          value: '1'
      - stepper.set_target:
          id: my_stepper
#         for calibration the number of steps is doubled
          target: !lambda "if(id(calib_state) == 0) { return id(stepper_steps); } else { return id(stepper_steps) * 2; }"
#       wait until the stepper is no longer moving
      - wait_until:
          - lambda: "if (id(my_stepper).current_position == id(my_stepper).target_position) { return 1; } else { return 0; }"
      - globals.set:
          id: stepper_position
          value: !lambda "return id(my_stepper).target_position;"
      - globals.set:
          id: stepper_state
          value: '0'
      - lambda: |
          if (id(calib_state) == 1) {
            //reached double the current stepper distance
            id(stepper_steps) = id(stepper_position);
            //continue with calibration
            id(blind).close();
          }
    stop_action:
      - stepper.set_target:
          id: my_stepper
          target: !lambda "return id(my_stepper).current_position;" 
      - globals.set:
          id: stepper_position
          value: !lambda "return id(my_stepper).current_position;"
      - globals.set:
          id: stepper_state
          value: '0'
      - lambda: |
          if (id(calib_state) == 1 && id(endstop).state == 0) {
            //stopping at something other than the endstop
            id(calib_state) = 0;
            id(stepper_steps) = id(stepper_position);
          }
      - cover.template.publish:
          id: blind
          position: !lambda "return 0 + (id(stepper_position) / id(stepper_steps));"
    position_action:
      - stepper.set_target:
          id: my_stepper
          target: !lambda "return id(stepper_steps) * (0 + pos);"
    optimistic: true
    assumed_state: true
    has_position: true
    

stepper:
  - platform: a4988
    id: my_stepper
    dir_pin: 
      number: 26
      inverted: true
    step_pin: 25
    max_speed: 2500

    # Optional:
    sleep_pin: 27
    acceleration: 1500
    deceleration: 3000

I kilka fotek:



2 polubienia

Allegro A4988 chyba ma zabezpieczenia, ale ja bym wstawił jakiś czujnik zaryglowania.

W kwestii tych ostrzeżeń - nie wyglądają groźnie, ale możesz założyć issue (tzn. najpierw poszukać istniejącego, bo może jest?)

Te ostrzeżenia to tylko informacje że używa przestarzałych wywołań funkcji.

Dzieki za informację odnośnie błędów.

Co masz na myśli? O jakich zabezpieczeniach mówisz i przed czym?

Przed przeciążeniem i przegrzaniem.