diff --git a/firmware/lib/event_log/event_log.cpp b/firmware/lib/event_log/event_log.cpp index 95fd630..12beed3 100644 --- a/firmware/lib/event_log/event_log.cpp +++ b/firmware/lib/event_log/event_log.cpp @@ -102,7 +102,11 @@ void event_log_init() { void event_log_write(EventLogTag tag, uint16_t data0, uint16_t data1) { #ifdef ARDUINO if (!s_ok) return; - if (s_mutex) xSemaphoreTake(s_mutex, portMAX_DELAY); + // Bounded wait: skip on contention rather than stall the calling task. + // This matters because event_log_write runs from the WiFi event task + // (priority 23); blocking it on a 10-100ms NVS write can overflow the + // event queue. Diagnostic loss is preferable to dropped WiFi events. + if (s_mutex && xSemaphoreTake(s_mutex, pdMS_TO_TICKS(50)) != pdTRUE) return; EventLogEntry e = {}; time_t now = time(nullptr); e.ts_unix = (now > NTP_SYNC_THRESHOLD) ? (uint32_t)now : 0; @@ -132,7 +136,9 @@ void event_log_write(EventLogTag tag, uint16_t data0, uint16_t data1) { size_t event_log_read_recent(EventLogEntry* out, size_t max_entries) { #ifdef ARDUINO if (!s_ok) return 0; - if (s_mutex) xSemaphoreTake(s_mutex, portMAX_DELAY); + // Bounded wait to match event_log_write. Reads are slower (32 NVS gets), + // but returning 0 entries under contention beats blocking the caller. + if (s_mutex && xSemaphoreTake(s_mutex, pdMS_TO_TICKS(50)) != pdTRUE) return 0; #endif uint32_t head = g_head; uint32_t cnt = g_cnt; diff --git a/firmware/lib/event_log/event_log.h b/firmware/lib/event_log/event_log.h index 0782743..470f45a 100644 --- a/firmware/lib/event_log/event_log.h +++ b/firmware/lib/event_log/event_log.h @@ -35,6 +35,12 @@ static_assert(sizeof(EventLogEntry) == 32, "EventLogEntry must be 32 bytes"); // NVS-backed 32-slot ring buffer. Safe to call before NTP sync. // Call exactly once from application setup, before any task writes events. void event_log_init(); + +// Safe to call from any FreeRTOS task after event_log_init(). +// Bounded mutex wait (~50ms) — will silently skip on contention rather than +// block the calling task. Acceptable for diagnostic logging. void event_log_write(EventLogTag tag, uint16_t data0 = 0, uint16_t data1 = 0); + +// Same bounded-wait contract as event_log_write: returns 0 on mutex timeout. size_t event_log_read_recent(EventLogEntry* out, size_t max_entries); uint16_t event_log_path_hash(const char* path); // fnv1a16 — exposed for tests