Inline yield_with_select_ for ESP8266/RP2040 (socket_delay) and
no-socket (delay) paths in addition to the LWIP_FAST_SELECT path.
Only the select() fallback (host platform) remains in the .cpp.
This ensures yield_with_select_ is inlined into the loop on all
embedded platforms, not just ESP32/LibreTiny.
The play_complex overrides cost flash per template instantiation
across every automation. The always_inline on the forwarding chain
(Trigger::trigger, Automation::trigger, ActionList::play) is a
fixed cost that collapses 3 frames into 1.
LambdaAction and ContinuationAction overrides caused flash bloat
by replacing shared base class play_complex instantiations with
per-class copies. StatelessLambdaAction is the most common
automation action and its no-args variant is only 24 bytes.
The always_inline on Trigger::trigger(), ActionList::play(), and
Automation::trigger() duplicates code at every trigger call site,
adding ~384 bytes of flash. Revert to compiler-managed inlining
and keep only the play_complex overrides (phase 2).
When USE_LWIP_FAST_SELECT is defined (ESP32/LibreTiny), move the
yield_with_select_ implementation to application.h as always_inline.
This eliminates another stack frame from the hot loop path on these
platforms. The fallback select()/delay() paths for other platforms
remain in application.cpp.
Force-inline the Trigger→Automation→ActionList forwarding chain
and override play_complex() in leaf action classes to skip the
virtual play() dispatch, reducing the button→lambda call stack
from 8 frames to ~4.
Move Application::loop() from application.cpp to application.h as
inline ESPHOME_ALWAYS_INLINE so the compiler can inline it at all
call sites. On ESP32, loop_task() now calls App.loop() directly
instead of going through the generated loop() wrapper.
This eliminates one stack frame from the main loop call chain on
all platforms, producing cleaner crash backtraces and reducing
function call overhead on every loop iteration.
Without these flags, libsodium's sodium_memzero() falls through to the
slowest fallback: a volatile byte-by-byte zeroing loop. With them, it
uses memset() guarded by a weak-symbol call and inline asm memory
clobber, which is significantly faster.
This also improves sodium_memcmp() and sodium_compare() codegen by
allowing non-volatile pointer access with weak barriers, and enables
a timing-safe comparison optimization in crypto_verify.
Both flags are safe for all ESPHome targets: __attribute__((weak)) and
__asm__ are core GCC features supported by all toolchains (Xtensa,
RISC-V, ARM, x86_64).
On RP2040, get_mac_address_raw() only queried WiFi.macAddress(). When
ethernet is configured without WiFi, the MAC address was left
uninitialized, causing get_mac_address() and get_mac_address_pretty()
to return all zeros. This affects device identification (API, mDNS,
unique ID).
Unlike ESP32 which reads from eFuse (always available regardless of
network interface), RP2040 must query the actual network driver.
Fall back to ethernet::global_eth_component->get_eth_mac_address_raw()
when USE_ETHERNET is defined and USE_WIFI is not.
Per swoboda1337's suggestion, run the heater after the measurement
read instead of before. This maximizes cooldown time before the
next reading and avoids skipping any measurement cycles.
Only advance last_heater_millis_ after the heater command succeeds
so a failed write retries on the next cycle instead of silently
skipping.
Add heater settings to the test YAML to ensure CI exercises the
heater code path.
When heater_interval < update_interval, the heater would fire on
nearly every update cycle, skipping almost all real measurements.
Push the next eligible heater time forward by at least one
update_interval to guarantee at least one ambient reading between
heater activations.
Integrate heater activation into the measurement cycle instead of
running it on an independent timer. The separate heater interval
could overlap with measurement polling, causing readings to be
taken while the sensor was still hot.
Now the heater fires during update() when due, skipping that
measurement cycle. The next regular update() takes an ambient
reading after the sensor has cooled.
Also replace magic numbers with named constexpr constants for
the datasheet conversion formulas.
Closes https://github.com/esphome/esphome/issues/15011