fix: CheckDelay returns false forever when timedly is negative (-S2) #5
Reference in New Issue
Block a user
Delete Branch "fix/checkdelay-negative-timedly"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Upstream OpenInverter bug surfaced after recent SKUDAK PRs invalidated saved params. Bench symptom (reported by Kyle): teslacharger detects EVSE, shows AC limit, but stays stuck in WaitStart and never proceeds to Enable / Activate / Run.
This is not a regression from any SKUDAK code change —
git log -- src/charger.cppshows no SKUDAK commit has touched the state machine. But every SKUDAK PR that bumped the param table (uaux broadcast, expanded 0x210, 0x210→0x211, version suffix) invalidates the saved-params CRC and resetstimedlyto its-1default — which is when the latent upstream bug surfaces.Root cause
src/charger.cpp:85-91:With
timedly = -1(the param's documented "no delay" sentinel and its default):Param::GetInt(timedly)-1(signed int)-1 * 60-60(signed int math)uint32_t start0xFFFFFFC4≈ 4.29 billionstart <= 0(now - startTime) > 4294967236CheckDelay()returnsfalseforever; charger wedges in WaitStart.Fix
Use signed math for the short-circuit so
timedly <= 0still means "start immediately":The
(uint32_t)startcast is only reached whenstart > 0(short-circuit OR), so it can't reinterpret a negative as huge.(now - startTime)isuint32_t - uint32_t— well-defined modular arithmetic, handles RTC wraparound correctly.timedlystart-1(no-delay sentinel)-60start <= 0true00start <= 0true5(5 min)30010000(max)600000intrangeNote on
CheckTimeout()The companion
CheckTimeout()at line 75 has the sameuint32_tpattern but works "correctly by accident" becausetimelim = -1makestimeouthuge and(now - startTime) > timeoutis false, which matches the intended "no timeout" semantics. Leaving that alone — fixing it would change behaviour, not fix bugs.Version suffix
VER suffix bumped
-S1 → -S2so the OI web UI clearly shows the post-fix firmware. Verified in built binary as4=1.19.R-S2.Build
Links clean; pre-existing pedantic warning on
extern "C" int mainunchanged.Upstream consideration
This bug is also present in
johanneshuebner/stm32-charger. Worth a PR upstream once we've confirmed the fix on the bench.Test plan
4=1.19.R-S2timedly = -1(default), plug EVSE → state should progress past WaitStart within secondstimedly = 5(5 min) → state should sit in WaitStart for ~5 min before progressing (verifies positive-delay still works)🤖 Generated with Claude Code
Upstream openinverter bug surfaced after recent SKUDAK PRs. Symptom (reported by Kyle on the bench): teslacharger detects EVSE, shows the AC limit, but stays stuck in WaitStart and never proceeds to Enable / Activate / Run. State machine looks like it's the OFF state but it's actually one step further on. Root cause in src/charger.cpp:85-91: bool CheckDelay() { uint32_t now = rtc_get_counter_val(); uint32_t start = Param::GetInt(Param::timedly) * 60; return start <= 0 || (now - startTime) > start; } With timedly = -1 (the param's documented "no delay" sentinel and its default value), the math is: Param::GetInt(timedly) -> -1 (signed int) -1 * 60 -> -60 (signed) assigned to uint32_t -> 0xFFFFFFC4 (~4.29 billion) start <= 0 -> false (uint32_t can't be negative) (now - startTime) > 4294967236 -> false for ~136 years So CheckDelay() returns false forever; charger wedges in WaitStart. Why "it was working before": every SKUDAK PR that bumped the param table (uaux broadcast, expanded 0x210, 0x210->0x211, version suffix) invalidates the saved-params CRC and resets timedly to its -1 default. Kyle had a non-default value (probably 0) saved before. Fix: signed math for the short-circuit so timedly <= 0 still means "start immediately." Cast to uint32_t only on the right operand, which is only evaluated when start > 0 — can't reinterpret negative as huge. The companion CheckTimeout() at line 75 has the same uint32_t pattern but works "correctly by accident" because timelim = -1 makes timeout huge and (now - startTime) > timeout is false, which matches the intended "no timeout" semantics. Leaving that alone — fixing it would change behaviour, not bugs. VER suffix bumped -S1 -> -S2 so the OI web UI clearly shows the deployed firmware (post-fix). Confirmed in built binary as "4=1.19.R-S2". Build: text=25192, links clean, pre-existing pedantic warning unchanged. This bug is also present upstream (johanneshuebner/stm32-charger); worth a PR upstream once we've confirmed the fix on the bench. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>