fix: use uint32_t for micros() timestamps to prevent 72-minute overflow#42
fix: use uint32_t for micros() timestamps to prevent 72-minute overflow#42amasolov wants to merge 1 commit intoslimcdk:masterfrom
Conversation
micros() returns uint32_t which wraps at 2^32 µs (~71.6 min). The stepper base class stored these values in time_t (int64_t on ESP-IDF v5+). After the wrap, (now - last) yields a massive negative delta instead of the correct small positive one, because signed subtraction does not wrap like unsigned. In PULSES_CONTROL mode this is fatal: the negative dt never satisfies the step-interval check, last_step_ is never updated, and the motor stops permanently. Changes: - stepper.h: last_calculation_ and last_step_ → uint32_t - stepper.cpp: calculate_speed_() and should_step_() parameter → uint32_t - tmc2209_stepper.cpp: loop() now uses uint32_t for timing; guard against vactual_ == 0 to avoid division by zero (+inf) - tmc2300_stepper.cpp: identical fix for the TMC2300 variant Fixes slimcdk#38 Made-with: Cursor
EvoSems
left a comment
There was a problem hiding this comment.
having similar issues with high dose steps
|
Thank you very much for looking into this, @amasolov. I haven't had the time to review this yet and may not for the next couple of weeks. Considering the PR number, it may be the answer (to everything). If anyone wants to test this PR, it is possible to reference it in your ESPHome config by adding external_components:
- source: github://slimcdk/esphome-custom-components@pull/42/head
components: [tmc2209_hub, tmc2209, stepper]
refresh: 0s@EvoSems can you clarify on what you mean? Do you experience those issues from current master branch or from this fix? |
|
Hardware tested: Full open/close cycle executed successfully at 93 minutes uptime (past the 2^32 µs rollover). Motor responded normally to move_to_position commands. Tested on ESP32 rev3.1 with TMC2209 in PULSES_CONTROL mode (StealthChop, 4 microsteps, 200 steps/s). |
|
Sorry it took so long to get it tested. Had to disassemble several things to do so. |
|
It's still working for me after ~18 hours. |
Hi mate. sorry I have been away from home. |
|
Thanks a lot for this! I can confirm that running with the fix, it's been working fine for more than a week without issues. I can confirm that both IDF and Arduino compilation, pulse and serial, as well as normal operation works. |
Summary
Fixes #38 — stepper stops responding permanently after ~71.6 minutes (2³² µs).
Root cause:
micros()returnsuint32_twhich wraps at 2³² µs. The stepper base class stored these values intime_t(int64_ton ESP-IDF v5+). After the wrap,now - lastyields a massive negative delta (~-4.29 billion) instead of the correct small positive one, because signed subtraction doesn't wrap like unsigned.In
PULSES_CONTROLmode this is fatal: the negativedtnever satisfies the step-interval check,last_step_is never updated (it's only set inside the if-block), and the motor stops permanently — not just for one cycle.Bugs fixed:
stepper.h—last_calculation_andlast_step_changed fromtime_ttouint32_tstepper.cpp—calculate_speed_()andshould_step_()parameter types changed touint32_t; explicit(uint32_t)cast on delta to ensure unsigned wraparoundtmc2209_stepper.cpp—loop()usesuint32_tfor all timing; added guard againstvactual_ == 0to prevent1/0.0f = +infmaking the step condition permanently falsetmc2300_stepper.cpp— identical fix applied (same bug pattern)Why
uint32_tfixes itWith unsigned 32-bit arithmetic, subtraction naturally handles rollover:
With signed 64-bit (
time_t):Status
🚧 Work in progress — compiles cleanly but not yet tested on hardware. Looking for someone with a long-running stepper setup to validate the fix survives past the 72-minute mark.
Test plan
PULSES_CONTROLmode for >75 minutes — confirm no stallSERIAL_CONTROLmode for >75 minutes — confirm no stallMade with Cursor