This guide shows how to configure an ESP32-WROOM-32 board to generate and decode coredumps using only the Arduino CLI, the ESP32 Arduino core, and TraceBreaker (trbr
). You'll go through:
- Install required tools (Arduino CLI, ESP32 core,
trbr
). - Enable ELF-formatted coredumps in your sketch.
- Compile and upload firmware via Arduino CLI.
- Trigger a crash to write the dump to flash.
- Extract the raw flash partition containing the dump.
- Decode and inspect the dump using
trbr
.
Adapted from this Arduino Forum post.
Make sure the following command-line tools are available:
brew install jq pipx # macOS
sudo apt install jq pipx # Debian/Ubuntu
ⓘ
jq
is used to extract values from the Arduino CLI JSON output.pipx
is used to run Python-based tools likeesp-coredump
in isolation.pipx
is optional if you do not want to use esp-coredump.
Install the ESP32 Arduino core (one-time setup):
./arduino-cli \
core install esp32:esp32 \
--additional-urls https://espressif.github.io/arduino-esp32/package_esp32_index.json
Set the environment variables, for example:
# sometimes the ESP-IDF chip does not match the Arduino FQBN's board ID
chip=esp32
board_id=esp32da
fqbn="esp32:esp32:$board_id"
port="/dev/cu.usbserial-0001"
ⓘ Use the
board search
command of the Arduino CLI to find your device's FQBN.
ⓘ Use the
board list
command of the Arduino CLI to find your device's port.
Compile your project with coredump support:
elf_path=$(./arduino-cli compile \
--fqbn $fqbn \
--format json \
--build-property "compiler.c.extra_flags=-D CONFIG_LOG_DEFAULT_LEVEL=3 \
-D CONFIG_ESP_COREDUMP_ENABLE=1 \
-D CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF=1 \
-D CONFIG_ESP_COREDUMP_FLASH=1 \
-D CONFIG_ESP_COREDUMP_CHECKSUM_CRC32=1 \
-D CONFIG_ESP_COREDUMP_LOG_LVL=0 \
-D CONFIG_ESP_COREDUMP_USE_STACK_SIZE=1 \
-D CONFIG_ESP_COREDUMP_STACK_SIZE=1792 \
-D CONFIG_ESP_COREDUMP_MAX_TASKS_NUM=64 \
-D CONFIG_ESP_COREDUMP_CHECK_BOOT=1" \
--build-property "compiler.cpp.extra_flags=-D CONFIG_LOG_DEFAULT_LEVEL=3 \
-D CONFIG_ESP_COREDUMP_ENABLE=1 \
-D CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF=1 \
-D CONFIG_ESP_COREDUMP_FLASH=1 \
-D CONFIG_ESP_COREDUMP_CHECKSUM_CRC32=1 \
-D CONFIG_ESP_COREDUMP_LOG_LVL=0 \
-D CONFIG_ESP_COREDUMP_USE_STACK_SIZE=1 \
-D CONFIG_ESP_COREDUMP_STACK_SIZE=1792 \
-D CONFIG_ESP_COREDUMP_MAX_TASKS_NUM=64 \
-D CONFIG_ESP_COREDUMP_CHECK_BOOT=1" \
src/esp32backtracetest | \
jq -r '.builder_result.build_properties[]
| select(startswith("debug.executable="))
| split("=")[1]'
)
ⓘ Save the compiled ELF to the
elf_path
variable.
Upload the firmware to your board:
./arduino-cli upload \
--fqbn $fqbn \
--port $port \
src/esp32backtracetest
⚠️ After uploading, allow the firmware to run for 10–15 seconds (or until a crash occurs) to ensure the coredump is written to flash. The crash must happen and be flushed before powering off or resetting the board.
Open a serial monitor:
./arduino-cli monitor \
--port $port \
--config baudrate=115200
Find the path to esptool
:
esptool_dir=$(./arduino-cli board details \
--fqbn $fqbn \
--format json | \
jq -r '.build_properties[]
| select(startswith("runtime.tools.esptool_py.path="))
| split("=")[1]'
)
Use esptool
to read the flash partition that contains the coredump:
$esptool_dir/esptool \
--chip $chip \
--port $port \
--baud 115200 \
read_flash 0x3F0000 0x10000 coredump.raw
ⓘ
0x3F0000
is the coredump partition offset.0x10000
is the partition size.
After extracting the raw flash data (coredump.raw
), decode it using trbr
:
⚠️ The ELF used for decoding must match the binary running at the time of crash. SHA mismatches will result in decode failure.
./trbr decode \
--input coredump.raw \
--elf-path "$elf_path" \
--fqbn $fqbn \
--coredump-mode
Click to see the decode output
==================== THREADS INFO ====================
ID Target ID Frame
* 1 process 1073447304 0x400d15c5: functionC (num=42) at /Users/kittaakos/dev/sandbox/esp32-coredump-guide/src/esp32backtracetest/module2.cpp:9
2 process 1073469216 0x400867ca: esp_cpu_wait_for_intr () at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_hw_support/cpu.c:64
3 process 1073469588 0x400867ca: esp_cpu_wait_for_intr () at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_hw_support/cpu.c:64
4 process 1073443696 0x4000bff0: ??
5 process 1073445952 0x4000bff0: ??
6 process 1073498360 0x40083160: esp_crosscore_int_send_yield (core_id=1) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/crosscore_int.c:121
7 process 1073445240 0x4000bff0: ??
==================== THREAD 1 (TCB: 0x3ffb8188) ====================
0x400d15c5: functionC (num=42) at /Users/kittaakos/dev/sandbox/esp32-coredump-guide/src/esp32backtracetest/module2.cpp:9
0x400d15dd: functionB (ptr=0x3ffb221c) at /Users/kittaakos/dev/sandbox/esp32-coredump-guide/src/esp32backtracetest/module2.cpp:14
0x400d15b0: functionA (value=<optimized out>) at /Users/kittaakos/dev/sandbox/esp32-coredump-guide/src/esp32backtracetest/module1.cpp:7
0x400d1594: setup () at /Users/kittaakos/dev/sandbox/esp32-coredump-guide/src/esp32backtracetest/esp32backtracetest.ino:9
0x400d30b9: loopTask (pvParameters=0x0) at /Users/kittaakos/Library/Arduino15/packages/esp32/hardware/esp32/3.2.0/cores/esp32/main.cpp:59
0x40088ca4: vPortTaskWrapper (pxCode=0x400d309c <loopTask(void*)>, pvParameters=0x0) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139
==================== THREAD 2 (TCB: 0x3ffbd720) ====================
0x400867ca: esp_cpu_wait_for_intr () at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_hw_support/cpu.c:64
0x400d6715: esp_vApplicationIdleHook () at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/freertos_hooks.c:58
0x40089c8e: prvIdleTask (pvParameters=0x0) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/tasks.c:4353
0x40088ca4: vPortTaskWrapper (pxCode=0x40089c18 <prvIdleTask>, pvParameters=0x0) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139
==================== THREAD 3 (TCB: 0x3ffbd894) ====================
0x400867ca: esp_cpu_wait_for_intr () at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_hw_support/cpu.c:64
0x400d6715: esp_vApplicationIdleHook () at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/freertos_hooks.c:58
0x40089c8e: prvIdleTask (pvParameters=0x0) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/tasks.c:4353
0x40088ca4: vPortTaskWrapper (pxCode=0x40089c18 <prvIdleTask>, pvParameters=0x0) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139
==================== THREAD 4 (TCB: 0x3ffb7370) ====================
0x4000bff0: ??
0x40088f11: vPortClearInterruptMaskFromISR () at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h:560
0x40088f11: vPortExitCritical (mux=0x3ffbdd28 <xKernelLock>) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:514
0x4008ae69: ulTaskGenericNotifyTake (uxIndexToWait=0, xClearCountOnExit=1, xTicksToWait=4294967295) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/tasks.c:5765
0x4008231d: ipc_task (arg=<optimized out>) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/esp_ipc.c:62
0x40088ca4: vPortTaskWrapper (pxCode=0x400822dc <ipc_task>, pvParameters=0x0) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139
==================== THREAD 5 (TCB: 0x3ffb7c40) ====================
0x4000bff0: ??
0x40088f11: vPortClearInterruptMaskFromISR () at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h:560
0x40088f11: vPortExitCritical (mux=0x3ffbdd28 <xKernelLock>) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:514
0x4008ae69: ulTaskGenericNotifyTake (uxIndexToWait=0, xClearCountOnExit=1, xTicksToWait=4294967295) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/tasks.c:5765
0x400e1af2: timer_task (arg=<optimized out>) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_timer/src/esp_timer.c:459
0x40088ca4: vPortTaskWrapper (pxCode=0x400e1ae4 <timer_task>, pvParameters=0x0) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139
==================== THREAD 6 (TCB: 0x3ffc48f8) ====================
0x40083160: esp_crosscore_int_send_yield (core_id=1) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/crosscore_int.c:121
0x40089604: prvProcessTimerOrBlockTask (xNextExpireTime=0, xListWasEmpty=<optimized out>) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/xtensa/include/xt_utils.h:41
0x40089604: prvTimerTask (pvParameters=<optimized out>) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/timers.c:685
0x40088ca4: vPortTaskWrapper (pxCode=0x400894f4 <prvTimerTask>, pvParameters=0x0) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139
==================== THREAD 7 (TCB: 0x3ffb7978) ====================
0x4000bff0: ??
0x40088f11: vPortClearInterruptMaskFromISR () at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h:560
0x40088f11: vPortExitCritical (mux=0x3ffbdd28 <xKernelLock>) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:514
0x4008ae69: ulTaskGenericNotifyTake (uxIndexToWait=0, xClearCountOnExit=1, xTicksToWait=4294967295) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/tasks.c:5765
0x4008231d: ipc_task (arg=<optimized out>) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/esp_ipc.c:62
0x40088ca4: vPortTaskWrapper (pxCode=0x400822dc <ipc_task>, pvParameters=0x1) at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139
Compare the decode output with the official esp-coredump one:
pipx run esp-coredump info_corefile \
--core "" \
--core-format elf \
--off 0x3F0000 \
--save-core coredump.elf \
$elf_path
ⓘ This step requires Python,
pipx
, andesp-coredump
to be installed.
You might need to set
ESP-IDF
to the shell. For example,source ~/esp/v5.4.1/esp-idf/export.sh
.
Thanks to ptillisch for the helpful instructions.