FastLED 3.10.8
Loading...
Searching...
No Matches
async_logger.cpp.hpp
Go to the documentation of this file.
1
3
5#include "fl/stl/cstdio.h"
7#include "fl/math/math.h"
8#include "fl/log/log.h" // For FL_ERROR macro
9#include "fl/task/task.h" // For fl::task
10#include "fl/task/scheduler.h" // For fl::task::Scheduler
11#include "fl/stl/noexcept.h"
12#include "fl/stl/compiler_control.h" // IWYU pragma: keep - FL_UNUSED
13
14namespace fl {
15
16namespace detail {
20 void printLoggerDisabledError(const char* category_name, const char* define_name) FL_NO_EXCEPT {
21 FL_UNUSED(category_name); // only consumed by FL_ERROR, a no-op on small platforms
22 FL_UNUSED(define_name);
23 FL_ERROR(category_name << " ASYNC LOGGING NOT ENABLED. Add '#define " << define_name << "' before including FastLED.h");
24 }
25} // namespace detail
26
27// ============================================================================
28// Background flush infrastructure (timer-based automatic flushing)
29// ============================================================================
30
31namespace detail {
32 // Global state for background flushing
34 volatile bool mNeedsFlush; // Flag set by ISR, cleared by service function
35 fl::isr::handle mTimerHandle; // ISR timer handle
36 fl::size mMessagesPerTick; // Max messages to flush per timer tick
37 bool mEnabled; // Whether background flushing is enabled
38
40 : mNeedsFlush(false)
41 , mTimerHandle() // Default constructor
43 , mEnabled(false) {}
44 };
45
47 BackgroundFlushState* state = static_cast<BackgroundFlushState*>(user_data);
48 state->mNeedsFlush = true;
49 // Debug: Toggle a counter to verify ISR is firing (visible in debugger)
50 static volatile fl::u32 isr_fire_count = 0;
51 isr_fire_count = isr_fire_count + 1;
52 }
53} // namespace detail
54
55// ============================================================================
56// AsyncLogger implementation using AsyncLogQueue backend (zero heap allocation)
57// ============================================================================
58
60
62 mQueue.push(msg);
63}
64
65void AsyncLogger::push(const char* msg) FL_NO_EXCEPT {
66 mQueue.push(msg);
67}
68
70 const char* msg;
71 fl::u16 len;
72
73 // 256-byte stack buffer - handles most messages in a single chunk
74 // Avoids heap allocation entirely by chunking long messages
75 char buffer[256];
76
77 while (mQueue.tryPop(&msg, &len)) {
78 fl::u16 offset = 0;
79
80 // Process message in chunks to avoid heap allocation
81 while (offset < len) {
82 fl::u16 chunk_size = min(len - offset, static_cast<fl::u16>(sizeof(buffer) - 1));
83 fl::memcpy(buffer, msg + offset, chunk_size);
84 buffer[chunk_size] = '\0'; // Null-terminate
85
86 offset += chunk_size;
87
88 if (offset >= len) {
89 // Last chunk or whole message - use println (adds newline)
90 fl::println(buffer);
91 } else {
92 // Not last chunk - use print (no newline)
93 fl::print(buffer);
94 }
95 }
96
97 mQueue.commit();
98 }
99}
100
102 return mQueue.size();
103}
104
106 return mQueue.empty();
107}
108
110 // Drain queue without printing
111 const char* msg;
112 fl::u16 len;
113 while (mQueue.tryPop(&msg, &len)) {
114 mQueue.commit();
115 }
116}
117
119 return mQueue.droppedCount();
120}
121
122fl::size AsyncLogger::flushN(fl::size maxMessages) FL_NO_EXCEPT {
123 fl::size flushed = 0;
124 const char* msg;
125 fl::u16 len;
126
127 // 256-byte stack buffer - handles most messages in a single chunk
128 // Avoids heap allocation entirely by chunking long messages
129 char buffer[256];
130
131 while (flushed < maxMessages && mQueue.tryPop(&msg, &len)) {
132 fl::u16 offset = 0;
133
134 // Process message in chunks to avoid heap allocation
135 while (offset < len) {
136 fl::u16 chunk_size = min(len - offset, static_cast<fl::u16>(sizeof(buffer) - 1));
137 fl::memcpy(buffer, msg + offset, chunk_size);
138 buffer[chunk_size] = '\0'; // Null-terminate
139
140 offset += chunk_size;
141
142 if (offset >= len) {
143 // Last chunk or whole message - use println (adds newline)
144 fl::println(buffer);
145 } else {
146 // Not last chunk - use print (no newline)
147 fl::print(buffer);
148 }
149 }
150
151 mQueue.commit();
152 flushed++;
153 }
154
155 return flushed;
156}
157
158bool AsyncLogger::enableBackgroundFlush(fl::u32 interval_ms, fl::size messages_per_tick) FL_NO_EXCEPT {
160
161 // If already enabled, disable first
162 if (state.mEnabled) {
164 }
165
166 // Configure flush parameters
167 state.mMessagesPerTick = messages_per_tick;
168
169 // Setup ISR timer configuration
170 fl::isr::config config;
172 config.user_data = &state;
173 config.frequency_hz = 1000 / interval_ms; // Convert ms to Hz
174 config.priority = fl::isr::ISR_PRIORITY_LOW; // Low priority to avoid interfering with LED timing
176
177 // Attach timer ISR (returns 0 on success, negative on error)
178 if (fl::isr::attach_timer_handler(config, &state.mTimerHandle) != 0) {
179 return false; // Platform doesn't support timers or attachment failed
180 }
181
182 state.mEnabled = true;
183 return true;
184}
185
188
189 if (state.mEnabled && state.mTimerHandle.is_valid()) {
190 fl::isr::detach_handler(state.mTimerHandle);
191 state.mTimerHandle = fl::isr::handle(); // Reset to default (invalid) handle
192 state.mEnabled = false;
193 state.mNeedsFlush = false;
194 }
195}
196
200
201// ============================================================================
202// Background flush service function (call from main loop)
203// ============================================================================
204
206 // Get background flush state
208
209 // Quick check - return immediately if no flush needed
210 if (!state.mNeedsFlush) {
211 return;
212 }
213
214 // Clear flag
215 state.mNeedsFlush = false;
216
217 // Flush all instantiated async loggers (uses ActiveLoggerRegistry)
218 // Only flushes loggers that have been accessed via template functions
219 fl::size n = state.mMessagesPerTick;
221 logger.flushN(n);
222 });
223}
224
225// ============================================================================
226// Auto-instantiating service task implementation
227// ============================================================================
228
229namespace detail {
230
235
237 : mIntervalMs(16)
239 , mTask()
240{
241 // Create and register task with scheduler
242 // Default 16ms interval = 60 Hz (one frame at 60fps)
244 .then([this]() {
245 // Service all registered async loggers
246 this->serviceLoggers();
247 });
248
250}
251
253 mIntervalMs = interval_ms;
254
255 // Dynamically update task interval if task exists
256 if (mTask.is_valid()) {
257 mTask.set_interval_ms(interval_ms);
258 }
259}
260
262 mMessagesPerTick = messages_per_tick;
263}
264
266 // Flush N messages from all registered loggers
268 logger.flushN(mMessagesPerTick);
269 });
270}
271
272} // namespace detail
273
274// ============================================================================
275// Public configuration API
276// ============================================================================
277
278void configureAsyncLogService(u32 interval_ms, fl::size messages_per_tick) FL_NO_EXCEPT {
281}
282
283} // namespace fl
TestState state
ISR-safe async logger using SPSC queue backend (zero heap allocation)
bool isBackgroundFlushEnabled() const FL_NO_EXCEPT
Check if background flushing is enabled.
bool enableBackgroundFlush(fl::u32 interval_ms, fl::size messages_per_tick=5) FL_NO_EXCEPT
Enable background timer-based flushing (opt-in)
AsyncLogger() FL_NO_EXCEPT
fl::size flushN(fl::size maxMessages) FL_NO_EXCEPT
Flush up to N messages from queue (bounded flush)
fl::u32 droppedCount() const FL_NO_EXCEPT
AsyncLogQueue< 128, 4096 > mQueue
bool empty() const FL_NO_EXCEPT
void push(const fl::string &msg) FL_NO_EXCEPT
void disableBackgroundFlush() FL_NO_EXCEPT
Disable background flushing.
fl::size size() const FL_NO_EXCEPT
void clear() FL_NO_EXCEPT
void flush() FL_NO_EXCEPT
ISR-safe async logger wrapper (zero heap allocation) Uses embedded AsyncLogQueue instead of heap-allo...
static T & instance() FL_NO_EXCEPT
Definition singleton.h:41
void setMessagesPerTick(fl::size messages_per_tick) FL_NO_EXCEPT
Configure number of messages to flush per service call.
void setInterval(u32 interval_ms) FL_NO_EXCEPT
Change the service interval (default 16ms)
void serviceLoggers() FL_NO_EXCEPT
Service all registered loggers (called by task)
static AsyncLoggerServiceTask & instance() FL_NO_EXCEPT
Handle & then(function< void()> on_then) FL_NO_EXCEPT
Sets the callback invoked when the task runs successfully.
Definition task.cpp.hpp:276
static Scheduler & instance() FL_NO_EXCEPT
int add_task(Handle t) FL_NO_EXCEPT
#define FL_UNUSED(x)
#define FL_IRAM
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
ISR handler types and API declarations.
#define FL_ERROR(...)
Definition log.h:373
Centralized logging categories for FastLED hardware interfaces and subsystems.
void printLoggerDisabledError(const char *category_name, const char *define_name) FL_NO_EXCEPT
Print error message for disabled logger (non-template helper) Called from checkLoggerEnabled template...
void FL_IRAM async_log_flush_timer_isr(void *user_data) FL_NO_EXCEPT
Compile-time linker keep-alive hook for a single fl::Bus.
Definition bus_info.h:57
constexpr u32 ISR_FLAG_IRAM_SAFE
Definition constants.h:21
constexpr u8 ISR_PRIORITY_LOW
Definition constants.h:13
int attach_timer_handler(const config &cfg, handle *out_handle) FL_NO_EXCEPT
Attach a timer-based ISR handler.
int detach_handler(handle &h) FL_NO_EXCEPT
Detach an ISR handler.
Handle every_ms(int interval_ms) FL_NO_EXCEPT
Creates a task that runs at a fixed millisecond interval.
Definition task.cpp.hpp:320
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
void print(const char *str) FL_NO_EXCEPT
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NO_EXCEPT
Definition math.h:71
FASTLED_FORCE_INLINE void * memcpy(void *dest, const void *src, size_t n) FL_NO_EXCEPT
Definition cstring.h:136
void configureAsyncLogService(u32 interval_ms, fl::size messages_per_tick) FL_NO_EXCEPT
Configure async logger automatic servicing task.
void async_log_service() FL_NO_EXCEPT
void println(const char *str) FL_NO_EXCEPT
Base definition for an LED controller.
Definition crgb.hpp:179
Task scheduler — manages timer and frame-based tasks.
static ActiveLoggerRegistry & instance() FL_NO_EXCEPT
void forEach(Func func) FL_NO_EXCEPT
handler_fn handler
Definition handler.h:20
void * user_data
Definition handler.h:21
Configuration for ISR attachment.
Definition handler.h:19
Opaque handle to an attached ISR.
Definition handler.h:36