ESPHome 2026.1.4
Loading...
Searching...
No Matches
task_log_buffer_libretiny.cpp
Go to the documentation of this file.
1#ifdef USE_LIBRETINY
2
5#include "esphome/core/log.h"
6
7#ifdef USE_ESPHOME_TASK_LOG_BUFFER
8
9namespace esphome::logger {
10
12 this->size_ = total_buffer_size;
13 // Allocate memory for the circular buffer using ESPHome's RAM allocator
14 RAMAllocator<uint8_t> allocator;
15 this->storage_ = allocator.allocate(this->size_);
16 // Create mutex for thread-safe access
17 this->mutex_ = xSemaphoreCreateMutex();
18}
19
21 if (this->mutex_ != nullptr) {
22 vSemaphoreDelete(this->mutex_);
23 this->mutex_ = nullptr;
24 }
25 if (this->storage_ != nullptr) {
26 RAMAllocator<uint8_t> allocator;
27 allocator.deallocate(this->storage_, this->size_);
28 this->storage_ = nullptr;
29 }
30}
31
32size_t TaskLogBufferLibreTiny::available_contiguous_space() const {
33 if (this->head_ >= this->tail_) {
34 // head is ahead of or equal to tail
35 // Available space is from head to end, plus from start to tail
36 // But for contiguous, just from head to end (minus 1 to avoid head==tail ambiguity)
37 size_t space_to_end = this->size_ - this->head_;
38 if (this->tail_ == 0) {
39 // Can't use the last byte or head would equal tail
40 return space_to_end > 0 ? space_to_end - 1 : 0;
41 }
42 return space_to_end;
43 } else {
44 // tail is ahead of head
45 // Available contiguous space is from head to tail - 1
46 return this->tail_ - this->head_ - 1;
47 }
48}
49
51 if (message == nullptr || text == nullptr) {
52 return false;
53 }
54
55 // Check if buffer was initialized successfully
56 if (this->mutex_ == nullptr || this->storage_ == nullptr) {
57 return false;
58 }
59
60 // Try to take mutex without blocking - if busy, we'll get messages next loop iteration
61 if (xSemaphoreTake(this->mutex_, 0) != pdTRUE) {
62 return false;
63 }
64
65 if (this->head_ == this->tail_) {
66 xSemaphoreGive(this->mutex_);
67 return false;
68 }
69
70 // Read message header from tail
71 LogMessage *msg = reinterpret_cast<LogMessage *>(this->storage_ + this->tail_);
72
73 // Check for padding marker (indicates wrap-around)
74 // We check the level field since valid levels are 0-7, and 0xFF indicates padding
75 if (msg->level == PADDING_MARKER_LEVEL) {
76 // Skip to start of buffer and re-read
77 this->tail_ = 0;
78 msg = reinterpret_cast<LogMessage *>(this->storage_);
79 }
80 *message = msg;
81 *text = msg->text_data();
82 this->current_message_size_ = message_total_size(msg->text_length);
83
84 // Keep mutex held until release_message_main_loop()
85 return true;
86}
87
89 // Advance tail past the current message
90 this->tail_ += this->current_message_size_;
91
92 // Handle wrap-around if we've reached the end
93 if (this->tail_ >= this->size_) {
94 this->tail_ = 0;
95 }
96
97 this->message_count_--;
98 this->current_message_size_ = 0;
99
100 xSemaphoreGive(this->mutex_);
101}
102
103bool TaskLogBufferLibreTiny::send_message_thread_safe(uint8_t level, const char *tag, uint16_t line,
104 TaskHandle_t task_handle, const char *format, va_list args) {
105 // First, calculate the exact length needed using a null buffer (no actual writing)
106 va_list args_copy;
107 va_copy(args_copy, args);
108 int ret = vsnprintf(nullptr, 0, format, args_copy);
109 va_end(args_copy);
110
111 if (ret <= 0) {
112 return false; // Formatting error or empty message
113 }
114
115 // Calculate actual text length (capped to maximum size)
116 static constexpr size_t MAX_TEXT_SIZE = 255;
117 size_t text_length = (static_cast<size_t>(ret) > MAX_TEXT_SIZE) ? MAX_TEXT_SIZE : ret;
118
119 // Calculate total size needed (header + text length + null terminator)
120 size_t total_size = message_total_size(text_length);
121
122 // Check if buffer was initialized successfully
123 if (this->mutex_ == nullptr || this->storage_ == nullptr) {
124 return false; // Buffer not initialized, fall back to direct output
125 }
126
127 // Try to acquire mutex without blocking - don't block logging tasks
128 if (xSemaphoreTake(this->mutex_, 0) != pdTRUE) {
129 return false; // Mutex busy, fall back to direct output
130 }
131
132 // Check if we have enough contiguous space
133 size_t contiguous = this->available_contiguous_space();
134
135 if (contiguous < total_size) {
136 // Not enough contiguous space at end
137 // Check if we can wrap around
138 size_t space_at_start = (this->head_ >= this->tail_) ? this->tail_ : 0;
139 if (space_at_start > 0) {
140 space_at_start--; // Leave 1 byte gap to distinguish full from empty
141 }
142
143 // Need at least enough space to safely write padding marker (level field is at end of struct)
144 constexpr size_t PADDING_MARKER_MIN_SPACE = offsetof(LogMessage, level) + 1;
145
146 if (space_at_start >= total_size && this->head_ > 0 && contiguous >= PADDING_MARKER_MIN_SPACE) {
147 // Add padding marker (set level field to indicate this is padding, not a real message)
148 LogMessage *padding = reinterpret_cast<LogMessage *>(this->storage_ + this->head_);
149 padding->level = PADDING_MARKER_LEVEL;
150 this->head_ = 0;
151 } else {
152 // Not enough space anywhere, or can't safely write padding marker
153 xSemaphoreGive(this->mutex_);
154 return false;
155 }
156 }
157
158 // Write message header
159 LogMessage *msg = reinterpret_cast<LogMessage *>(this->storage_ + this->head_);
160 msg->level = level;
161 msg->tag = tag;
162 msg->line = line;
163
164 // Store the thread name now to avoid crashes if task is deleted before processing
165 const char *thread_name = pcTaskGetTaskName(task_handle);
166 if (thread_name != nullptr) {
167 strncpy(msg->thread_name, thread_name, sizeof(msg->thread_name) - 1);
168 msg->thread_name[sizeof(msg->thread_name) - 1] = '\0';
169 } else {
170 msg->thread_name[0] = '\0';
171 }
172
173 // Format the message text directly into the buffer
174 char *text_area = msg->text_data();
175 ret = vsnprintf(text_area, text_length + 1, format, args);
176
177 if (ret <= 0) {
178 xSemaphoreGive(this->mutex_);
179 return false;
180 }
181
182 // Remove trailing newlines
183 while (text_length > 0 && text_area[text_length - 1] == '\n') {
184 text_length--;
185 }
186
187 msg->text_length = text_length;
188
189 // Advance head
190 this->head_ += total_size;
191
192 // Handle wrap-around (shouldn't happen due to contiguous space check, but be safe)
193 if (this->head_ >= this->size_) {
194 this->head_ = 0;
195 }
196
197 this->message_count_++;
198
199 xSemaphoreGive(this->mutex_);
200 return true;
201}
202
203} // namespace esphome::logger
204
205#endif // USE_ESPHOME_TASK_LOG_BUFFER
206#endif // USE_LIBRETINY
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:1422
void deallocate(T *p, size_t n)
Definition helpers.h:1480
T * allocate(size_t n)
Definition helpers.h:1442
bool borrow_message_main_loop(LogMessage **message, const char **text)
bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, TaskHandle_t task_handle, const char *format, va_list args)
const char * message
Definition component.cpp:38