Stepper motor stutter with D1 Mini + NEMA 17 Stepper + TMC2209 Driver using A4988 component

Hi, I’m working on a project to automate some window roller shades. I’m essentially doing something very similar to this: roller blind connected to NEMA 17 stepper motor, driven by a TMC2209 driver, with a solenoid and ratchet mechanism to prevent the shade from falling when the motor is not energized.

However, instead of an Arduino in the above example, I’d like to use a D1 Mini with ESPHome so I can integrate into Home Assistant. I’ve basically got this up and running with one problem: when I include the solenoid output in my yaml code, it causes the stepper motor to stutter while running. When the solenoid is not included in the yaml code, it runs smoothly and everything is as expected.

So, I have a few theories on what might be happening:

  1. Calling the wait_until action is introducing an added delay at odd intervals (note that I needed to include this because it seems like the actions within the cover are non-blocking: without wait_until, the solenoid would switch on, the motor would start turning, then the solenoid would immediately switch off - before the motor finished its movement.)
  2. I am terrible at selecting hardware and the D1 mini is underpowered for this application, so it can’t handle the solenoid and stepper motor at the same time.
  3. Something else.

My first thought was that (1) was the problem, and I would need to build my own custom external component for this application. But being a mouth-breathing dunce with very limited coding skills, I didn’t want to waste a bunch of time trying to figure out external components if the problem is actually (2) or (3).

Does anyone have any insights into what might cause this? Please find my yaml code and wiring diagram attached below.

Thank you in advance!

substitutions:
  name: esphome-web-2f9fd4
  friendly_name: Living Room Left Blackout Shade
  devicename: living_room_left_blackout_shade
  height: '16000'
  speed: '2000'
  accel: '1000'

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  name_add_mac_suffix: false
  project:
    name: esphome.web
    version: '1.0'

esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:

# Allow Over-The-Air updates
ota:

# Allow provisioning Wi-Fi via serial
improv_serial:

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

  # Set up a wifi access point
  ap:
    ssid: "ssid"
    password: "password"

# In combination with the `ap` this allows the user
# to provision wifi credentials to the device via WiFi AP.
captive_portal:

dashboard_import:
  package_import_url: github://esphome/example-configs/esphome-web/esp8266.yaml@main
  import_full_config: true

# To have a "next url" for improv serial
web_server:

cover:
  - platform: template
    name: None
    id: ${devicename}
    device_class: blind
    open_action: 
      then:
        - switch.turn_on: solenoid
        - stepper.set_target: 
            id: my_stepper
            target: ${height}
        - sensor.template.publish:
            id: position
            state: !lambda return id(my_stepper).target_position;
        - wait_until: 
            condition:
              - lambda: |-
                  return id(my_stepper).current_position == id(my_stepper).target_position;
            timeout: 30s
        - switch.turn_off: solenoid
    close_action: 
      then:
        - switch.turn_on: solenoid
        - stepper.set_target: 
            id: my_stepper
            target: '0'
        - sensor.template.publish:
            id: position
            state: !lambda return id(my_stepper).target_position;
        - wait_until: 
            condition:
              - lambda: |-
                  return id(my_stepper).current_position == id(my_stepper).target_position;
            timeout: 30s
        - switch.turn_off: solenoid
    stop_action: 
      - then:
    toggle_action:
      then:
        - if:
            condition: 
              - lambda: |-
                  return id(my_stepper).current_position == 0;
            then:
              - switch.turn_on: solenoid
              - stepper.set_target: 
                  id: my_stepper
                  target: ${height}
              - sensor.template.publish:
                  id: position
                  state: !lambda return id(my_stepper).target_position;
              - wait_until: 
                  condition:
                    - lambda: |-
                        return id(my_stepper).current_position == id(my_stepper).target_position;
                  timeout: 30s
              - switch.turn_off: solenoid
            else: 
              - switch.turn_on: solenoid
              - stepper.set_target: 
                  id: my_stepper
                  target: '0'
              - sensor.template.publish:
                  id: position
                  state: !lambda return id(my_stepper).target_position;
              - wait_until: 
                  condition:
                    - lambda: |-
                        return id(my_stepper).current_position == id(my_stepper).target_position;
                  timeout: 30s
              - switch.turn_off: solenoid
    position_action: 
      - then:
    assumed_state: true
    has_position: true

sensor: 
  - platform: template
    name: "Living Room Left Blackout Shade Position"
    id: position

binary_sensor:
  - platform: gpio
    id: limit_switch
    name: "Living Room Left Blackout Shade Manual Toggle"
    pin: GPIO12
    on_release: 
      then:
        - cover.toggle: living_room_left_blackout_shade

switch: 
  - platform: gpio
    pin: GPIO13
    id: solenoid

stepper: 
  - platform: a4988
    id: my_stepper
    step_pin: GPIO14
    dir_pin: GPIO4
    sleep_pin: 
      number: GPIO5
      inverted: true
    max_speed: ${speed}
    acceleration: ${accel}
    deceleration: ${accel}

to start with, put correct board

Hi! This is the board that esphome auto-detected on initial set-up (apologies if this is not the correct terminology). Is there a different method for determining the correct board to put here? I may be mistaken in referring to it as “D1 Mini” (I’m very much a beginner in a lot of this).

In any case, everything compiles properly and works flawlessly - without the solenoid. Only when I add the code for the solenoid does the problem present itself.

If you have wrong board, it can cause several problems .
I’m sure you can recognize Esp01 from D1 mini…

Thank you for your help, I tried this again with board: d1_mini with the same results. I’ve been using no-name cheapo boards for this and am noticing a lot of little issues beyond this problem (components getting very hot, certain pins not outputting, etc.) so I may have to chalk this up to getting what I paid for.

I’m going to pick up a different (more reputable) board and try again, and see if that helps at all.

This is certainly from wrong board setup. Esp01 has only few pins available.

Thank you but I don’t think that’s the issue in this particular case. I have multiples of this board and one seemed to have a dead pin. I tried the exact same configuration on a replacement (same esp01 type board) and had no issues. I think the issue is a quality control one with the boards I purchased.

Again, could be wrong, I’m not an expert, but this setup (minus solenoid) was working just fine both in esphome and also when I initially was tinkering thru arduino IDE. The only issue is the motor stutter when the solenoid is added to the mix. In any case I’ve ordered a different board to test this theory.

Thanks again for your help!

Can be bad board or power issue, or code…
Anyway it’s important to have correct board defined!

Do you mean just solenoid output on code or also solenoid wired?
I mean, if you disconnect solenoid while leaving output in yaml, is your motor working??

Good call on checking this - can’t believe I didn’t think to do this. When I disconnect the solenoid and leave the code in yaml, the motor works as expected. I think this would suggest a power issue, yeah?

For reference I’m using a 12V/3A PSU and this 5V mini push-pull solenoid (wired with mosfet).

power issue or interferences or wrong type of mosfet or…
show me the solenoid circuit with component list.

Just updating this topic to report that I found a solution: I replaced the D1 mini with an ESP32S dev board and the original code works fabulously.

That’s most important. :+1:
But it’s just kind of “solution”. You still don’t know what was wrong…

Technically you didnt include a solenoid “output”. You’ve used a gpio_switch. Depending on how you are driving the solenoid it can make a big difference. A gpio switch is either fully On or fully Off and would be used for toggling a relay for example. A gpio_output uses PWM and would be more for driving a motor or a mosfet for example.

You wrote a short story as your post and instead should have used a few sentences to describe the problem and then listed actual details like which components your using. Model numbers, product links, pictures, etc. Thats the stuff you should include and not stories about being dropped on your head as a baby or being a mouth breather, whatever it was you said, none of it is helpful.

The di mini or any esp board doesnt “handle” them, thats what you have a power supply for, it handles things and the esp sends directions and handles that just fine.

Did you bother to even look at the documentation for steppers or is this a copy/paste while cluelessly following the guide situation? I had to go look at it and refresh my memory. One thing that stood out was your speed. Where did 2000 steps/s come from? That seems waaaaay to high.

What does the nema17 spec sheet recommend? You read it, right? Or hopefully looked at the documentation where this is mentioned… 250 steps/s.

Thanks for your input.

Wow! Your tone is very rude and condescending. Is this the way you speak to people in real life? It’s incredibly abrasive. I couldn’t imagine talking like this to anyone, let alone a stranger and fellow hobbyist reaching out for help and trying to learn.

So, sorry I “wrote a short story.” I don’t think my post is any longer than the average post on here. I’ll note that my initial post (301 words) is similar in length to your comment (237 words). My attempt to inject some self-deprecation and humility seems to have just made you angry, but I suspect that’s more of a you problem.

For the record, my initial post does include details like what components I’m using (NEMA 17 motor, TMC2209 driver, D1 mini dev board) and includes a detailed picture of my schematic, which includes MORE component information (AOD210 mosfet, 1N4001 diode, XL7015 step down buck converter, etc.). Perhaps next time, before making snarky comments, you could check if you’ve had a failure of reading comprehension?

Thank you for your patronizingly-phrased question! Yes, I always do my best to read up on the documentation before reaching out for help. I did see that 250 steps/s limit, but also found other forum comments that suggest using a higher value when using a TMC2208 or TMC2209 with the A4988 esphome component (related to how the different drivers handle microstepping) - see here:

I tried all sorts of different speeds (100-2000 steps/s) while troubleshooting, and indeed the software seems to cap speed at about 2000 when using the TMC2209 - if you enter, say, 4000, it will run at the same speed as 2000. The motor stutter issue was present at every speed I tried; if anything, it was worse when running at slow speed (~100 steps/s).

Anyway, as I stated in a previous comment, I have already found a setup that is working for my needs. I replaced the D1 mini with an ESP32S dev board. I have since also changed the mosfet to a TIP102 transistor per this schematic, which has improved performance even more (ever so slightly smoother motor, lower heat from the transistor vs. the mosfet).

Thanks again for your input and have a nice day!