#include #include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "logger.h" #include "output_buffer.h" #define LOG_BUFFER_SIZE 6096 // tamanho total do buffer circular #define MAX_LOG_SIZE 256 // ✅ reduzir stack/CPU; era 512 static SemaphoreHandle_t mutex = NULL; static output_buffer_t *buffer = NULL; EventGroupHandle_t logger_event_group = NULL; // opcional: contador de mensagens dropadas quando mutex está ocupado static volatile uint32_t s_dropped = 0; void logger_init(void) { // Permitir múltiplas chamadas seguras if (mutex != NULL) { return; } mutex = xSemaphoreCreateMutex(); configASSERT(mutex != NULL); logger_event_group = xEventGroupCreate(); configASSERT(logger_event_group != NULL); buffer = output_buffer_create(LOG_BUFFER_SIZE); configASSERT(buffer != NULL); } uint16_t logger_count(void) { if (!mutex || !buffer) { return 0; } // ✅ não bloquear para sempre (mas aqui pode bloquear sem stress) xSemaphoreTake(mutex, portMAX_DELAY); uint16_t c = buffer->count; xSemaphoreGive(mutex); return c; } uint32_t logger_dropped_count(void) { return s_dropped; } void logger_print(const char *str) { if (!str || !mutex || !buffer) { return; } // Limitar comprimento para evitar entradas enormes size_t len = strlen(str); if (len > (MAX_LOG_SIZE - 1)) { len = MAX_LOG_SIZE - 1; } // ✅ NÃO bloquear aqui: se o mutex estiver ocupado, dropa if (xSemaphoreTake(mutex, 0) != pdTRUE) { s_dropped++; return; } output_buffer_append_buf(buffer, str, (uint16_t)len); xEventGroupSetBits(logger_event_group, LOGGER_SERIAL_BIT); xSemaphoreGive(mutex); } int logger_vprintf(const char *fmt, va_list args) { char log_buf[MAX_LOG_SIZE]; #ifdef CONFIG_ESP_CONSOLE_UART // Duplicar va_list para ecoar na UART sem consumir o original va_list args_copy; va_copy(args_copy, args); vprintf(fmt, args_copy); va_end(args_copy); #endif // Se ainda não inicializado, apenas formatar para devolver comprimento if (!mutex || !buffer) { int len = vsnprintf(log_buf, MAX_LOG_SIZE, fmt, args); if (len < 0) { len = 0; } else if (len >= MAX_LOG_SIZE) { len = MAX_LOG_SIZE - 1; } return len; } int len = vsnprintf(log_buf, MAX_LOG_SIZE, fmt, args); if (len < 0) { len = 0; } else if (len >= MAX_LOG_SIZE) { len = MAX_LOG_SIZE - 1; } // ✅ NÃO bloquear o sistema (sys_evt/httpd/wifi/etc) por causa de log if (xSemaphoreTake(mutex, 0) != pdTRUE) { s_dropped++; return len; } output_buffer_append_buf(buffer, log_buf, (uint16_t)len); xEventGroupSetBits(logger_event_group, LOGGER_SERIAL_BIT); xSemaphoreGive(mutex); return len; } /** * ⚠️ API antiga: devolve ponteiro interno do buffer. * Só é segura se o caller COPIAR imediatamente e garantir que não há novas escritas. * Recomendo usar logger_read_copy(). */ bool logger_read(uint16_t *index, char **str, uint16_t *len) { if (!mutex || !buffer || !index || !str || !len) { return false; } xSemaphoreTake(mutex, portMAX_DELAY); bool has_next = output_buffer_read(buffer, index, str, len); xSemaphoreGive(mutex); return has_next; } // ✅ API segura: copia a linha para buffer do caller (evita ponteiro ficar inválido após rotação) bool logger_read_copy(uint16_t *index, char *out, uint16_t out_sz, uint16_t *out_len) { if (!mutex || !buffer || !index || !out || out_sz == 0) { return false; } xSemaphoreTake(mutex, portMAX_DELAY); char *ptr = NULL; uint16_t len = 0; bool ok = output_buffer_read(buffer, index, &ptr, &len); if (!ok) { xSemaphoreGive(mutex); return false; } uint16_t n = (len < (out_sz - 1)) ? len : (out_sz - 1); memcpy(out, ptr, n); out[n] = '\0'; if (out_len) { *out_len = n; } xSemaphoreGive(mutex); return true; }