diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index b42496af66..a2c2aa0320 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -48,6 +48,11 @@ void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const ch // For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered message_sent = this->log_buffer_->send_message_thread_safe(level, tag, static_cast(line), current_task, format, args); + if (message_sent) { + // Enable logger loop to process the buffered message + // This is safe to call from any context including ISRs + this->enable_loop_soon_any_context(); + } #endif // USE_ESPHOME_TASK_LOG_BUFFER // Emergency console logging for non-main tasks when ring buffer is full or disabled @@ -139,6 +144,10 @@ Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate #ifdef USE_ESPHOME_TASK_LOG_BUFFER void Logger::init_log_buffer(size_t total_buffer_size) { this->log_buffer_ = esphome::make_unique(total_buffer_size); + + // Start with loop disabled when using task buffer (unless using USB CDC) + // The loop will be enabled automatically when messages arrive + this->disable_loop_when_buffer_empty_(); } #endif @@ -189,6 +198,10 @@ void Logger::loop() { this->write_msg_(this->tx_buffer_); } } + } else { + // No messages to process, disable loop if appropriate + // This reduces overhead when there's no async logging activity + this->disable_loop_when_buffer_empty_(); } #endif } diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index ea82764393..38faf73d84 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -358,6 +358,26 @@ class Logger : public Component { static const uint16_t RESET_COLOR_LEN = strlen(ESPHOME_LOG_RESET_COLOR); this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size); } + +#ifdef USE_ESP32 + // Disable loop when task buffer is empty (with USB CDC check) + inline void disable_loop_when_buffer_empty_() { + // Thread safety note: This is safe even if another task calls enable_loop_soon_any_context() + // concurrently. If that happens between our check and disable_loop(), the enable request + // will be processed on the next main loop iteration since: + // - disable_loop() takes effect immediately + // - enable_loop_soon_any_context() sets a pending flag that's checked at loop start +#if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO) + // Only disable if not using USB CDC (which needs loop for connection detection) + if (this->uart_ != UART_SELECTION_USB_CDC) { + this->disable_loop(); + } +#else + // No USB CDC support, always safe to disable + this->disable_loop(); +#endif + } +#endif }; extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index c9fea90386..8abd6598f7 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -132,6 +132,8 @@ // ESP32-specific feature flags #ifdef USE_ESP32 +#define USE_ESPHOME_TASK_LOG_BUFFER + #define USE_BLUETOOTH_PROXY #define USE_CAPTIVE_PORTAL #define USE_ESP32_BLE