The Short Answer: Fixing the CRC32 Checksum Failure
The config_esp_coredump_checksum_crc32 error occurs when the ESP-IDF core dump retrieval tool (espcoredump.py) calculates a CRC32 hash of the flash partition that does not match the hash stored in the core dump header. This almost always points to corrupted flash data rather than a bug in your application logic.
E (1245) esp_core_dump_flash: Core dump data checksum check failedOften followed by:
CRC32 mismatch: expected 0xXXXX, got 0xYYYY
When this error halts your debug workflow, these are the first three things to check, ranked by probability:
- Brownout During Flash Write: The ESP32 panicked and attempted to write the dump to SPI flash, but the 3.3V rail drooped before the 4KB flash page completed writing. Fix: Add a 100µF low-ESR electrolytic capacitor directly across the 3V3 and GND pins on your dev board.
- Partition Table Misalignment: Your custom
partitions.csvdoes not align thecoredumppartition to a 4KB (0x1000) boundary, causing the flash driver to silently truncate the write. Fix: Ensure the offset and size in your CSV are multiples of 0x1000. - Host Tool Version Mismatch: You are using an older global installation of
espcoredump.pythat expects an MD5 header while your firmware compiled with CRC32. Fix: Always run the tool via the ESP-IDF environment wrapper:idf.py coredump-inforather than calling the Python script directly.
Hardware Setup & Pin Mapping for JTAG Retrieval
To reliably capture and retrieve core dumps without relying on unstable UART bridges, we use the native USB/JTAG interface. This guide targets the ESP32-S3-WROOM-1-N8R8 (8MB Flash, 8MB PSRAM). The 8MB flash is critical here: it provides enough space for a robust 1MB core dump partition without starving your OTA (Over-The-Air) update slots.
Parts List
- MCU: ESP32-S3-DevKitC-1-N8R8 (Espressif official or Adafruit 5364 equivalent)
- Power Stability: 100µF 16V electrolytic capacitor + 0.1µF ceramic bypass capacitor
- Connection: High-quality USB-C data cable (must support USB 2.0 data lines, not just charge wires)
- Host OS: Linux/Windows/macOS with ESP-IDF v5.2+ installed
ESP32-S3 Native USB/JTAG Pin Mapping
| Function | GPIO Pin | Host Connection | Notes |
|---|---|---|---|
| USB D+ | GPIO 20 | USB-C Data+ | Native USB routing for espcoredump.py |
| USB D- | GPIO 19 | USB-C Data- | Do not use external pull-ups; S3 handles internally |
| 5V Input | 5V Pin | USB VBUS | Feed your board here to bypass diode drops |
| GND | GND | USB Shield/GND | Common ground reference for logic analyzer if probing |
| Status LED | GPIO 48 | N/A (Onboard) | Used in code below to signal panic state |
Decision Tree: Which Core Dump Checksum Should You Use?
ESP-IDF allows you to select the checksum algorithm in menuconfig under Component config → Core dump → Checksum type. Choosing the wrong one for your hardware constraints leads to either bloated partitions or silent corruption.
| Algorithm | Overhead | Speed | Collision Risk | Best Use Case |
|---|---|---|---|---|
| CRC32 | 4 Bytes | Very Fast (<1ms) | Low (1 in 4 billion) | Standard production firmware, battery-powered devices |
| MD5 | 16 Bytes | Slower (~5ms) | Negligible | High-vibration industrial environments with SPIFFS wear |
| None | 0 Bytes | Instant | 100% (No validation) | Extreme flash-space constraints (2MB boards), RAM-only dumps |
The Concrete Pick: For 95% of projects using the ESP32-S3 or ESP32-C3, select CRC32. It provides the optimal balance of write-speed (critical during a brownout panic) and data integrity. Only switch to MD5 if you are operating in high-EMI environments where single-bit flash flips are statistically probable.
Step-by-Step: Configuring menuconfig for Reliable Dumps
Follow these exact steps to configure your ESP-IDF project. This ensures the firmware and the host tool agree on the CRC32 parameter.
- Open Configuration: Run
idf.py menuconfigin your terminal. - Enable Core Dump: Navigate to Component config → Core dump. Ensure Enable Core dump is checked (
CONFIG_ESP_COREDUMP_ENABLE=y). - Set Destination: Select Data destination → Flash. (Do not use UART unless you are actively tethered to a PC 24/7).
- Set Checksum: Select Checksum type → CRC32. This explicitly sets
CONFIG_ESP_COREDUMP_CHECKSUM_CRC32=y. - Configure Partitions: Navigate to Partition Table → Custom partition table CSV. Ensure your
partitions.csvincludes the coredump row:
Note: The offset 0x3f0000 is standard for 4MB flash. For 8MB flash, you can move this to 0x7f0000 to keep it away from active OTA partitions.# Name, Type, SubType, Offset, Size, Flags coredump, data, coredump, 0x3f0000, 64K, - Save and Build: Press
Qto save, then runidf.py build flash monitor.
Complete ESP-IDF Code: Panic Trigger and Validation
The following C code targets the ESP32-S3. On boot, it checks the flash partition for an existing, valid core dump using the CRC32 checksum. If a valid dump exists, it logs the success. If you press the BOOT button (GPIO 0), it intentionally triggers a null-pointer panic to generate a fresh dump for testing.
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_core_dump.h"
#include "esp_system.h"
#include "driver/gpio.h"
// --- Pin Definitions for ESP32-S3-DevKitC-1 ---
#define BOOT_BUTTON_GPIO 0
#define STATUS_LED_GPIO 48
static const char *TAG = "COREDUMP_DEBUG";
// Initialize GPIOs for interaction
static void init_gpio(void) {
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << STATUS_LED_GPIO),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
io_conf.pin_bit_mask = (1ULL << BOOT_BUTTON_GPIO);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_conf);
}
// Check for existing core dump on boot
static void check_existing_coredump(void) {
ESP_LOGI(TAG, "Checking flash for existing core dump...");
esp_err_t ret = esp_core_dump_image_check();
if (ret == ESP_OK) {
ESP_LOGW(TAG, "Valid core dump found in flash! CRC32 checksum passed.");
ESP_LOGI(TAG, "Run 'idf.py coredump-info' on your host to decode it.");
// Blink LED rapidly to indicate dump is waiting
for (int i = 0; i < 10; i++) {
gpio_set_level(STATUS_LED_GPIO, 1);
vTaskDelay(pdMS_TO_TICKS(100));
gpio_set_level(STATUS_LED_GPIO, 0);
vTaskDelay(pdMS_TO_TICKS(100));
}
} else if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGI(TAG, "No core dump found in flash partition.");
} else {
ESP_LOGE(TAG, "Core dump partition check failed (Error: %s). Check partition alignment.", esp_err_to_name(ret));
}
}
// Task to monitor button and trigger intentional panic
static void panic_trigger_task(void *pvParameters) {
while (1) {
if (gpio_get_level(BOOT_BUTTON_GPIO) == 0) {
ESP_LOGE(TAG, "Boot button pressed. Triggering intentional panic...");
gpio_set_level(STATUS_LED_GPIO, 1); // Solid ON before crash
// Intentional Null Pointer Dereference
int *bad_ptr = NULL;
*bad_ptr = 42; // This will trigger a LoadProhibited panic and write the dump
// We will never reach here
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}
void app_main(void) {
ESP_LOGI(TAG, "ESP32-S3 Core Dump Debugger Starting...");
init_gpio();
// 1. Validate any previous crash data
check_existing_coredump();
// 2. Start monitoring for manual panic trigger
xTaskCreate(panic_trigger_task, "panic_task", 2048, NULL, 5, NULL);
ESP_LOGI(TAG, "System ready. Press BOOT button to trigger a test panic.");
// Main loop heartbeat
while (1) {
gpio_set_level(STATUS_LED_GPIO, 1);
vTaskDelay(pdMS_TO_TICKS(1000));
gpio_set_level(STATUS_LED_GPIO, 0);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
Extending, Simplifying, and Final Recommendations
Depending on your production constraints, you may need to pivot away from the standard flash-based CRC32 setup.
How to Extend (For RAM-Constrained / Low-Flash Boards)
If you are using an ESP32-C3 with only 2MB of flash and cannot spare 64KB for a partition, extend your debug capability by routing the core dump to UART.
In menuconfig, set Data destination to UART. When the device crashes, it will print a Base64 encoded string to the serial monitor. You can copy this string, save it to a .b64 file, and decode it on your host using espcoredump.py --core-format b64 --core my_dump.b64. This costs zero flash space but requires a human to be watching the serial log when the crash happens.
How to Simplify (For Final Production OTA)
If your firmware is stable and you are pushing a production OTA update where every byte of flash matters, simplify the build by disabling core dumps entirely. Set CONFIG_ESP_COREDUMP_ENABLE=n in your sdkconfig.defaults. This reclaims the partition space and eliminates the ~2ms overhead of CRC32 calculation during a fatal exception, allowing the watchdog to reset the system marginally faster.
CONFIG_ESP_COREDUMP_CHECKSUM_CRC32=y enabled with a 64KB flash partition for all prototyping and beta-hardware deployments. The 4-byte CRC32 overhead is negligible, and the ability to decode a stack trace from a device that crashed 3,000 miles away will save you days of blind guesswork. Only disable it when you sign off on the final gold-master firmware for mass manufacturing.
For deeper architectural details on how ESP-IDF handles memory mapping during a panic, refer to the official Espressif Core Dump API Guide and the ESP32-S3 System API Reference.






