Hiya,
I’m trying to do exactly the same thing. Here’s where I ended up:
- Hardware: D1Mini (ESP8266), A4988 motor driver, 28BYJ-48 motor (5V version, hacked to be bipolar), voltage converter (set to convert 12V to 5V), 12V power supply.
- Mounting: I don’t have a 3D printer but I LOVE instamorph and use it for everything, and it’s perfect for this. You can get knockoff versions, they’re basically the same. Heat it up in a pot of hot water, and you get a gob of almost molten plastic, into which you can sink the motor to make a mounting. It hardens solid in < 5min and I found it was completely perfect.
-
Coupling: I have two blinds and they required different couplings because one has a very stiff tilt shaft, the other moves easily
- For the easy moving one, I used a metal shaft coupler from Amazon. Not perfect as I had to grind down the tilt rod slightly but very strong.
- For the stiff one, I left the worm gear mechanism in place and just removed the spool, replacing it with a LEGO 24-tooth Technic gear. I fitted a 16-tooth gear to the motor shaft, and mounted the motor sideways so that the gears meshed. This gear ratio plus the worm gear mechanism gave me enough reduction to allow the 28BYJ to tilt these very stiff blinds.
-
Connections (sorry I don’t know how to do the fancy diagrams!):
- Control
- D1Mini D2 — A4988 Sleep — A4988 Reset (see below!)
- D1Mini D3 — A4933 Step
- D1Mini D4 — A4988 Direction
- Motor
- A4988 1B pin 11 — Motor orange
- A4988 1A pin 12 — Motor pink
- A4988 2A pin 13 — Motor yellow
- A4988 2B pin 14 — Motor blue
- Motor power
- 12VAC — Voltage converter IN+ — A4988 VMOT
- Voltage converter IN- — A4988 GND pin 15
- Logic power
- Voltage converter OUT+ (5V) — D1Mini 5V — A4988 VDD
- D1Mini GND — A4988 GND pin 9 — Voltage converter OUT-
- Control
-
ESPHome config:
esphome: name: blinds-tilt3 platform: ESP8266 board: d1_mini esp8266_restore_from_flash: true on_boot: priority: -100 then: - stepper.report_position: id: blind_stepper position: !lambda "return id(saved_position);" - stepper.set_target: id: blind_stepper target: !lambda "return id(saved_position);" - stepper.set_speed: id: blind_stepper speed: 200 steps/s - script.execute: update_cover_position logger: ota: password: "foo" # set by ESPHome - use that value! wifi: ssid: "foo" password: "foo" reboot_timeout: 2min # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "Blinds-Tilt3 Fallback Hotspot" password: "foo" captive_portal: # Enable Home Assistant API api: services: - service: set_stepper_target variables: target: int then: - stepper.set_target: id: blind_stepper target: !lambda 'return target;' - script.execute: record_stepper_position - service: set_stepper_speed variables: speed: int then: - stepper.set_speed: id: blind_stepper speed: !lambda 'return speed;' - service: set_stepper_position variables: stepper_position: int then: - stepper.report_position: id: blind_stepper position: !lambda "return stepper_position;" - stepper.set_target: id: blind_stepper target: !lambda "return stepper_position;" globals: - id: open_position type: int initial_value: '8000' - id: closed_position type: int initial_value: '1000' - id: saved_position type: int initial_value: '1000' restore_value: true stepper: - platform: a4988 id: blind_stepper dir_pin: D4 step_pin: D3 sleep_pin: D2 max_speed: 250 steps/s acceleration: 200 deceleration: 200 cover: - platform: template id: "blind_cover" device_class: blind name: "Blinds" has_position: true optimistic: false open_action: - logger.log: "Opening" - cover.template.publish: id: blind_cover current_operation: OPENING - stepper.set_target: id: blind_stepper target: !lambda "return id(open_position);" - while: condition: lambda: 'return id(blind_stepper).current_position < id(open_position);' then: - script.execute: update_cover_position - delay: 1000 ms - script.execute: update_cover_position - script.execute: record_stepper_position - cover.template.publish: id: blind_cover current_operation: IDLE close_action: - logger.log: "Closing" - cover.template.publish: id: blind_cover current_operation: CLOSING - stepper.set_target: id: blind_stepper target: !lambda "return id(closed_position);" - while: condition: lambda: 'return id(blind_stepper).current_position > id(closed_position);' then: - script.execute: update_cover_position - delay: 1000 ms - script.execute: update_cover_position - script.execute: record_stepper_position - cover.template.publish: id: blind_cover current_operation: IDLE position_action: - logger.log: "Setting position" - stepper.set_target: id: blind_stepper target: !lambda 'return (float(pos) * float( float(id(open_position)) - float(id(closed_position)) )) + float(id(closed_position));' - while: condition: lambda: 'return id(blind_stepper).current_position != ((float(pos) * float( float(id(open_position)) - float(id(closed_position)) )) + float(id(closed_position)));' then: - script.execute: update_cover_position - delay: 1000 ms - script.execute: update_cover_position - script.execute: record_stepper_position - cover.template.publish: id: blind_cover current_operation: IDLE stop_action: - logger.log: "Stopping" - cover.template.publish: id: blind_cover current_operation: IDLE - stepper.set_target: id: blind_stepper target: !lambda 'return id(blind_stepper).current_position;' - script.execute: update_cover_position - script.execute: record_stepper_position sensor: - platform: template name: "Current stepper position" lambda: return id(blind_stepper).current_position; update_interval: 5s script: - id: update_cover_position then: - cover.template.publish: id: blind_cover position: !lambda 'return float( float(id(blind_stepper).current_position) - float(id(closed_position))) / float( float(id(open_position)) - float(id(closed_position)) );' - id: record_stepper_position then: - globals.set: id: saved_position value: !lambda 'return id(blind_stepper).current_position;'
Key learnings:
- Many tutorials tell you to bridge the reset and sleep pins but don’t say to provide a sleep signal. I tried various combinations before realising that I needed to connect a pin on my microcontroller to both the sleep and reset pins, and configure it in ESPHome as the sleep pin.
- D4 is the built in LED on the D1 Mini so if you use it for the direction signal to the motor controller, it will light up when the motor is going in one direction and be off when it goes in the other direction!
- It’s pretty vital to have the ESPHome remember the current position of the motor, because if not, and you have a power cut, when the power comes back you might break your blinds/motor/mount by trying to close already-closed blinds.
- Having a bit of acceleration and deceleration in the ESPHome config gives you extra torque when you need it.