Convert rotary_encoder register_listener, canbus add_callback,
sdl add_key_listener, and udp add_listener from std::function to
templates to avoid the heap-allocation fallback path.
MQTT backend set_on_* methods are virtual and cannot be templatized.
Pass wrapping lambdas directly to CallbackManager::add() instead of
first wrapping in std::function. The lambdas capture [this, callback]
(two pointers) so they hit the heap path either way, but this avoids
double-wrapping and the extra std::function overhead.
Also fix "deallaction" typo.
Convert haier, nextion, microphone, ezo, uart, speaker, zigbee,
and hlk_fm22x callback methods that were missed by the initial
conversion (they used add_*_callback instead of add_on_*_callback).
Use (*ptr)(args...) instead of ptr->operator()(args...) so the
inline path works correctly for function pointers, which are
trivially copyable and pointer-sized but lack operator().
Replace char-by-char loops with __builtin_memcpy in the inline
Callback storage path. Generates identical code but is cleaner
and avoids clang-analyzer false positives without NOLINT annotations.
Convert all remaining component add_on_*_callback methods from
std::function to templates so lambdas flow through without wrapping.
This avoids a heap allocation regression where passing std::function
through Callback::create would heap-allocate the std::function itself.
Also add NOLINT annotations for clang-analyzer false positive on
byte-wise copy in Callback::create.
Replace std::function with a lightweight type-erased Callback struct
(8 bytes on 32-bit vs 16 for std::function) in CallbackManager and
LazyCallbackManager. Using C++20 if constexpr, small trivially-copyable
callables like [this] lambdas (81% of all callback registrations) are
stored inline in the function pointer context without heap allocation.
This eliminates std::function null checks (and __throw_bad_function_call)
from all entity callback paths, and templatizes add_on_*_callback methods
across all entity types so lambdas flow through without being wrapped in
std::function first.
Also converts EntityStateBase from hand-rolled nullable CallbackManager
pointers to LazyCallbackManager for consistency.
Measured flash savings:
- ESP8266 simple: -160 B
- ESP8266 ratgdo: -592 B
- ESP32 IDF (large): -456 B
- ESP32 IDF (minimal): -212 B
Create per-platform logger_*.h headers with inline write_msg_()
for ESP32, ESP8266, RP2040, and LibreTiny. These are all one-liners
that benefit from inlining into write_to_console_(), eliminating
function call overhead on every log message.
Host and Zephyr implementations remain in .cpp files as they are
more complex (timestamp formatting, printk + uart loop).
Create per-platform logger_*.h headers with inline write_msg_()
for ESP32, ESP8266, RP2040, and LibreTiny. These are all one-liners
that benefit from inlining into write_to_console_(), eliminating
function call overhead on every log message.
Host and Zephyr implementations remain in .cpp files as they are
more complex (timestamp formatting, printk + uart loop).
Replace write_() call with direct char stores for the 4-byte
ANSI reset sequence ("\033[0m"). This eliminates function call
overhead (entry/retw, bounds check, memcpy setup) on every log
message and avoids a rodata string reference that consumes RAM
on ESP8266.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
On IDF 5.x, the ESP-IDF headers use CONFIG_ETH_SPI_ETHERNET_W5500 and
CONFIG_ETH_SPI_ETHERNET_DM9051 to conditionally expose the W5500/DM9051
types. Static analysis (clang-tidy) needs both the new USE_ETHERNET_*
defines and the old CONFIG_* defines to resolve all symbols.
On IDF 5.x these headers don't exist as standalone files — they are
included internally via esp_eth.h. On IDF 6.0 they moved to registry
components and need explicit includes.
LAN867x is unchanged (external component since IDF 5.3).
pioarduino only patches esp_eth_phy_new_jl1101() into IDF 5.4.2-5.x,
not IDF 6.0. Update guards in both the .c driver and .h declaration
to also compile when IDF >= 6.0.
Guard logic: compile custom driver when JL1101 configured AND
(IDF >= 6.0 OR IDF < 5.4.2 OR not PlatformIO)
- Exclude esp_eth_phy_jl1101.c when JL1101 is not configured
- Exclude on IDF 5.4.2-5.x where pioarduino has it builtin
- Keep custom driver for IDF < 5.4.2 and IDF 6.0+ (pioarduino may
not have patched JL1101 into their IDF 6.0 fork yet)
- Always use esp_eth_phy_new_jl1101() in C++ (no generic fallback)
- Namespace ethernet type in CORE.data[KEY_ETHERNET]
Only exclude esp_eth_phy_jl1101.c at the Python level on IDF >= 6.0.
On IDF 5.4.2+ with PlatformIO, the file's own preprocessor guard
(checking the C-level PLATFORMIO define) compiles it to empty.
- Add explicit includes for per-chip PHY/MAC headers (required on IDF 6.0
where they are no longer pulled in via esp_eth.h, also works on IDF 5.x)
- Exclude esp_eth_phy_jl1101.c from compilation on IDF >= 6.0 (uses
generic PHY) and IDF >= 5.4.2 with PlatformIO (uses builtin driver)