diff --git a/16m.csv b/16m.csv index 29284b8..f7ddaff 100644 --- a/16m.csv +++ b/16m.csv @@ -3,7 +3,9 @@ nvs, data, nvs, 0x9000, 0x4000, otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, -gifpack, data, littlefs, , 0x800000, +gifpack, data, littlefs, , 0x780000, +logfs, data, littlefs, , 0x80000, ota_0, app, ota_0, , 0x330000, ota_1, app, ota_1, , 0x330000, model, data, spiffs, , 0x180000, +coredump, data, coredump,, 0x10000, diff --git a/components/control_plane/CMakeLists.txt b/components/control_plane/CMakeLists.txt index 315b3fb..5ab7e78 100644 --- a/components/control_plane/CMakeLists.txt +++ b/components/control_plane/CMakeLists.txt @@ -22,6 +22,8 @@ idf_component_register( "internal" PRIV_REQUIRES domain + espcoredump + esp_app_format esp_driver_gpio esp_timer esp_system diff --git a/components/control_plane/internal/control_plane_internal.h b/components/control_plane/internal/control_plane_internal.h index 8eb7628..405d6b0 100644 --- a/components/control_plane/internal/control_plane_internal.h +++ b/components/control_plane/internal/control_plane_internal.h @@ -145,3 +145,6 @@ void control_plane_set_ptt_active(bool active); esp_err_t control_plane_remote_log_start(void); void control_plane_remote_log_stop(int timeout_ms); +esp_err_t control_plane_remote_log_flush_now(void); +esp_err_t control_plane_remote_log_clear(void); +esp_err_t control_plane_remote_log_format_status(char *out, size_t out_len); diff --git a/components/control_plane/src/runtime/control_plane_factory_cli.c b/components/control_plane/src/runtime/control_plane_factory_cli.c index 35d65ea..6370729 100644 --- a/components/control_plane/src/runtime/control_plane_factory_cli.c +++ b/components/control_plane/src/runtime/control_plane_factory_cli.c @@ -10,6 +10,7 @@ */ #include "control_plane.h" +#include "control_plane_internal.h" #include #include @@ -357,6 +358,41 @@ static void handle_prepare_rotate(uint32_t key_version) cli_write_ok(); } +static void handle_log_command(size_t argc, char *argv[]) +{ + if (argc < 2 || is_text_empty(argv[1])) { + cli_write_error("missing_log_command"); + return; + } + + if (strcmp(argv[1], "status") == 0) { + char json[FACTORY_CLI_JSON_MAX_LEN] = {0}; + esp_err_t err = control_plane_remote_log_format_status(json, sizeof(json)); + if (err != ESP_OK) { + cli_write_error("log_status_failed"); + return; + } + cli_write_line(json); + cli_write_ok(); + } else if (strcmp(argv[1], "flush") == 0) { + esp_err_t err = control_plane_remote_log_flush_now(); + if (err != ESP_OK) { + cli_write_error("log_flush_failed"); + return; + } + cli_write_ok(); + } else if (strcmp(argv[1], "clear") == 0) { + esp_err_t err = control_plane_remote_log_clear(); + if (err != ESP_OK) { + cli_write_error("log_clear_failed"); + return; + } + cli_write_ok(); + } else { + cli_write_error("unknown_log_command"); + } +} + static void handle_command_line(char *line) { trim_text(line); @@ -429,6 +465,8 @@ static void handle_command_line(char *line) return; } handle_prepare_rotate(key_version); + } else if (strcmp(cmd, "log") == 0) { + handle_log_command(argc, argv); } else { cli_write_error("unknown_command"); } diff --git a/components/control_plane/src/runtime/control_plane_remote_log_service.c b/components/control_plane/src/runtime/control_plane_remote_log_service.c index a7c62a6..eea5e11 100644 --- a/components/control_plane/src/runtime/control_plane_remote_log_service.c +++ b/components/control_plane/src/runtime/control_plane_remote_log_service.c @@ -16,7 +16,11 @@ #include #include "domain.h" +#include "esp_app_desc.h" +#include "esp_core_dump.h" +#include "esp_heap_caps.h" #include "esp_log.h" +#include "esp_system.h" #include "esp_timer.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -76,7 +80,7 @@ #define REMOTE_LOG_TASK_STACK_SIZE (1024 * 8) #define REMOTE_LOG_TASK_PRIORITY 3 -#define REMOTE_LOG_DIR "/littlefs/remote_logs" +#define REMOTE_LOG_DIR "/logfs/remote_logs" #define REMOTE_LOG_FILE_PREFIX "log_" #define REMOTE_LOG_FILE_SUFFIX ".jsonl" #define REMOTE_LOG_PATH_MAX 96 @@ -104,6 +108,16 @@ static volatile bool s_stop_requested; static portMUX_TYPE s_lock = portMUX_INITIALIZER_UNLOCKED; static vprintf_like_t s_original_vprintf; static bool s_hook_installed; +static uint32_t s_successful_uploads; +static uint32_t s_failed_uploads; +static int s_last_http_status = -1; +static esp_err_t s_last_upload_err = ESP_ERR_NOT_FOUND; +static int64_t s_last_upload_ms; +static int64_t s_last_success_ms; +static bool s_coredump_present; +static bool s_coredump_reported; +static size_t s_coredump_addr; +static size_t s_coredump_size; static bool remote_log_path_active(void) { @@ -230,6 +244,15 @@ static bool dequeue_entry(remote_log_entry_t *out_entry) return ok; } +static size_t snapshot_queue_count(void) +{ + size_t count = 0U; + portENTER_CRITICAL(&s_lock); + count = s_queue_count; + portEXIT_CRITICAL(&s_lock); + return count; +} + static uint32_t snapshot_and_clear_dropped_count(void) { uint32_t dropped = 0; @@ -451,6 +474,12 @@ static size_t collect_files(uint32_t *out_sequences, size_t capacity) return count; } +static size_t count_pending_files(void) +{ + uint32_t sequences[CONFIG_TQ_REMOTE_LOG_MAX_FILES + 8] = {0}; + return collect_files(sequences, sizeof(sequences) / sizeof(sequences[0])); +} + static void trim_old_files(void) { uint32_t sequences[CONFIG_TQ_REMOTE_LOG_MAX_FILES + 8] = {0}; @@ -541,6 +570,102 @@ static bool realtime_path_busy(void) #endif } +static const char *reset_reason_name(esp_reset_reason_t reason) +{ + switch (reason) { + case ESP_RST_POWERON: + return "poweron"; + case ESP_RST_EXT: + return "external"; + case ESP_RST_SW: + return "software"; + case ESP_RST_PANIC: + return "panic"; + case ESP_RST_INT_WDT: + return "interrupt_wdt"; + case ESP_RST_TASK_WDT: + return "task_wdt"; + case ESP_RST_WDT: + return "other_wdt"; + case ESP_RST_DEEPSLEEP: + return "deepsleep"; + case ESP_RST_BROWNOUT: + return "brownout"; + case ESP_RST_SDIO: + return "sdio"; + default: + return "unknown"; + } +} + +static esp_err_t append_device_state(char *body, size_t body_len, size_t *offset) +{ + if (body == NULL || offset == NULL || *offset >= body_len) { + return ESP_ERR_INVALID_ARG; + } + + const esp_app_desc_t *app = esp_app_get_description(); + esp_reset_reason_t reset_reason = esp_reset_reason(); + control_plane_multimodal_runtime_state_t mm = {0}; + bool have_mm = (control_plane_multimodal_get_runtime_state(&mm) == ESP_OK); + + int written = snprintf(body + *offset, + body_len - *offset, + ",\"device_state\":{" + "\"reset_reason\":\"%s\"," + "\"reset_reason_code\":%d," + "\"firmware_version\":\"", + reset_reason_name(reset_reason), + (int)reset_reason); + if (written < 0 || (size_t)written >= body_len - *offset) { + return ESP_ERR_INVALID_SIZE; + } + *offset += (size_t)written; + json_append_escaped(body, body_len, offset, app != NULL ? app->version : ""); + written = snprintf(body + *offset, + body_len - *offset, + "\",\"project_name\":\""); + if (written < 0 || (size_t)written >= body_len - *offset) { + return ESP_ERR_INVALID_SIZE; + } + *offset += (size_t)written; + json_append_escaped(body, body_len, offset, app != NULL ? app->project_name : ""); + written = snprintf(body + *offset, + body_len - *offset, + "\",\"free_heap\":%lu," + "\"minimum_free_heap\":%lu," + "\"free_internal\":%lu," + "\"largest_internal\":%lu," + "\"free_spiram\":%lu," + "\"largest_spiram\":%lu," + "\"task_count\":%lu," + "\"multimodal_connected\":%s," + "\"multimodal_active_task\":%s," + "\"multimodal_client_speech_active\":%s," + "\"multimodal_remote_responding\":%s," + "\"coredump_present\":%s," + "\"coredump_size\":%lu" + "}", + (unsigned long)esp_get_free_heap_size(), + (unsigned long)esp_get_minimum_free_heap_size(), + (unsigned long)heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), + (unsigned long)heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), + (unsigned long)heap_caps_get_free_size(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT), + (unsigned long)heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT), + (unsigned long)uxTaskGetNumberOfTasks(), + (have_mm && mm.connected) ? "true" : "false", + (have_mm && mm.has_active_task) ? "true" : "false", + (have_mm && mm.client_speech_active) ? "true" : "false", + (have_mm && mm.remote_responding) ? "true" : "false", + s_coredump_present ? "true" : "false", + (unsigned long)s_coredump_size); + if (written < 0 || (size_t)written >= body_len - *offset) { + return ESP_ERR_INVALID_SIZE; + } + *offset += (size_t)written; + return ESP_OK; +} + static esp_err_t build_upload_body(uint32_t sequence, uint32_t dropped_count, char *body, @@ -616,7 +741,16 @@ static esp_err_t build_upload_body(uint32_t sequence, } fclose(file); - written = snprintf(body + offset, body_len - offset, "]}"); + written = snprintf(body + offset, body_len - offset, "]"); + if (written < 0 || (size_t)written >= body_len - offset) { + return ESP_ERR_INVALID_SIZE; + } + offset += (size_t)written; + esp_err_t state_err = append_device_state(body, body_len, &offset); + if (state_err != ESP_OK) { + return state_err; + } + written = snprintf(body + offset, body_len - offset, "}"); if (written < 0 || (size_t)written >= body_len - offset) { return ESP_ERR_INVALID_SIZE; } @@ -669,11 +803,16 @@ static bool upload_one_file(uint32_t sequence) free(body); free(response); + s_last_upload_ms = esp_timer_get_time() / 1000; + s_last_upload_err = err; + s_last_http_status = status; if (err == ESP_OK && (status == 200 || status == 202 || status == 204)) { char path[REMOTE_LOG_PATH_MAX] = {0}; make_log_path(sequence, path, sizeof(path)); (void)unlink(path); + s_successful_uploads++; + s_last_success_ms = s_last_upload_ms; return true; } @@ -682,10 +821,12 @@ static bool upload_one_file(uint32_t sequence) make_log_path(sequence, path, sizeof(path)); (void)unlink(path); restore_dropped_count(dropped); + s_failed_uploads++; return true; } restore_dropped_count(dropped); + s_failed_uploads++; return false; } @@ -703,6 +844,144 @@ static bool upload_pending_files(void) return upload_one_file(sequences[0]); } +static void detect_coredump(void) +{ + size_t addr = 0U; + size_t size = 0U; + esp_err_t err = esp_core_dump_image_get(&addr, &size); + if (err == ESP_OK && size > 0U) { + s_coredump_present = true; + s_coredump_addr = addr; + s_coredump_size = size; + } else { + s_coredump_present = false; + s_coredump_addr = 0U; + s_coredump_size = 0U; + } +} + +static esp_err_t build_coredump_report_body(char *body, size_t body_len) +{ + if (body == NULL || body_len == 0U) { + return ESP_ERR_INVALID_ARG; + } + + char device_id[64] = {0}; + char serial_number[64] = {0}; + domain_runtime_read_device_identity(device_id, sizeof(device_id), serial_number, sizeof(serial_number)); + + char panic_reason[192] = {0}; +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF + (void)esp_core_dump_get_panic_reason(panic_reason, sizeof(panic_reason)); +#endif + + size_t offset = 0U; + int written = snprintf(body, + body_len, + "{\"schema\":\"tq.coredump.report.v1\",\"device_id\":\""); + if (written < 0 || (size_t)written >= body_len) { + return ESP_ERR_INVALID_SIZE; + } + offset = (size_t)written; + json_append_escaped(body, body_len, &offset, device_id); + written = snprintf(body + offset, body_len - offset, "\",\"serial_number\":\""); + if (written < 0 || (size_t)written >= body_len - offset) { + return ESP_ERR_INVALID_SIZE; + } + offset += (size_t)written; + json_append_escaped(body, body_len, &offset, serial_number); + written = snprintf(body + offset, + body_len - offset, + "\",\"sent_at_ms\":%lld,\"coredump_addr\":%lu,\"coredump_size\":%lu,\"panic_reason\":\"", + (long long)(esp_timer_get_time() / 1000), + (unsigned long)s_coredump_addr, + (unsigned long)s_coredump_size); + if (written < 0 || (size_t)written >= body_len - offset) { + return ESP_ERR_INVALID_SIZE; + } + offset += (size_t)written; + json_append_escaped(body, body_len, &offset, panic_reason); + written = snprintf(body + offset, body_len - offset, "\""); + if (written < 0 || (size_t)written >= body_len - offset) { + return ESP_ERR_INVALID_SIZE; + } + offset += (size_t)written; + esp_err_t state_err = append_device_state(body, body_len, &offset); + if (state_err != ESP_OK) { + return state_err; + } + written = snprintf(body + offset, body_len - offset, "}"); + if (written < 0 || (size_t)written >= body_len - offset) { + return ESP_ERR_INVALID_SIZE; + } + return ESP_OK; +} + +static bool upload_coredump_report(void) +{ + if (!s_coredump_present || s_coredump_reported || + !control_plane_is_sta_connected() || realtime_path_busy()) { + return false; + } + + char *body = (char *)malloc(2048); + char *response = (char *)malloc(REMOTE_LOG_RESPONSE_MAX); + if (body == NULL || response == NULL) { + free(body); + free(response); + return false; + } + + esp_err_t err = build_coredump_report_body(body, 2048); + if (err != ESP_OK) { + free(body); + free(response); + return false; + } + + int status = -1; + size_t response_len = 0U; + err = domain_runtime_post_json(CONFIG_TQ_REMOTE_LOG_ENDPOINT, + body, + NULL, + CONFIG_TQ_REMOTE_LOG_AUTH_TOKEN, + CONFIG_TQ_REMOTE_LOG_UPLOAD_TIMEOUT_MS, + 0, + 0, + response, + REMOTE_LOG_RESPONSE_MAX, + &response_len, + &status); + free(body); + free(response); + + s_last_upload_ms = esp_timer_get_time() / 1000; + s_last_upload_err = err; + s_last_http_status = status; + if (err == ESP_OK && (status == 200 || status == 202 || status == 204)) { + s_successful_uploads++; + s_last_success_ms = s_last_upload_ms; + s_coredump_reported = true; + return true; + } + s_failed_uploads++; + return false; +} + +static void log_startup_self_check(void) +{ + ESP_LOGW("remote_log", + "enabled endpoint=%s dir=%s pending_files=%u queue=%u max_files=%u file_max=%u coredump_present=%d coredump_size=%u", + CONFIG_TQ_REMOTE_LOG_ENDPOINT, + REMOTE_LOG_DIR, + (unsigned)count_pending_files(), + (unsigned)CONFIG_TQ_REMOTE_LOG_QUEUE_LEN, + (unsigned)CONFIG_TQ_REMOTE_LOG_MAX_FILES, + (unsigned)CONFIG_TQ_REMOTE_LOG_FILE_MAX_BYTES, + s_coredump_present, + (unsigned)s_coredump_size); +} + static void install_hook(void) { if (s_hook_installed) { @@ -726,7 +1005,7 @@ static void remote_log_task(void *arg) { (void)arg; - esp_err_t mount_err = domain_runtime_mount_littlefs(); + esp_err_t mount_err = domain_runtime_mount_logfs(); if (mount_err != ESP_OK) { s_remote_log_task = NULL; vTaskDelete(NULL); @@ -734,8 +1013,10 @@ static void remote_log_task(void *arg) } (void)make_log_dir(); s_file_sequence = find_next_file_sequence(); + detect_coredump(); install_hook(); + log_startup_self_check(); TickType_t last_flush = xTaskGetTickCount(); uint32_t backoff_ms = 0U; @@ -750,6 +1031,7 @@ static void remote_log_task(void *arg) if (backoff_ms == 0U || now >= next_upload_tick) { drain_memory_queue_to_flash(); + (void)upload_coredump_report(); bool ok = upload_pending_files(); if (ok) { backoff_ms = 0U; @@ -792,6 +1074,81 @@ esp_err_t control_plane_remote_log_start(void) return created == pdPASS ? ESP_OK : ESP_ERR_NO_MEM; } +esp_err_t control_plane_remote_log_flush_now(void) +{ + if (s_remote_log_task == NULL) { + return ESP_ERR_INVALID_STATE; + } + drain_memory_queue_to_flash(); + if (!control_plane_is_sta_connected()) { + return ESP_ERR_INVALID_STATE; + } + (void)upload_coredump_report(); + return upload_pending_files() ? ESP_OK : ESP_FAIL; +} + +esp_err_t control_plane_remote_log_clear(void) +{ + portENTER_CRITICAL(&s_lock); + s_queue_read = 0U; + s_queue_count = 0U; + s_dropped_count = 0U; + portEXIT_CRITICAL(&s_lock); + + uint32_t sequences[CONFIG_TQ_REMOTE_LOG_MAX_FILES + 8] = {0}; + size_t count = collect_files(sequences, sizeof(sequences) / sizeof(sequences[0])); + for (size_t i = 0; i < count; ++i) { + char path[REMOTE_LOG_PATH_MAX] = {0}; + make_log_path(sequences[i], path, sizeof(path)); + (void)unlink(path); + } + return ESP_OK; +} + +esp_err_t control_plane_remote_log_format_status(char *out, size_t out_len) +{ + if (out == NULL || out_len == 0U) { + return ESP_ERR_INVALID_ARG; + } + + uint32_t dropped = 0U; + portENTER_CRITICAL(&s_lock); + dropped = s_dropped_count; + portEXIT_CRITICAL(&s_lock); + + int written = snprintf(out, + out_len, + "{\"enabled\":true,\"running\":%s,\"hook_installed\":%s," + "\"endpoint\":\"%s\",\"dir\":\"%s\",\"queue_count\":%u," + "\"queue_capacity\":%u,\"pending_files\":%u," + "\"max_files\":%u,\"file_max_bytes\":%u," + "\"dropped_count\":%lu,\"successful_uploads\":%lu," + "\"failed_uploads\":%lu,\"last_http_status\":%d," + "\"last_upload_err\":%d,\"last_upload_ms\":%lld," + "\"last_success_ms\":%lld,\"coredump_present\":%s," + "\"coredump_reported\":%s,\"coredump_size\":%lu}", + s_remote_log_task != NULL ? "true" : "false", + s_hook_installed ? "true" : "false", + CONFIG_TQ_REMOTE_LOG_ENDPOINT, + REMOTE_LOG_DIR, + (unsigned)snapshot_queue_count(), + (unsigned)CONFIG_TQ_REMOTE_LOG_QUEUE_LEN, + (unsigned)count_pending_files(), + (unsigned)CONFIG_TQ_REMOTE_LOG_MAX_FILES, + (unsigned)CONFIG_TQ_REMOTE_LOG_FILE_MAX_BYTES, + (unsigned long)dropped, + (unsigned long)s_successful_uploads, + (unsigned long)s_failed_uploads, + s_last_http_status, + (int)s_last_upload_err, + (long long)s_last_upload_ms, + (long long)s_last_success_ms, + s_coredump_present ? "true" : "false", + s_coredump_reported ? "true" : "false", + (unsigned long)s_coredump_size); + return (written > 0 && (size_t)written < out_len) ? ESP_OK : ESP_ERR_INVALID_SIZE; +} + void control_plane_remote_log_stop(int timeout_ms) { if (s_remote_log_task == NULL) { @@ -821,4 +1178,23 @@ void control_plane_remote_log_stop(int timeout_ms) { (void)timeout_ms; } + +esp_err_t control_plane_remote_log_flush_now(void) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +esp_err_t control_plane_remote_log_clear(void) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +esp_err_t control_plane_remote_log_format_status(char *out, size_t out_len) +{ + if (out == NULL || out_len == 0U) { + return ESP_ERR_INVALID_ARG; + } + int written = snprintf(out, out_len, "{\"enabled\":false}"); + return (written > 0 && (size_t)written < out_len) ? ESP_OK : ESP_ERR_INVALID_SIZE; +} #endif diff --git a/components/domain/include/domain.h b/components/domain/include/domain.h index 9a44b89..80d3eaf 100644 --- a/components/domain/include/domain.h +++ b/components/domain/include/domain.h @@ -188,6 +188,7 @@ bool domain_runtime_slot_feature_switch_is_active(void); esp_err_t domain_runtime_prepare_storage(void); esp_err_t domain_runtime_mount_littlefs(void); +esp_err_t domain_runtime_mount_logfs(void); void domain_runtime_read_device_identity(char *device_id, size_t device_id_len, char *serial_number, diff --git a/components/domain/src/runtime/domain_runtime_adapters.c b/components/domain/src/runtime/domain_runtime_adapters.c index 944acec..c1a9923 100644 --- a/components/domain/src/runtime/domain_runtime_adapters.c +++ b/components/domain/src/runtime/domain_runtime_adapters.c @@ -758,6 +758,11 @@ esp_err_t domain_runtime_mount_littlefs(void) return platform_mount_littlefs(); } +esp_err_t domain_runtime_mount_logfs(void) +{ + return platform_mount_logfs(); +} + void domain_runtime_read_device_identity(char *device_id, size_t device_id_len, char *serial_number, diff --git a/components/platform/include/platform.h b/components/platform/include/platform.h index 6449ff6..585925a 100644 --- a/components/platform/include/platform.h +++ b/components/platform/include/platform.h @@ -133,6 +133,7 @@ typedef struct { esp_err_t platform_init(void); esp_err_t platform_prepare_storage(void); esp_err_t platform_mount_littlefs(void); +esp_err_t platform_mount_logfs(void); void platform_read_device_identity(char *device_id, size_t device_id_len, char *serial_number, diff --git a/components/platform/src/platform_behavior_stubs.c b/components/platform/src/platform_behavior_stubs.c index 4b7c7a3..40a990d 100644 --- a/components/platform/src/platform_behavior_stubs.c +++ b/components/platform/src/platform_behavior_stubs.c @@ -33,6 +33,8 @@ #define VOICE_PROMPT_DEFAULT_BASE_PATH "/littlefs/audio_prompts" #define VOICE_PROMPT_LITTLEFS_BASE_PATH "/littlefs" #define VOICE_PROMPT_LITTLEFS_PARTITION "gifpack" +#define PLATFORM_LOGFS_BASE_PATH "/logfs" +#define PLATFORM_LOGFS_PARTITION "logfs" #define VOICE_PROMPT_FRAME_HEADER_BYTES 4U #define VOICE_PROMPT_MAX_OPUS_PACKET_BYTES 2048U #define VOICE_PROMPT_OPUS_FRAME_MS 60U @@ -43,6 +45,8 @@ static const char *VOICE_TAG = "voice_prompt"; static bool s_power_hold_configured; static bool s_voice_prompt_littlefs_mount_attempted; static esp_err_t s_voice_prompt_littlefs_mount_result = ESP_ERR_INVALID_STATE; +static bool s_logfs_mount_attempted; +static esp_err_t s_logfs_mount_result = ESP_ERR_INVALID_STATE; static uint64_t power_manager_gpio_bit_mask(int gpio_num) { @@ -154,6 +158,49 @@ esp_err_t platform_mount_littlefs(void) return err; } +esp_err_t platform_mount_logfs(void) +{ + if (s_logfs_mount_attempted) { + return s_logfs_mount_result; + } + + esp_vfs_littlefs_conf_t conf = { + .base_path = PLATFORM_LOGFS_BASE_PATH, + .partition_label = PLATFORM_LOGFS_PARTITION, + .partition = NULL, + .format_if_mount_failed = true, + .dont_mount = false, + }; + + esp_err_t err = esp_vfs_littlefs_register(&conf); + if (err == ESP_ERR_INVALID_STATE && + esp_littlefs_mounted(PLATFORM_LOGFS_PARTITION)) { + err = ESP_OK; + } + if (err == ESP_OK) { + size_t total_bytes = 0; + size_t used_bytes = 0; + esp_err_t info_err = esp_littlefs_info(PLATFORM_LOGFS_PARTITION, + &total_bytes, + &used_bytes); + if (info_err == ESP_OK) { + ESP_LOGI(VOICE_TAG, + "LogFS mounted at %s, used=%u total=%u", + PLATFORM_LOGFS_BASE_PATH, + (unsigned)used_bytes, + (unsigned)total_bytes); + } + } else { + ESP_LOGW(VOICE_TAG, + "Mount LogFS failed: %s", + esp_err_to_name(err)); + } + + s_logfs_mount_attempted = true; + s_logfs_mount_result = err; + return err; +} + esp_err_t voice_prompt_play_file(const char *path) { if (path == NULL || path[0] == '\0') { diff --git a/sdkconfig.defaults b/sdkconfig.defaults index d15ab16..3216907 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -34,8 +34,20 @@ CONFIG_TQ_REMOTE_LOG_ENTRY_MAX_BYTES=256 CONFIG_TQ_REMOTE_LOG_BATCH_MAX_ENTRIES=30 CONFIG_TQ_REMOTE_LOG_FLUSH_INTERVAL_MS=5000 CONFIG_TQ_REMOTE_LOG_UPLOAD_TIMEOUT_MS=8000 -CONFIG_TQ_REMOTE_LOG_MAX_FILES=32 +CONFIG_TQ_REMOTE_LOG_MAX_FILES=24 CONFIG_TQ_REMOTE_LOG_FILE_MAX_BYTES=16384 +CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=y +# CONFIG_ESP_COREDUMP_ENABLE_TO_UART is not set +# CONFIG_ESP_COREDUMP_ENABLE_TO_NONE is not set +CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF=y +# CONFIG_ESP_COREDUMP_DATA_FORMAT_BIN is not set +CONFIG_ESP_COREDUMP_CHECKSUM_CRC32=y +# CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 is not set +# CONFIG_ESP_COREDUMP_CAPTURE_DRAM is not set +CONFIG_ESP_COREDUMP_CHECK_BOOT=y +# CONFIG_ESP_COREDUMP_LOGS is not set +CONFIG_ESP_COREDUMP_MAX_TASKS_NUM=16 +CONFIG_ESP_COREDUMP_STACK_SIZE=1792 CONFIG_TQ_KEY_INPUT_ENABLE=y CONFIG_TQ_KEY_INPUT_TASK_STACK=4096 CONFIG_TQ_KEY_IRQ_QUEUE_LEN=16