mirror of
https://github.com/esphome/esphome.git
synced 2026-09-17 10:08:40 +00:00
[esp32] Use ESP-IDF Log V2 to reduce flash usage
Switch from ESP-IDF Log V1 to V2, which centralizes log formatting inside esp_log() instead of expanding esp_log_timestamp(), color codes, and LOG_FORMAT() at every ESP_LOGx macro call site. This saves ~9KB of flash by eliminating ~500 per-site macro expansions in ESP-IDF library code (gpio, ethernet, mdns, uart, wifi, etc.). Override esp_log_format() to skip ESP-IDF's own formatting after the ESPHome logger hook is installed, since ESPHome does its own formatting. For early boot and constrained environments (ISR, cache disabled), format messages in ESPHome style with colors using a stack buffer.
This commit is contained in:
@@ -1614,6 +1614,11 @@ async def to_code(config):
|
||||
# This saves ~250 bytes of RAM (tag cache) and associated code
|
||||
add_idf_sdkconfig_option("CONFIG_LOG_TAG_LEVEL_IMPL_NONE", True)
|
||||
|
||||
# Use ESP-IDF Log V2 to eliminate per-site esp_log_timestamp() macro expansions
|
||||
# V2 centralizes formatting inside esp_log(), reducing flash usage
|
||||
add_idf_sdkconfig_option("CONFIG_LOG_VERSION_1", False)
|
||||
add_idf_sdkconfig_option("CONFIG_LOG_VERSION_2", True)
|
||||
|
||||
# Reduce PHY TX power in the event of a brownout
|
||||
add_idf_sdkconfig_option("CONFIG_ESP_PHY_REDUCE_TX_POWER", True)
|
||||
|
||||
|
||||
@@ -86,3 +86,73 @@ int HOT esp_idf_log_vprintf_(const char *format, va_list args) { // NOLINT
|
||||
#endif
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#if defined(USE_ESP32) && !defined(BOOTLOADER_BUILD)
|
||||
// Override esp_log_format to disable ESP-IDF's own log formatting so that
|
||||
// the vprintf hook receives a single call per message (just the user's format
|
||||
// string + args). Without this, Log V2 makes 3 vprintf calls per message
|
||||
// (header, body, newline) which fragments the output in ESPHome's logger.
|
||||
// This strong definition overrides the archive symbol from ESP-IDF's liblog.
|
||||
// It affects all callers including precompiled blobs (e.g. wifi).
|
||||
//
|
||||
// Before the ESPHome logger hook is installed (early boot), we fall through
|
||||
// to the original ESP-IDF formatting so boot messages have proper formatting.
|
||||
#include <esp_private/log_message.h>
|
||||
#include <esp_private/log_print.h>
|
||||
#include <esp_private/log_format.h>
|
||||
#include <esp_log_write.h>
|
||||
|
||||
// Outlined cold path for early boot / constrained environment logging.
|
||||
// Uses esp_log_printf/esp_log_vprintf which dispatch to esp_rom_vprintf
|
||||
// for constrained environments (same as ESP-IDF's original esp_log_format).
|
||||
// Must be in IRAM since it's called from the IRAM esp_log_format during
|
||||
// early boot when the scheduler isn't running (constrained_env=1).
|
||||
static void IRAM_ATTR __attribute__((noinline)) esp_log_format_early_(esp_log_msg_t *message) {
|
||||
// ESP-IDF levels: NONE=0 ERROR=1 WARN=2 INFO=3 DEBUG=4 VERBOSE=5
|
||||
// Color digits: E=1(red) W=3(yellow) I=2(green) D=6(cyan) V=7(gray)
|
||||
// DRAM_ATTR required since this function is in IRAM and can't access flash constants
|
||||
static DRAM_ATTR const char color_digit[] = {'\0', '1', '3', '2', '6', '7'};
|
||||
static DRAM_ATTR const char lvl[] = {'\0', 'E', 'W', 'I', 'D', 'V'};
|
||||
uint8_t level = message->config.opts.log_level;
|
||||
#if CONFIG_LIBC_NEWLIB
|
||||
if (!message->config.opts.constrained_env) {
|
||||
flockfile(stdout);
|
||||
}
|
||||
#endif
|
||||
if (level > 0 && level < sizeof(lvl)) {
|
||||
esp_log_printf(message->config, "\033[0;3%cm[%c][%s:000]: ", color_digit[level], lvl[level],
|
||||
message->tag ? message->tag : "esp-idf");
|
||||
}
|
||||
esp_log_vprintf(message->config, message->format, message->args);
|
||||
if (level > 0 && level < sizeof(lvl)) {
|
||||
esp_log_printf(message->config, "\033[0m\n");
|
||||
} else {
|
||||
esp_log_printf(message->config, "\n");
|
||||
}
|
||||
#if CONFIG_LIBC_NEWLIB
|
||||
if (!message->config.opts.constrained_env) {
|
||||
funlockfile(stdout);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// IRAM_ATTR required because ESP-IDF places esp_log_format in IRAM when
|
||||
// CONFIG_LOG_IN_IRAM is enabled, and it may be called from constrained
|
||||
// environments (ISR, cache disabled) where flash is inaccessible.
|
||||
void IRAM_ATTR esp_log_format(esp_log_msg_t *message) {
|
||||
// Check if ESPHome's vprintf hook is installed by comparing against default.
|
||||
// Before logger init, esp_log_vprint_func == &vprintf (the default).
|
||||
extern vprintf_like_t esp_log_vprint_func;
|
||||
extern int vprintf(const char *, __gnuc_va_list); // NOLINT
|
||||
if (esp_log_vprint_func == &vprintf || message->config.opts.constrained_env) [[unlikely]] {
|
||||
// Early boot or constrained env (ISR, cache disabled):
|
||||
// use ROM functions only — flash may be inaccessible.
|
||||
esp_log_format_early_(message);
|
||||
return;
|
||||
}
|
||||
// After hook installed, normal environment: skip formatting, forward body only
|
||||
esp_log_vprintf(message->config, message->format, message->args);
|
||||
}
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user