Hi, I’m working on an automated coffee capsule machine project controlled by an ESP32 WROOM via Home Assistant and ESPHome. I’m using a DRV8825 driver to control a NEMA17 stepper motor. The motor is responsible for positioning the coffee capsule for extraction.
I’ve successfully configured the YAML in ESPHome, and the motor moves correctly, but it doesn’t hold its position after completing a movement—it goes into what seems like “idle” or “freewheel” mode. I’m looking to keep the motor locked in place after the movement, as I need to maintain pressure during the coffee capsule positioning.
Is there something specific I need to configure in ESPHome to keep the stepper motor holding position when it’s not moving? Or is there a setting related to current/holding torque I might be missing? Any suggestions would be greatly appreciated!
Driver needs to have enable pin low to keep position, if you disable it the motor is not powered.
Sleep pin reduces motor current, it might hold the position even if set to sleep, you can try that.
Also take note that when you hold it with the power on, then your driver board might overheat.
A solution might be to get a permanent magnet (PM) motor and/or use gearing.
Thanks for your response! I’ve tried implementing your suggestion to keep the ENABLE pin LOW in order to maintain the stepper motor’s position. I set up the ENABLE pin as a separate switch in ESPHome and ensured that it stays active (LOW). However, the situation hasn’t changed—the stepper motor still goes into “freewheel” mode after completing its movement.
Here’s the current configuration:
ENABLE pin (GPIO27) is set as a switch, always active (LOW):
- platform: gpio
name: "DRV8825 Enable"
pin: GPIO27 # Pin ENABLE
id: drv8825_enable
inverted: true # Enable the driver when the pin is LOW
restore_mode: ALWAYS_ON # Always active to keep the engine blocked
(You can see my complete YAML code in the main post)
It seems I might still be missing something crucial to get the motor to hold its position. Could you clarify a bit further on how to correctly keep the ENABLE pin LOW and whether there’s another setting or approach I should take?
Thank you for the suggestion! I appreciate the note about the potential overheating of the driver board when holding the motor in position. To address this, I’m planning to install a rack and pinion system: the plunger that pushes the coffee capsule into position will be moved by a rack, and the pinion will be attached to the stepper motor.
Additionally, I’ve already installed a heatsink on the DRV8825, and I’ll be monitoring the temperatures during some tests to ensure everything stays within safe limits.
As a first step, I disconnected the following pins
RESET from 3.3V
SLEEP from GPIO18
Then I shorted them together, leaving the ENABLE pin connected to GPIO27 on the ESP32. After testing, I noticed that the stepper motor worked correctly during its movement and is now holding its position properly once the movement is completed.
In terms of temperature, I measured 62°C on the heatsink of the DRV8825, while the motor’s temperature is 23°C when stationary.
Now, I’m wondering how to put the DRV8825 and the stepper motor into rest mode after the coffee is extracted and the motor returns to its starting position. Do you have any advice on how to implement that?
With the sleep pin of course.
You connect reset pin to 3.3V
Sleep pin to Gpio output, and write that low when you want to sleep.
The wiring scheme of DRV8825 is not same as A4988
Also disconnect fault pin from 3.3V, it’s output pin.
Thank you so much for clarifying the connections for the SLEEP and RESET pins to manage the sleep mode of the DRV8825 via GPIO18 on the ESP32. Your explanation was really helpful!
I noticed that on the Pololu website, the FAULT pin is shown as usable as a 3.3V input, since it’s internally connected to the SLEEP pin. Pololu states that this setup respects the pin layout of the previous A4988 driver. However, in the second configuration shown by Pololu, which is similar to your suggestion, the FAULT pin is left disconnected.
In a few moments, I will modify the connections as per your advice. Could you also help me with updating the YAML file to implement the suspension of the DRV8825 using the SLEEP pin?
Unfortunately, the situation has reverted to how it was before, with the DRV8825 disengaging the stepper after movement. Previously, this issue was resolved by shorting RESET and SLEEP together and supplying 3.3V to the FAULT pin.
You mentioned that SLEEP can be managed via the YAML in ESPHome. Could you clarify how I can ensure that the stepper motor remains engaged after movement and how to implement the sleep function properly?
First confirm that the wiring I suggested works for your board.
reset and sleep to 3.3V, Enable to gnd(or disconnected) and fault disconnected.
Is it Pololu or something else?
Hi KAROSM, I followed your suggestion, and I’m happy to report that everything works as expected! I connected RESET and SLEEP to 3.3V, ENABLE to GND, and FAULT disconnected. The driver and motor now function correctly, with the motor locking in place after each movement. Thanks for your help!
Now, I just need to control the SLEEP pin to put the stepper and driver into sleep mode when required. Do you have any suggestions for how to best implement that in YAML of ESPhome?
You just create gpio output or gpio switch for that pin and set it defaul high. You remove sleep_pin from stepper component.
Then control the pin:
then:
- switch.turn_off: sleep_pin
Thank you for the suggestion. I’ve set the SLEEP pin to HIGH by default in the YAML file (see code below) and kept ENABLE connected to GND as you advised. However, the energy-saving SLEEP mode doesn’t seem to hold the motor in place effectively. Only when I press “Push Stepper Target Position” or “Push Stepper Home” does the motor remain stable in position, as these actions don’t activate the SLEEP function.
Of course, I’m open to any further suggestions you might have.
Here’s the YAML configuration for reference:
#The substitution block has all the configurable details
substitutions:
name_of_board: capsy
stepper_speed: "300 steps/s"
stepper_max_speed: "250 steps/s"
acceleration_value: "150"
deceleration_value: "150"
initial_open_position: "8000" # sets this as the open position or the farthest motor will move for open
initial_closed_position: "1000" # sets this as the close position or the farthest motor will move for close
initial_saved_position: "1000" # sets the default value if there is no saved position saved to flash
############################################
#YOU SHOULD NOT NEED TO EDIT BELOW THIS LINE
############################################
esphome:
name: $name_of_board
on_boot:
priority: -100
then:
- stepper.report_position:
id: push_stepper
position: !lambda "return id(saved_position);"
- stepper.set_target:
id: push_stepper
target: !lambda "return id(saved_position);"
- stepper.set_speed:
id: push_stepper
speed: $stepper_speed
- script.execute: update_cover_position
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "your_key"
services:
- service: set_stepper_target
variables:
target: int
then:
- stepper.set_target:
id: push_stepper
target: !lambda 'return target;'
- script.execute: record_stepper_position
- service: set_stepper_speed
variables:
speed: int
then:
- stepper.set_speed:
id: push_stepper
speed: !lambda 'return speed;'
- service: set_stepper_position
variables:
stepper_position: int
then:
- stepper.report_position:
id: push_stepper
position: !lambda "return stepper_position;"
- stepper.set_target:
id: push_stepper
target: !lambda "return stepper_position;"
globals:
- id: open_position
type: int
initial_value: $initial_open_position
- id: closed_position
type: int
initial_value: $initial_closed_position
- id: saved_position
type: int
initial_value: $initial_saved_position
restore_value: true
ota:
- platform: esphome
password: "your_ota_password"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Capsy Fallback Hotspot"
password: "your_ap_password"
captive_portal:
# LED exposed as binary light
output:
- platform: gpio
pin: GPIO23
id: output_led
light:
- platform: binary
id: light_led
name: LED
output: output_led
# Relays exposed as switches
switch:
- platform: gpio
pin: GPIO16
id: relay_1
name: Caldaia
- platform: gpio
pin: GPIO17
id: relay_2
name: CoffePump
- platform: gpio
pin: GPIO18
id: drv8825_sleep
restore_mode: ALWAYS_ON # Imposta SLEEP su HIGH all'avvio
- platform: gpio
name: "DRV8825 Enable"
pin: GPIO27 # Pin ENABLE
id: drv8825_enable
inverted: true # Abilitare il driver quando il pin è LOW
restore_mode: ALWAYS_ON # Sempre attivo per mantenere il motore bloccato
- platform: gpio
pin: GPIO22 # M0 pin
name: "DRV8825 M0"
id: drv8825_m0
restore_mode: ALWAYS_ON # Imposta M0 su HIGH all'avvio (per 1/32 step)
- platform: gpio
pin: GPIO21 # M1 pin
name: "DRV8825 M1"
id: drv8825_m1
restore_mode: ALWAYS_ON # Imposta M1 su HIGH all'avvio (per 1/32 step)
- platform: gpio
pin: GPIO19 # M2 pin
name: "DRV8825 M2"
id: drv8825_m2
restore_mode: ALWAYS_ON # Imposta M2 su HIGH all'avvio (per 1/32 step)
# Configurazione del motore stepper
stepper:
- platform: a4988 # Usa DRV8825, che è compatibile
id: push_stepper
step_pin: GPIO26 # STEP pin
dir_pin: GPIO13 # DIR pin
acceleration: $acceleration_value
deceleration: $deceleration_value
max_speed: $stepper_max_speed # Velocità massima in passi al secondo
cover:
- platform: template
id: cappusher
device_class: blind
name: ${name_of_board}
has_position: true
optimistic: false
open_action:
- logger.log: "Opening"
- switch.turn_on: drv8825_sleep # Attiva il pin SLEEP per il movimento
- cover.template.publish:
id: cappusher
current_operation: OPENING
- stepper.set_target:
id: push_stepper
target: !lambda "return id(open_position);"
- while:
condition:
lambda: 'return id(push_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: cappusher
current_operation: IDLE
- switch.turn_off: drv8825_sleep # Disattiva il pin SLEEP al termine del movimento
close_action:
- logger.log: "Closing"
- switch.turn_on: drv8825_sleep # Attiva il pin SLEEP per il movimento
- cover.template.publish:
id: cappusher
current_operation: CLOSING
- stepper.set_target:
id: push_stepper
target: !lambda "return id(closed_position);"
- while:
condition:
lambda: 'return id(push_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: cappusher
current_operation: IDLE
- switch.turn_off: drv8825_sleep # Disattiva il pin SLEEP al termine del movimento
position_action:
- logger.log: "Setting position"
- switch.turn_on: drv8825_sleep # Attiva il pin SLEEP per il movimento
- stepper.set_target:
id: push_stepper
target: !lambda 'return (float(pos) * float( float(id(open_position)) - float(id(closed_position)) )) + float(id(closed_position));'
- while:
condition:
lambda: 'return id(push_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: cappusher
current_operation: IDLE
- switch.turn_off: drv8825_sleep # Disattiva il pin SLEEP al termine del movimento
stop_action:
- logger.log: "Stopping"
- cover.template.publish:
id: cappusher
current_operation: IDLE
- stepper.set_target:
id: push_stepper
target: !lambda 'return id(push_stepper).current_position;'
- script.execute: update_cover_position
- script.execute: record_stepper_position
# Controllo dello stepper tramite una slider nell'interfaccia di Home Assistant
number:
- platform: template
name: "Push Stepper Target Position"
id: target_position
min_value: 1000
max_value: 8000
step: 10
unit_of_measurement: steps
icon: mdi:target
set_action:
# Assicurati che ENABLE e SLEEP siano attivi prima del movimento
- switch.turn_on: drv8825_enable
- switch.turn_on: drv8825_sleep # Attiva il pin SLEEP per il movimento
- stepper.set_target:
id: push_stepper
target: !lambda 'return (int(x));'
# Opzionale: comando per tornare a 0 passi
button:
- platform: template
name: "Push Stepper Home"
on_press:
# Attiva il driver prima del movimento verso la posizione di origine
- switch.turn_on: drv8825_enable
- switch.turn_on: drv8825_sleep # Attiva il pin SLEEP per il movimento
- stepper.set_target:
id: push_stepper
target: 1000
sensor:
- platform: template
name: ${name_of_board} Current position
lambda: return id(push_stepper).current_position;
update_interval: 5s
- platform: wifi_signal
name: ${name_of_board} Wifi
update_interval: 60s
binary_sensor:
- platform: status
name: ${name_of_board} Status
script:
- id: update_cover_position
then:
- cover.template.publish:
id: cappusher
position: !lambda 'return float( float(id(push_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(push_stepper).current_position;'
Yes there is difference between off and sleep, but depends on the driver, how much current is given in sleep mode.
Ideally you have some place to “park” your mechanism where no external load is on motor.