#pragma once #ifdef USE_ESP32 #include "esphome/core/defines.h" #include "esphome/core/helpers.h" #ifdef USE_ESPHOME_TASK_LOG_BUFFER #include #include #include #include #include namespace esphome::logger { /** * @brief Task log buffer for ESP32 platform using FreeRTOS ring buffer. * * Threading Model: Multi-Producer Single-Consumer (MPSC) * - Multiple FreeRTOS tasks can safely call send_message_thread_safe() concurrently * - Only the main loop task calls borrow_message_main_loop() and release_message_main_loop() * * This uses the FreeRTOS ring buffer (RINGBUF_TYPE_NOSPLIT) which provides * built-in thread-safety for the MPSC pattern. The ring buffer ensures * message integrity - each message is stored contiguously. * * Design: * - Variable-size messages with header + text stored contiguously * - FreeRTOS ring buffer handles synchronization internally * - Atomic counter for fast has_messages() check without ring buffer lock */ class TaskLogBuffer { public: // Structure for a log message header (text data follows immediately after) struct LogMessage { const char *tag; // We store the pointer, assuming tags are static char thread_name[16]; // Store thread name directly (only used for non-main threads) uint16_t text_length; // Length of the message text (up to ~64KB) uint16_t line; // Source code line number uint8_t level; // Log level (0-7) // Methods for accessing message contents inline char *text_data() { return reinterpret_cast(this) + sizeof(LogMessage); } inline const char *text_data() const { return reinterpret_cast(this) + sizeof(LogMessage); } }; TaskLogBuffer(); ~TaskLogBuffer(); // NOT thread-safe - borrow a message from the ring buffer, only call from main loop bool borrow_message_main_loop(LogMessage *&message, uint16_t &text_length); // NOT thread-safe - release a message buffer and update the counter, only call from main loop void release_message_main_loop(); // Thread-safe - send a message to the ring buffer from any thread bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name, const char *format, va_list args); // Check if there are messages ready to be processed using an atomic counter for performance inline bool HOT has_messages() const { return message_counter_.load(std::memory_order_relaxed) != last_processed_counter_; } // Get the total buffer size in bytes static constexpr size_t size() { return ESPHOME_TASK_LOG_BUFFER_SIZE; } private: RingbufHandle_t ring_buffer_{nullptr}; // FreeRTOS ring buffer handle StaticRingbuffer_t structure_; // Static structure for the ring buffer uint8_t storage_[ESPHOME_TASK_LOG_BUFFER_SIZE]; // Embedded in Logger (no separate heap allocation) // Atomic counter for message tracking (only differences matter) std::atomic message_counter_{0}; // Incremented when messages are committed mutable uint16_t last_processed_counter_{0}; // Tracks last processed message void *current_token_{nullptr}; }; } // namespace esphome::logger #endif // USE_ESPHOME_TASK_LOG_BUFFER #endif // USE_ESP32