Motor on a roller blind - ESPHome version?

Hi,

For the record, this is the code I’m trying now:

I have now tried with both of these but none of them will work:

sleep_pin:
  number: D2
  inverted: no
sleep_pin:
  number: D2
  inverted: yes

And if I then connect RESET with SLEEP everything start working as expected…

1 Like

Hi again,

I think I got it working!
Maybe it’s abvious but for me I had no idea of this until I read about this in a post somewhere else.

I still have D2 connected to the SLEEP pin and then I connected the RESET to the 3V.
Then everything started to work as expected.

Really don’t know

around 30-40s

As my roller blind are also quite big and heavy, I am trying to use a NEMA 17 and this gearbox: https://www.instructables.com/3D-Printed-161-Nema-17-Gearbox/

I am struggling with fixating the the nut to the bold. In the instructions above, some glue is used https://youtu.be/87yFGr4fGRY?t=143 As I do not have some special glue, I have tried it with hot glue, but that isn’t working. I’ll need some specific glue for metals, I suppose.

For those who, like me, need to open a large blind fast and not struggle with it rolling down when the power is removed, I have a DC worm-drive motor working very well.

My blind is over 2m tall in an exit doorway so the target opening time is less than 10 s - it is currently about 12 s and I’m hoping to reduce that!

The full code is at Motorised blind. Remember position - how? - I continue to search for a way to make the blind position survive power failures but the rest of the code works just fine and may help someone.

I’m looking for something you’re using. Can you sketch up how you have connected everything to your ESP? Would help me alot

I’m using a D1 mini and the following:

  1. Touch sensor module to D0
  2. Adafruit TB6612 module to D6 and D7 (see Adafruit or Sparkfun connection info https://learn.sparkfun.com/tutorials/tb6612fng-hookup-guide)
  3. LED to D3 and +3V3 through resistor (can’t remember the resistance, maybe 100Ohm)
  4. two 3144 Hall effect ICs connected to D1 and D2 for rotary encoder. See https://components101.com/a3144-hall-effect-sensor for connection diagram)
  5. Sharp GP2Y0D21YK0F optical sensor connected to D5 https://datasheetspdf.com/datasheet/GP2Y0D21YK0F.html

I use a 5V DC-DC converter to provide the 5V to the Hall effect sensors, D1 Mini, the optical sensor and the TB6612 https://recom-power.com/pdf/Innoline/R-78E-0.5.pdf, with capacitors etc. The 3V3 of the D1 Mini supplies the LED, Hall effect sensors, touch sensor.

I designed and 3D printed a new blind end for the motor that has a series of magnets mounted into it.

The motor is a 12V worm drive motor like this

Hope that helps.

Thanks for the clarification. How do you power your 12v motor? As the incoming power for the D1 mini is 5V max?

The 12V supply goes direct to the H-bridge TB6612 and to the r-78-e0.5 DC-DC converter - the 5V from that goes to the D1 etc.

1 Like

Thx. This helped me alot

Thanks Richard!

Can you share some details? Which parts do you use, and how is it mounted? Is the motor mounted at one end, and the rotary encoder at the other? I have everything working with some 28BYJ-48 on 12V, but I find them way to slow since the maximum speed I can set them to without skipping steps is 450 steps.

All the essential details are above. I 3D printed a housing and added the magnets to the rotating blind end. I’ve since found motors like this and I’d use one of these if I did this again https://www.ebay.co.uk/itm/Reversible-GW4058-31ZY-Worm-Gear-Motor-Self-locking-High-Torque-With-Hall-Drive/274534578568

Hope that helps.

1 Like

thanks for the code. Let me know if you figure out the stuttering.

Unfortunately not, but it doesn’t bother me anymore. I also added position support since Esphome was updated. And also added an open offset, since for me at least, I had some problems that my motor would skip when opening, so the blind was not exactly in the correct physical position, and when closing again, it would sometimes go too much. This offset makes sure that when opening the blind, it is open with slightly more steps than needed, but these extra steps are not taken into account for the current position.
Here is the updated code, it also contains a switch for a relay controlling my heating, since the esp is close to that. You can ignore that part. (left more details below the code if interested)

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: 400 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: "xxxxx"
  password: "xxxxx"
  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
  # auth:
  #     username: admin
  #     password: !secret haLocalKey

# Enable logging
logger:

ota:
  password: !secret esphome_password

# Enable Home Assistant API
api:
  password: !secret esphome_password

script:
- id: turn_on_heating
  mode: restart
  then:
    - switch.turn_on: heating_relay
    - delay: 5 min
    - switch.turn_off: heating_relay
- id: turn_off_heating
  mode: restart
  then:
    - switch.turn_off: heating_relay

globals:
  - id: open_position
    type: int
    initial_value: '9400'
  - id: open_position_offset
    type: int
    initial_value: '65'
  - id: middle_position
    type: int
    initial_value: '3760'
  - id: current_position
    type: int
    initial_value: '9400'
    restore_value: true
  - id: temp_position
    type: int
    initial_value: '0'

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

switch:
  - platform: gpio
    name: "Heating Relay"
    id: heating_relay
    pin:
      number: D7
      inverted: true
    restore_mode: ALWAYS_OFF
  - platform: template
    name: "Heating Switch"
    id: heating_switch
    lambda: |-
        return false;
    assumed_state: true
    turn_on_action:
      - script.execute: turn_on_heating
    turn_off_action:
      - script.execute: turn_off_heating

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: 400 steps/s
      - stepper.set_target:
          id: blind_stepper
          target: !lambda "return id(open_position) + id(open_position_offset);"
      - 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) + id(open_position_offset))));'
            - delay: 1000 ms
      - cover.template.publish:
          id: kitchen_blind
          current_operation: IDLE
          position: !lambda 'return 1;'
      - stepper.report_position:
          id: blind_stepper
          position: !lambda "return id(open_position);"
      - stepper.set_target:
          id: blind_stepper
          target: !lambda "return id(open_position);"
      - 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
    position_action:
      - globals.set:
          id: temp_position
          value: !lambda 'return float(float(id(open_position)) * pos);'
      - stepper.set_speed:
          id: blind_stepper
          speed: !lambda |-
            if (id(temp_position) >= id(blind_stepper).current_position) {
              return 400;
            } else {
              return 800;
            }
      - stepper.set_target:
          id: blind_stepper
          target: !lambda |-
            if (id(temp_position) >= id(blind_stepper).current_position) {
              return id(temp_position) + id(open_position_offset);
            } else {
              return id(temp_position);
            }
      - while:
          condition:
            lambda: |-
              return id(blind_stepper).current_position != id(temp_position) && id(blind_stepper).current_position != (id(temp_position) + id(open_position_offset));
          then:
            - cover.template.publish:
                id: kitchen_blind
                current_operation: !lambda |-
                    if(id(temp_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))));'
      - stepper.report_position:
          id: blind_stepper
          position: !lambda "return id(temp_position);"
      - stepper.set_target:
          id: blind_stepper
          target: !lambda "return id(temp_position);"
      - globals.set:
          id: current_position
          value: !lambda "return id(temp_position);"
      - output.turn_on: builtin_led
    has_position: true

I left the heating part here since it might be helpful for you guys to know about the new script feature with mode restart. The way I have this is that another Esp8266 calls this script to turn on heating, and it keeps calling it every 2 minutes or so. And in case the calls stop, after 5 minutes the script will turn off the heating, to avoid it running forever

4 Likes

Hello,
I am looking for ideas to automate these kind of curtains. Video. Any tips will be helpful. Thanks.

Wonderful to have report position and the open offset as some times my motors have torque issue.

BIG THANK’S

I’ve got a 12v worm motor with encoder. Does anyone also use these and got a proper config? It isn’t as straight forward as a stepping-motor. I’ve tried setting it up using the on_value of the encoder, but that’s hard since it isn’t able to stop at exactly 0. Every rotation of the encoder gives 11 pulses, and I believe it’s a 1 to 64 gear ratio, so that’s a lot. I’m still experimenting myself, but it would save a lot of time if somebody has an example.

So I’ve got a working config now. Just one thing I can’t get fixed is the position-report. From my understanding 0 means opened (rollerblinds up), and 1 means closed (rollerblinds down). However, when I set the position to 0 using esphome, Home Assistant puts the slider all the way to the right. This also happens when I set the state as OPEN. If i set the closed position to 0.01 and the opened to 0.99 it works almost perfectly, but I think there is something wrong in my configuration.

Here is my code so far. Since the encoder goes too fast i’m not able to make it stop exactly on 0, so that’s something to keep in mind:

globals:
   - id: blind_06_max
     type: int
     restore_value: no
     initial_value: '10000'
    
sensor:
  - platform: rotary_encoder
    name: "Rotary Encoder"
    pin_a: GPIO23
    pin_b: GPIO5
    id: blind_06_steps
    
            
switch:
  - platform: gpio
    pin: GPIO18
    name: "Blind 6 S1"
    id: "blind_06_s1"
    interlock: [blind_06_s2]
  - platform: gpio
    pin: GPIO19
    name: "Blind 6 S2"
    id: "blind_06_s2"
    interlock: [blind_06_s1]
    
cover:
  - platform: template
    name: "Blind 6"
    id: blind_06_cover
    device_class: shade
    has_position: true

    open_action:
      - switch.turn_off: blind_06_s1
      - switch.turn_on: blind_06_s2
      - cover.template.publish:
          id: blind_06_cover
          current_operation: OPENING
          position: !lambda |-
            float pos = id(blind_06_steps).state / id(blind_06_max);
            return pos < 0 ? 0 : pos;
      - wait_until: 
          lambda: 'return id(blind_06_steps).state <= 0;'
      - switch.turn_off: blind_06_s2
      - cover.template.publish:
          id: blind_06_cover
          state: OPEN
          current_operation: IDLE

    close_action:
      - switch.turn_on: blind_06_s1
      - switch.turn_off: blind_06_s2
      - cover.template.publish:
          id: blind_06_cover
          current_operation: CLOSING
          position: !lambda |-
            float pos = id(blind_06_steps).state / id(blind_06_max);
            return pos > 1 ? 1 : pos;
              
      - wait_until:
          lambda: 'return id(blind_06_steps).state >= id(blind_06_max);'
      - switch.turn_off: blind_06_s1
      - cover.template.publish:
          id: blind_06_cover
          state: CLOSED
          current_operation: IDLE
          
    stop_action:
      then:
        - switch.turn_off: blind_06_s1
        - switch.turn_off: blind_06_s2
        - cover.template.publish:
            id: blind_06_cover
            position: !lambda 'return id(blind_06_steps).state / id(blind_06_max);' 
            current_operation: IDLE