The compile-time loop() override detection using decltype(&T::loop)
fails when a component inherits from both Component (via
PollingComponent) and BLEClientNode, as both define loop() methods
making the expression ambiguous.
Add a SFINAE-based has_loop_override<T> trait that gracefully handles
this case: when decltype(&T::loop) is ill-formed due to ambiguity,
it falls back to true (conservatively assuming a loop override exists).
The Python tests were testing a Python reimplementation, not the
actual C++ code. Replace with an integration test that exercises
the real micros_to_millis() on the host platform, covering boundary
values, carry paths, and mod-8 shift edge cases (25 checks).
Make micros_to_millis a template on return type (default uint32_t).
millis_64() now calls micros_to_millis<uint64_t>() to eliminate
__udivdi3 there too — same Euclidean decomposition, just with a
32x32->64 multiply for hi*Q instead of truncating.
Add 64-bit variant tests (140 total, all passing).
Add Python unit tests (70 cases) verifying the Euclidean decomposition
matches us // 1000 across edge cases: small values, hi/lo boundaries,
carry-path overflow, realistic uptimes, and shift-boundary mod-8
variations.
Make micros_to_millis constexpr to enable compile-time validation
via static_assert.
On 32-bit targets GCC does not optimize 64-bit constant division
into a multiply-by-reciprocal, emitting a call to __udivdi3 instead
(~650-710 ns on Xtensa @ 240 MHz).
Add micros_to_millis() which exploits 1000 = 8 * 125: a free
right-shift by 3 followed by Euclidean decomposition with D=125,
reducing the 64-bit division to a single 32-bit / 125U that GCC
compiles to a multiply-by-reciprocal.
Benchmarked at 258 ns per call on ESP32 classic — a 2.5-2.8x
speedup. With ~21 millis() calls per loop iteration this saves
~9 us per loop.
Factor 1000 as 8 * 125. The divide-by-8 is a free right-shift,
then Euclidean decomposition with D=125 has a smaller remainder
(R=46 vs R=296), making hi*R cheaper and the safe input range
51x wider (~3.2e18 vs ~6.2e16 microseconds).
Benchmarked ~7% faster than the divide-by-1000 variant on ESP32
classic (257 ns vs 277 ns per call at 240 MHz).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On 32-bit targets, GCC does not optimize 64-bit constant division
into a multiply-by-reciprocal and instead calls __udivdi3 (software
64-bit divide). This replaces the division with Euclidean
decomposition using a single 32-bit divide that GCC optimizes into
a multiply-by-reciprocal.
Benchmarked on ESP32 classic (Xtensa LX6, 240 MHz):
__udivdi3: 628-705 ns
fast_div1000_32: 269-279 ns (2.3-2.6x faster)
millis() is called ~21 times per main loop iteration. At ~350 ns
savings per call, this saves ~7 us per loop iteration.