Motor on a roller blind - ESPHome version?

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

Hi all,
I come in this topic because I see a lot of specialist of Esphome related with stepper motor. My goal is to buiild a 1-axis solar tracker via i) a magnetic compass, here a BMM150 and ii) a stepper motor with the a4988. In my idea, the control of the stepper motor (direction & step) must be done on the difference between the heading given by the compass and the solar heading given the sun object with my local GPS coordinates. So in theory it’s simple if the difference is +D then set the target of the stepper with int(D/delta_angle), if the difference is -D then set with -int(D/delta_angle).
Here my current code

esphome:
  name: suiveur_1axe
  platform: ESP8266
  board: nodemcuv2
  includes:
    - BMM150_custom_sensor.h

wifi:
  ssid: !secret esphome_ssid
  password: !secret esphome_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Suiveur 1Axe"
    password: !secret esphome_password

captive_portal:

# Enable logging
logger:

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

ota:

sun:
  latitude: !secret esphome_lat
  longitude: !secret esphome_long 
  
time:
  - platform:  homeassistant    #sntp
    id: homeassistant_time  

output:
  - platform: gpio
    pin:
      number: GPIO02
      inverted: True
    id: gpio_02
    
light:
  - platform: binary   
    output: gpio_02
    name: suiveur_1axe_led_status
    id: suiveur_1axe_led_status

i2c:
  sda: GPIO12
  scl: GPIO14
  id: bus_i2c

sensor:

  - platform: sun
    name: suiveur_1axe_elevation_soleil
    type: elevation

  
  - platform: sun
    name: suiveur_1axe_azimuth_soleil
    id: suiveur_1axe_azimuth_soleil
    type: azimuth
    update_interval: 1s

#  - platform: qmc5883l
#    address: 0x0D
#    field_strength_x:
#      name: "suiveur_1axe_X"
#      id: suiveur_1axe_X
#    field_strength_y:
#      name: "suiveur_1axe_Y"
#      id: suiveur_1axe_Y
#    field_strength_z:
#      name: "suiveur_1axe_Z"
#      id: suiveur_1axe_Z
#    heading:
#      name: "suiveur_1axe_azimuth_plateau"
#      id: suiveur_1axe_azimuth_plateau
#    range: 200uT
#    oversampling: 256x
#    update_interval: 1s

  - platform: custom
    id: bmm150_id
    lambda: |-
      auto BMM150 = new BMM150CustomSensor();
      App.register_component(BMM150);
      return {BMM150->heading_sensor};

    sensors:
    - name: "suiveur_1axe_azimuth_plateau"
      id: suiveur_1axe_azimuth_plateau
      unit_of_measurement: "°"
      accuracy_decimals: 1
      icon: "mdi:compass-outline"
      filters:
       - sliding_window_moving_average:
           window_size: 3
           send_every: 1
      
#      filters:
#      - sliding_window_moving_average:
#          window_size: 15
#          send_every: 1  
    
#    - name: "suiveur_1axe_x"
#      unit_of_measurement: "uT"
#      accuracy_decimals: 2
#      icon: "mdi:magnet"
#      filters:
#      - sliding_window_moving_average:
#          window_size: 15
#          send_every: 1
#    - name: "suiveur_1axe_y"
#      unit_of_measurement: "uT"
#      accuracy_decimals: 2
#      icon: "mdi:magnet"
#      filters:
#      - sliding_window_moving_average:
#          window_size: 15
#          send_every: 1
          
#    - name: "suiveur_1axe_z"
#      unit_of_measurement: "uT"
#      accuracy_decimals: 2
#      icon: "mdi:magnet"  
#      filters:
      #- median:
      #    window_size: 7
      #    send_every: 1
#      - sliding_window_moving_average:
#          window_size: 15
#          send_every: 1  
    
    
#  - platform: template
#    id: suiveur_1axe_azimuth_plateau_smoothed
#    name: suiveur_1axe_azimuth_plateau_smoothed
#    update_interval: 1s
#    unit_of_measurement: "°"
#    lambda: return id(suiveur_1axe_azimuth_plateau).state + 0*180.0;
#    filters:
#      - sliding_window_moving_average:
#          window_size: 5
#          send_every: 1  
    
  - platform: template
    id: suiveur_1axe_azimuth_difference
    name: suiveur_1axe_azimuth_difference
    update_interval: 1s
    unit_of_measurement: "°"
    lambda: return ( id(suiveur_1axe_azimuth_soleil).state - id(suiveur_1axe_azimuth_plateau).state);
    filters:
      - sliding_window_moving_average:
          window_size: 5
          send_every: 1
    on_value:
      - if:
          condition:
            and:
             - lambda: |-
                 return (id(stepper_suiveur_1axe).current_position == id(stepper_suiveur_1axe).target_position );
             -  lambda: |-
                 return (  (  ( float(id(suiveur_1axe_azimuth_difference).state)  < -2.0) | ( float(id(suiveur_1axe_azimuth_difference).state)  > 2.0) )     );
             
            #not:   
            # - sensor.in_range:
            #     id: suiveur_1axe_azimuth_difference
            #     below: -2.0
            #     above: 2.0
               
            #lambda: |-
            #  return ( fabs(float(id(suiveur_1axe_azimuth_difference).state))  > 1.0 );
          then: 
            #- delay: 30s
            - light.turn_on:
                id: suiveur_1axe_led_status
            - stepper.set_target:
               id: stepper_suiveur_1axe
               target: !lambda |-
                if (float(id(suiveur_1axe_azimuth_difference).state) > 10.0) 
                {
                  return -int( float(id( suiveur_1axe_azimuth_difference).state)/((1.8/20.0)) );
                } 
                else if (float(id(suiveur_1axe_azimuth_difference).state) >  2.0)
                {
                   return -int( float(id( suiveur_1axe_azimuth_difference).state)/((1.8/10.0)) );
                }
                else if (float(id(suiveur_1axe_azimuth_difference).state) < -2.0)
                {
                  return int( float(id( suiveur_1axe_azimuth_difference).state)/((1.8/10.0)) );
                }
                else if (float(id(suiveur_1axe_azimuth_difference).state) < -10.0)
                {
                  return int( float(id( suiveur_1axe_azimuth_difference).state)/((1.8/20.0)) );
                }
            #- delay: 10s
            #- while:
            #    condition:
            #      lambda: 'return id(stepper_suiveur_1axe).current_position != id(stepper_suiveur_1axe).target_position;'
            #    then:
            #      - logger.log: "Rotation"
                
            - light.turn_off:
                id: suiveur_1axe_led_status      
          
  
       #- if:
          #condition:
          # Should return either true or false
          #  lambda: |-
          #    return (  (  ( float(id(suiveur_1axe_azimuth_difference).state)  < -1.0) | ( float(id(suiveur_1axe_azimuth_difference).state)  > 1.0) ) & (id(stepper_suiveur_1axe).current_position == id(stepper_suiveur_1axe).target_position )       );
          #              # return ( ( fabs(float(id(suiveur_1axe_azimuth_difference).state) ) > 1.0) & (id(stepper_suiveur_1axe).current_position == id(stepper_suiveur_1axe).target_position )       );

        #  then:
        #  - stepper.set_target:
        #      id: stepper_suiveur_1axe
        #      target: !lambda |-
        #        if (float(id(suiveur_1axe_azimuth_difference).state) > 0.0) 
        #        {
        #          return -10;
        #        } else 
        #        {
        #          return 10;
        #        }
              #!lambda  return ( -sign(float(id( suiveur_1axe_azimuth_difference).state)) ) ;
              #target: !lambda  return ( -int(float(id( suiveur_1axe_azimuth_difference).state)/((1.8/10))) ) ;
            
  - platform: uptime
    #name: "up_suiveur_1axe"
    id: uptime_sec  
    
  - platform: wifi_signal
    name: "WiFi puissance_suiveur_1axe"
    update_interval: 10s      

switch:
  - platform: restart
    name: "restart_suiveur_1axe"

stepper:
  - platform: a4988
    id: stepper_suiveur_1axe
    
    step_pin: 
      number: D0
      inverted: False
      
    dir_pin:
      number: D1
      inverted: False
      
    max_speed: 60 steps/s

    # Optional:
    sleep_pin:
      number: D2
      inverted: False
      
    acceleration: inf
    deceleration: inf
    
    
cover:
  - platform: template
    name: "suiveur_1axe"
    id: suiveur_1axe
    open_action:
      - stepper.set_target:
          id: stepper_suiveur_1axe
          target: -1000

    close_action:
      - stepper.set_target:
          id: stepper_suiveur_1axe
          target: 1000
    
    stop_action:
      - stepper.set_target:
          id: stepper_suiveur_1axe
          target: !lambda return id(stepper_suiveur_1axe).current_position;      
    
    optimistic: true    
    
    
binary_sensor:
  - platform: status
    name: "suiveur_1axe_status" 
    
    
text_sensor:
  - platform: template
    name: suiveur_1axe__uptime
    lambda: |-
      int seconds = (id(uptime_sec).state);
      int days = seconds / (24 * 3600);
      seconds = seconds % (24 * 3600); 
      int hours = seconds / 3600;
      seconds = seconds % 3600;
      int minutes = seconds /  60;
      seconds = seconds % 60;
      return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
    icon: mdi:clock-start
    update_interval: 113s    
    
  - platform: version
    name: "suiveur_1axe__ESPHome_version"  

In practice it’s not working … oscillating between the 0 value or even worser can converge the a non-zero value :frowning: I think the problem comes from the fact that during the rotation of the motor, the template sensor associated with difference of heading is also changing and then a new set_target is set.

When I would like to write and I don’t know yet how is to
i) compute the difference of heading and be sure its value is stablized then if the motor is in IDLE
ii) send the command to rotate correspondly to this difference AND do not update the difference sensor and/or wait the rotation reaches the target_position in order to send a new rotation command.

I hope I was more or less clear…

I’m trying to build my own roller but I find the 28BYJ-48 stepper motor (in 12v) has not enough torque to move my blind (36’’ or 91 cm wide). What kind of motor do you use?

Mine are 91x190 and it’s fine. Slow but fine. What power supply do you use? Is your motor trying to do anything at all? I.e. buzzing at least?

I use nema 17 motors

I use a 12v 2.0A PSU. Motor is working but it skip steps as soon as torque is needed. I guess my blinds are a bit too heavy.

That will be my next try. Is it okay with uln2003 ?

I initially used 12v 0.25A power supply and it worked. So you have plenty of power out there. I use uln2003 so it’s fine. If your motor works without the blind as expected then I guess blind is too heavy.

Is use a driver that was left over from my 3d printer project

would you been kind enough to explain how you did it? I mean what driver how you using and how do you pilot it from esphome?

I should have search first: using a A4988 and a control board, I guess.