From 278c4ca116e8f0712995f4d181eafce0d789e6e6 Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Mon, 12 Oct 2026 11:05:18 -0300 Subject: [PATCH] Integrate ESP32 health support --- .github/workflows/nightly.yml | 1 + .github/workflows/quicktest.yml | 1 + mongoose.c | 88 +++++++++++-------- mongoose.h | 76 ++++++++++++++-- src/arch_esp32.h | 6 +- src/health.c | 87 ++++++++++-------- src/health.h | 70 +++++++++++++-- tutorials/esp32/minimal/main/main.c | 4 +- .../esp32/minimal/main/mongoose_config.h | 2 + 9 files changed, 246 insertions(+), 89 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index d10d97a6..1c55628a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -637,6 +637,7 @@ jobs: example: - path: micropython/esp32 - path: esp32/uart-bridge + - path: esp32/minimal name: ${{ matrix.example.path }} env: GO: 0 diff --git a/.github/workflows/quicktest.yml b/.github/workflows/quicktest.yml index c593e86b..304837a9 100644 --- a/.github/workflows/quicktest.yml +++ b/.github/workflows/quicktest.yml @@ -186,6 +186,7 @@ jobs: matrix: example: - path: esp32/uart-bridge + - path: esp32/minimal name: ${{ matrix.example.path }} env: GO: 0 diff --git a/mongoose.c b/mongoose.c index 220cef5a..225f0e79 100644 --- a/mongoose.c +++ b/mongoose.c @@ -3641,12 +3641,13 @@ struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read, +#if MG_ENABLE_HEALTH // The one health record. Lives in RAM that survives a warm reset, see // MG_HEALTH_RAM and the .mg_health region in the linker script struct mg_health mg_health_record MG_HEALTH_RAM; -// STM32 H723 support. Needs the _estack symbol from the linker script -#if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5)) +// STM32 H7, H5, C5 support. Needs the _estack symbol from the linker script +#if defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5) // Fault handler body. Runs in exception context: no printf, no malloc, no // blocking calls. Copies the unwind registers and raw stack into the health // record, then resets. @@ -3688,52 +3689,57 @@ void BusFault_Handler(void) __attribute__((alias("HardFault_Handler"))); void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler"))); #endif -// Start a new boot: validate the record, count the boot, store the reset -// reason. The current boot moves to prev_* slots first, so the previous -// boot's uptime and reason stay readable. Called by MG_HEALTH_INIT() +// Start a new boot: validate the record. Called by MG_HEALTH_INIT() void mg_health_init(void) { char magic[] = MG_HEALTH_MAGIC; - // struct mg_health *h = mg_health_get(); if (!mg_health_valid()) { // First boot, or RAM lost its contents memset(&mg_health_record, 0, sizeof(mg_health_record)); memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic)); } - -#if 0 && MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH - { +#if MG_ARCH == MG_ARCH_ESP32 +#if CONFIG_IDF_TARGET_ARCH_XTENSA + mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_XTENSA; +#elif CONFIG_IDF_TARGET_ARCH_RISCV + mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_RISCV; +#endif + esp_reset_reason_t rr = esp_reset_reason(); + mg_health_record.reset_reason = esp2mg(rr); +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH == 0 + MG_ERROR(("MG_HEALTH is enabled but CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=n")); +#else // After a crash, copy the backtrace from the core dump in flash: the same // addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...". // The core dump stays in flash, so check the reset reason, otherwise an // old crash is reported on every boot - static esp_core_dump_summary_t s; // Big on RISC-V, keep it off the stack - esp_reset_reason_t rr = esp_reset_reason(); + esp_core_dump_summary_t *s = calloc(1, sizeof(*s)); uint32_t *out = mg_health_record.data; - size_t n = 0, max = MG_HEALTH_DATA_SIZE; - if (rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT && rr != ESP_RST_TASK_WDT) - return; // Not a crash that went through the panic handler - if (max == 0 || esp_core_dump_get_summary(&s) != ESP_OK) return; - - mg_health_record.cpuid = 42; // ESP32 - mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT; - + size_t n = 0; memset(out, 0, sizeof(mg_health_record.data)); - out[n++] = s.exc_pc; + if (s == NULL) return; + if ((rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT && + rr != ESP_RST_TASK_WDT) // Not a crash caught by the panic handler + || esp_core_dump_get_summary(s) != ESP_OK) { + free(s); + return; + } + out[n++] = s->exc_pc; + memcpy(&out[n], s->exc_task, 16), n += 4; #if CONFIG_IDF_TARGET_ARCH_XTENSA - for (uint32_t i = 0; i < s.exc_bt_info.depth && n < max; i++) { - if (s.exc_bt_info.bt[i] != s.exc_pc) out[n++] = s.exc_bt_info.bt[i]; - } + out[n++] = s->ex_info.exc_a[0]; + out[n++] = s->ex_info.exc_a[1]; + size_t len = s->exc_bt_info.depth; + if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n); + memcpy(&out[n], s->exc_bt_info.bt, 4 * len); // sizeof(uint32_t) #elif CONFIG_IDF_TARGET_ARCH_RISCV - // No unwinder: take the return address, then words on the stack that - // point into executable memory (a heuristic, may include stale values) - if (n < max) out[n++] = s.ex_info.ra; - for (uint32_t i = 0; i + 4 <= s.exc_bt_info.dump_size && n < max; i += 4) { - uint32_t w; - memcpy(&w, &s.exc_bt_info.stackdump[i], sizeof(w)); // Byte array - if (esp_ptr_executable((void *) (uintptr_t) w)) out[n++] = w; - } -#endif - } + out[n++] = s->ex_info.exc_ra; + out[n++] = s->ex_info.exc_sp; + size_t len = s->exc_bt_info.dump_size / 4 ; // sizeof(uint32_t) + if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n); + memcpy(&out[n], s->exc_bt_info.stackdump, 4 * len); #endif + free(s); +#endif // CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH +#endif // MG_ARCH_ESP32 } static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) { @@ -3747,11 +3753,17 @@ static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) { size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) { size_t len = 0; if (mg_health_valid() && - mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) { + (mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_FAULT +#if MG_ARCH == MG_ARCH_ESP32 + || mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_WATCHDOG +#endif + )) { size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]); - len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", // - MG_ESC("version"), mg_health_record.magic[3], // - MG_ESC("cpuid"), mg_health_record.cpuid, // + len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%hu,%m:%hu,%m:%u,%m:[%M]}", // + MG_ESC("version"), mg_health_record.magic[3], // + MG_ESC("arch"), mg_health_record.arch, // + MG_ESC("reason"), mg_health_record.reset_reason, // + MG_ESC("cpuid"), mg_health_record.cpuid, // MG_ESC("data"), print_uint32, n, mg_health_record.data); } else { len += mg_xprintf(fn, arg, "{}"); @@ -3759,6 +3771,7 @@ size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) { (void) ap; return len; } +#endif #ifdef MG_ENABLE_LINES #line 1 "src/http.c" @@ -34937,6 +34950,7 @@ struct mg_tcpip_driver mg_tcpip_driver_stm32f = { #if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || \ MG_ENABLE_DRIVER_STM32N) +// STM32C: vendor modded single-queue Synopsys v5.4, same settings as STM32H // STM32H: vendor modded single-queue Synopsys v4.2 // STM32N: dual-queue GbE Synopsys v5.2 with no hash table option, 64-bit AXI // MCXNx4x: dual-queue Synopsys v5.2 with no hash table option diff --git a/mongoose.h b/mongoose.h index 1ed986e6..0f7b197b 100644 --- a/mongoose.h +++ b/mongoose.h @@ -253,8 +253,10 @@ extern "C" { #include #include #include -// #include // For health record coredumps -// #include // For health record coredumps +#if MG_ENABLE_HEALTH +#include +#include +#endif #define MG_PATH_MAX 128 @@ -4371,6 +4373,8 @@ void mg_ota_poll(struct mg_mgr *); +#if MG_ENABLE_HEALTH + #ifndef MG_HEALTH_MAGIC #define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version #endif @@ -4379,13 +4383,15 @@ void mg_ota_poll(struct mg_mgr *); #define MG_HEALTH_RAM __attribute__((section(".mg_health"))) #endif -#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5)) -#define MG_HEALTH_DATA_SIZE 250 -#endif - #ifndef MG_HEALTH_DATA_SIZE +#if (defined(STM32H7_DEV_ID) || defined(STM32H5)) || defined(STM32C5) +#define MG_HEALTH_DATA_SIZE 250 +#elif MG_ARCH == MG_ARCH_ESP32 +#define MG_HEALTH_DATA_SIZE 23 +#else #define MG_HEALTH_DATA_SIZE 10 #endif +#endif // MG_HEALTH_DATA_SIZE // Places the record in RAM that survives a reset. Define in mongoose_config.h // to match the linker script, e.g. __attribute__((section(".mg_health"))) @@ -4393,6 +4399,13 @@ void mg_ota_poll(struct mg_mgr *); #define MG_HEALTH_RAM #endif +// CPU core architecture +enum mg_health_arch { + MG_HEALTH_ARCH_ARM = 0, + MG_HEALTH_ARCH_XTENSA, + MG_HEALTH_ARCH_RISCV +}; + // Why the device restarted. Hardware usually reports several flags at once, // so these are ordered by how much they tell you: the most specific cause a // device reports wins @@ -4409,7 +4422,8 @@ enum mg_health_reason { struct mg_health { char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data - uint32_t reset_reason; // enum mg_health_reason that started this boot + uint16_t reset_reason; // enum mg_health_reason that started this boot + uint16_t arch; // enum mg_health_arch, core architecture (ARM, etc) uint32_t cpuid; // ARM CPU ID uint32_t data[MG_HEALTH_DATA_SIZE]; }; @@ -4442,9 +4456,57 @@ static inline const char *mg_health_reason_str(int reason) { } } +#if MG_ARCH == MG_ARCH_ESP32 +#ifndef CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH +#define CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH 0 +#endif +#ifndef ESP_PARTITION_SUBTYPE_DATA_COREDUMP +#define ESP_PARTITION_SUBTYPE_DATA_COREDUMP 0x03 +#endif +// clang-format off +static inline uint16_t esp2mg(esp_reset_reason_t rr) { + switch(rr) { + case ESP_RST_POWERON: return (uint16_t) MG_HEALTH_RESET_POWER; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2) + case ESP_RST_PWR_GLITCH: +#endif + case ESP_RST_BROWNOUT: return (uint16_t) MG_HEALTH_RESET_BROWNOUT; + case ESP_RST_EXT: return (uint16_t) MG_HEALTH_RESET_PIN; + case ESP_RST_SW: return (uint16_t) MG_HEALTH_RESET_SOFTWARE; + case ESP_RST_DEEPSLEEP: return (uint16_t) MG_HEALTH_RESET_LOWPOWER; + case ESP_RST_INT_WDT: + case ESP_RST_TASK_WDT: + case ESP_RST_WDT: return (uint16_t) MG_HEALTH_RESET_WATCHDOG; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2) + case ESP_RST_CPU_LOCKUP: +#endif + case ESP_RST_PANIC: return (uint16_t) MG_HEALTH_RESET_FAULT; + default: break; + } + return (uint16_t) MG_HEALTH_RESET_UNKNOWN; +} +// clang-format on +#endif + #define MG_HEALTH_INIT() mg_health_init() extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *); extern void mg_health_init(void); +#else +#define MG_HEALTH_INIT() +static inline int mg_health_reason(void) { + return 0; +} +static inline const char *mg_health_reason_str(int reason) { + (void) reason; + return "unknown"; +} +static inline size_t mg_print_crash_record(mg_pfn_t fn, void *arg, + va_list *ap) { + (void) ap; + return mg_xprintf(fn, arg, "{}"); +} + +#endif // Cloud connector API to https://mdash.net - device management cloud diff --git a/src/arch_esp32.h b/src/arch_esp32.h index 24a4c366..a8f8257a 100644 --- a/src/arch_esp32.h +++ b/src/arch_esp32.h @@ -22,8 +22,10 @@ #include #include #include -// #include // For health record coredumps -// #include // For health record coredumps +#if MG_ENABLE_HEALTH +#include +#include +#endif #define MG_PATH_MAX 128 diff --git a/src/health.c b/src/health.c index 566e79eb..25b0fc77 100644 --- a/src/health.c +++ b/src/health.c @@ -1,12 +1,13 @@ #include "config.h" #include "health.h" +#if MG_ENABLE_HEALTH // The one health record. Lives in RAM that survives a warm reset, see // MG_HEALTH_RAM and the .mg_health region in the linker script struct mg_health mg_health_record MG_HEALTH_RAM; -// STM32 H723 support. Needs the _estack symbol from the linker script -#if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5)) +// STM32 H7, H5, C5 support. Needs the _estack symbol from the linker script +#if defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5) // Fault handler body. Runs in exception context: no printf, no malloc, no // blocking calls. Copies the unwind registers and raw stack into the health // record, then resets. @@ -48,52 +49,57 @@ void BusFault_Handler(void) __attribute__((alias("HardFault_Handler"))); void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler"))); #endif -// Start a new boot: validate the record, count the boot, store the reset -// reason. The current boot moves to prev_* slots first, so the previous -// boot's uptime and reason stay readable. Called by MG_HEALTH_INIT() +// Start a new boot: validate the record. Called by MG_HEALTH_INIT() void mg_health_init(void) { char magic[] = MG_HEALTH_MAGIC; - // struct mg_health *h = mg_health_get(); if (!mg_health_valid()) { // First boot, or RAM lost its contents memset(&mg_health_record, 0, sizeof(mg_health_record)); memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic)); } - -#if 0 && MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH - { +#if MG_ARCH == MG_ARCH_ESP32 +#if CONFIG_IDF_TARGET_ARCH_XTENSA + mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_XTENSA; +#elif CONFIG_IDF_TARGET_ARCH_RISCV + mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_RISCV; +#endif + esp_reset_reason_t rr = esp_reset_reason(); + mg_health_record.reset_reason = esp2mg(rr); +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH == 0 + MG_ERROR(("MG_HEALTH is enabled but CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=n")); +#else // After a crash, copy the backtrace from the core dump in flash: the same // addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...". // The core dump stays in flash, so check the reset reason, otherwise an // old crash is reported on every boot - static esp_core_dump_summary_t s; // Big on RISC-V, keep it off the stack - esp_reset_reason_t rr = esp_reset_reason(); + esp_core_dump_summary_t *s = calloc(1, sizeof(*s)); uint32_t *out = mg_health_record.data; - size_t n = 0, max = MG_HEALTH_DATA_SIZE; - if (rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT && rr != ESP_RST_TASK_WDT) - return; // Not a crash that went through the panic handler - if (max == 0 || esp_core_dump_get_summary(&s) != ESP_OK) return; - - mg_health_record.cpuid = 42; // ESP32 - mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT; - + size_t n = 0; memset(out, 0, sizeof(mg_health_record.data)); - out[n++] = s.exc_pc; + if (s == NULL) return; + if ((rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT && + rr != ESP_RST_TASK_WDT) // Not a crash caught by the panic handler + || esp_core_dump_get_summary(s) != ESP_OK) { + free(s); + return; + } + out[n++] = s->exc_pc; + memcpy(&out[n], s->exc_task, 16), n += 4; #if CONFIG_IDF_TARGET_ARCH_XTENSA - for (uint32_t i = 0; i < s.exc_bt_info.depth && n < max; i++) { - if (s.exc_bt_info.bt[i] != s.exc_pc) out[n++] = s.exc_bt_info.bt[i]; - } + out[n++] = s->ex_info.exc_a[0]; + out[n++] = s->ex_info.exc_a[1]; + size_t len = s->exc_bt_info.depth; + if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n); + memcpy(&out[n], s->exc_bt_info.bt, 4 * len); // sizeof(uint32_t) #elif CONFIG_IDF_TARGET_ARCH_RISCV - // No unwinder: take the return address, then words on the stack that - // point into executable memory (a heuristic, may include stale values) - if (n < max) out[n++] = s.ex_info.ra; - for (uint32_t i = 0; i + 4 <= s.exc_bt_info.dump_size && n < max; i += 4) { - uint32_t w; - memcpy(&w, &s.exc_bt_info.stackdump[i], sizeof(w)); // Byte array - if (esp_ptr_executable((void *) (uintptr_t) w)) out[n++] = w; - } -#endif - } + out[n++] = s->ex_info.exc_ra; + out[n++] = s->ex_info.exc_sp; + size_t len = s->exc_bt_info.dump_size / 4 ; // sizeof(uint32_t) + if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n); + memcpy(&out[n], s->exc_bt_info.stackdump, 4 * len); #endif + free(s); +#endif // CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH +#endif // MG_ARCH_ESP32 } static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) { @@ -107,11 +113,17 @@ static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) { size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) { size_t len = 0; if (mg_health_valid() && - mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) { + (mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_FAULT +#if MG_ARCH == MG_ARCH_ESP32 + || mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_WATCHDOG +#endif + )) { size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]); - len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", // - MG_ESC("version"), mg_health_record.magic[3], // - MG_ESC("cpuid"), mg_health_record.cpuid, // + len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%hu,%m:%hu,%m:%u,%m:[%M]}", // + MG_ESC("version"), mg_health_record.magic[3], // + MG_ESC("arch"), mg_health_record.arch, // + MG_ESC("reason"), mg_health_record.reset_reason, // + MG_ESC("cpuid"), mg_health_record.cpuid, // MG_ESC("data"), print_uint32, n, mg_health_record.data); } else { len += mg_xprintf(fn, arg, "{}"); @@ -119,3 +131,4 @@ size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) { (void) ap; return len; } +#endif diff --git a/src/health.h b/src/health.h index 67ba3fab..f77c0dd4 100644 --- a/src/health.h +++ b/src/health.h @@ -5,6 +5,8 @@ #include "arch.h" #include "printf.h" +#if MG_ENABLE_HEALTH + #ifndef MG_HEALTH_MAGIC #define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version #endif @@ -13,13 +15,15 @@ #define MG_HEALTH_RAM __attribute__((section(".mg_health"))) #endif -#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5)) -#define MG_HEALTH_DATA_SIZE 250 -#endif - #ifndef MG_HEALTH_DATA_SIZE +#if (defined(STM32H7_DEV_ID) || defined(STM32H5)) || defined(STM32C5) +#define MG_HEALTH_DATA_SIZE 250 +#elif MG_ARCH == MG_ARCH_ESP32 +#define MG_HEALTH_DATA_SIZE 23 +#else #define MG_HEALTH_DATA_SIZE 10 #endif +#endif // MG_HEALTH_DATA_SIZE // Places the record in RAM that survives a reset. Define in mongoose_config.h // to match the linker script, e.g. __attribute__((section(".mg_health"))) @@ -27,6 +31,13 @@ #define MG_HEALTH_RAM #endif +// CPU core architecture +enum mg_health_arch { + MG_HEALTH_ARCH_ARM = 0, + MG_HEALTH_ARCH_XTENSA, + MG_HEALTH_ARCH_RISCV +}; + // Why the device restarted. Hardware usually reports several flags at once, // so these are ordered by how much they tell you: the most specific cause a // device reports wins @@ -43,7 +54,8 @@ enum mg_health_reason { struct mg_health { char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data - uint32_t reset_reason; // enum mg_health_reason that started this boot + uint16_t reset_reason; // enum mg_health_reason that started this boot + uint16_t arch; // enum mg_health_arch, core architecture (ARM, etc) uint32_t cpuid; // ARM CPU ID uint32_t data[MG_HEALTH_DATA_SIZE]; }; @@ -76,6 +88,54 @@ static inline const char *mg_health_reason_str(int reason) { } } +#if MG_ARCH == MG_ARCH_ESP32 +#ifndef CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH +#define CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH 0 +#endif +#ifndef ESP_PARTITION_SUBTYPE_DATA_COREDUMP +#define ESP_PARTITION_SUBTYPE_DATA_COREDUMP 0x03 +#endif +// clang-format off +static inline uint16_t esp2mg(esp_reset_reason_t rr) { + switch(rr) { + case ESP_RST_POWERON: return (uint16_t) MG_HEALTH_RESET_POWER; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2) + case ESP_RST_PWR_GLITCH: +#endif + case ESP_RST_BROWNOUT: return (uint16_t) MG_HEALTH_RESET_BROWNOUT; + case ESP_RST_EXT: return (uint16_t) MG_HEALTH_RESET_PIN; + case ESP_RST_SW: return (uint16_t) MG_HEALTH_RESET_SOFTWARE; + case ESP_RST_DEEPSLEEP: return (uint16_t) MG_HEALTH_RESET_LOWPOWER; + case ESP_RST_INT_WDT: + case ESP_RST_TASK_WDT: + case ESP_RST_WDT: return (uint16_t) MG_HEALTH_RESET_WATCHDOG; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2) + case ESP_RST_CPU_LOCKUP: +#endif + case ESP_RST_PANIC: return (uint16_t) MG_HEALTH_RESET_FAULT; + default: break; + } + return (uint16_t) MG_HEALTH_RESET_UNKNOWN; +} +// clang-format on +#endif + #define MG_HEALTH_INIT() mg_health_init() extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *); extern void mg_health_init(void); +#else +#define MG_HEALTH_INIT() +static inline int mg_health_reason(void) { + return 0; +} +static inline const char *mg_health_reason_str(int reason) { + (void) reason; + return "unknown"; +} +static inline size_t mg_print_crash_record(mg_pfn_t fn, void *arg, + va_list *ap) { + (void) ap; + return mg_xprintf(fn, arg, "{}"); +} + +#endif diff --git a/tutorials/esp32/minimal/main/main.c b/tutorials/esp32/minimal/main/main.c index 6c525bb0..67d83a64 100644 --- a/tutorials/esp32/minimal/main/main.c +++ b/tutorials/esp32/minimal/main/main.c @@ -65,8 +65,10 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { } } +extern void wifi_init(const char *ssid, const char *pass); + void app_main(void) { - void wifi_init(const char *ssid, const char *pass); + MG_HEALTH_INIT(); wifi_init(WIFI_SSID, WIFI_PASS); struct mg_mgr mgr; diff --git a/tutorials/esp32/minimal/main/mongoose_config.h b/tutorials/esp32/minimal/main/mongoose_config.h index d7a026cb..efd4111c 100644 --- a/tutorials/esp32/minimal/main/mongoose_config.h +++ b/tutorials/esp32/minimal/main/mongoose_config.h @@ -1,4 +1,6 @@ #define MG_ARCH MG_ARCH_ESP32 +#define MG_ENABLE_HEALTH 1 + #define MG_TLS MG_TLS_BUILTIN //#define MG_TLS MG_TLS_MBED