This example application shows an integration with the ESP-IDF v3.3.5 SDK where a saved coredump is posted to the Memfault cloud for analysis.
If you already have an ESP-IDF project based on the v4.x or v3.x SDK, a step by step getting started guide can be found here.
The memfault SDK hooks into the pre-existing mechanism for saving coredumps. We save the Memfault coredump data in the exact same coredump partition the default implementation would use in the Memfault coredump format. This yields several main benefits:
- The memory regions which are collected collected is highly configurable (via
memfault_platform_coredump_get_regions()
). This means all of RAM can be collected or just key globals and statics depending on your use case. In the Memfault cloud you will be able to inspect any of the variables collected, just like you would in an IDE or GDB. - FreeRTOS thread recovery and traversal can take place in the Memfault cloud. By moving the recovery off the device, the fault handling logic is simplified and it's more likely for data to be recovered in the case where the crash happened as a result of memory corruption.
- Data can be posted directly from device to Memfault cloud for deduplication and analysis
The demo app is tested on the ESP-WROVER-KIT. The instructions below assume this development board is being used.
Make sure you have read the instructions in the README.md
in the root of the
SDK and performed the installation steps that are mentioned there.
We assume you have a working setup for the v4.2.2 SDK:
- have a version of CMAKE installed
- installed the xtensa toolchain and added it to your path
-
Delete the dummy esp-idf directory (if present) and clone a copy of the v4.2.2 SDK.
cd examples/esp32/ rm -rf esp-idf git clone -b v4.2.2 --recursive https://github.com/espressif/esp-idf.git esp-idf cd esp-idf export IDF_TOOLS_PATH=$(pwd) # you may need to install the sdk tools by running ./install.sh here source ./export.sh
-
Install the memfault build id utility:
pip install mflt-build-id
That's it! You should be good to go!
using pyinvoke:
invoke esp32.build
or use the Espressif command:
cd examples/esp32/apps/memfault_demo_app && idf.py build
invoke esp32.flash
or use the Espressif command:
cd examples/esp32/apps/memfault_demo_app && idf.py -p <console port, i.e -p /dev/ttyUSB1> flash
invoke esp32.console
or
miniterm.py ftdi://ftdi:2232/2 115200
The demo app is a simple console based app that has
commands to cause a crash in several ways. Once a coredump is captured, it can
be sent via WiFi to Memfault's web services to get analyzed and desymbolicated.
The memfault/http
component of the Memfault SDK is used to talk to Memfault's
web services.
Let's walk through this process step by step:
An API key will need to be baked into the demo app to enable it to communicate
with Memfault's web services. Go to https://app.memfault.com/, navigate to the
project you want to use and select 'Settings'. Copy the 'Project Key' and paste
it into esp32/apps/memfault_demo_app/main/config.c
, replacing
<YOUR PROJECT KEY HERE>
with your API key. Save the file and rebuild, reflash
the project and attach the debug console (see instruction above).
As a sanity check, let's request the device info from the debug console, enter
get_device_info
and press enter:
esp32> get_device_info
I (55239) mflt: S/N: 30AEA44AFF28
I (55239) mflt: SW type: esp32-main
I (55239) mflt: SW version: 1.0.0
I (55239) mflt: HW version: esp32-proto
In the platform reference implementation for ESP32, the hardware version is
hard-coded to esp32-proto
, software type is hard-coded to esp32-main
and the
WiFi MAC address is used as serial number. You can change this to match your
requirements (see
memfault_platform_device_info.c)
Command crash 1
will trigger a hard fault due to a bad instruction fetch at a
non-existing address, 0xbadcafe
:
esp32> crash 0
abort() was called at PC 0x400e54a3 on core 0
Backtrace: 0x4008fef0:0x3ffbc310 0x400900c7:0x3ffbc330 0x400e54a3:0x3ffbc350 0x400e5c0e:0x3ffbc370 0x4010a587:0x3ffbc390 0x400e4d09:0x3ffbc3b0 0x400d28b6:0x3ffbc3e0
Saving Memfault Coredump!
Rebooting...
Upon crashing, the coredump will be written to SPI flash. Note this can take a several seconds. Once done, the board should reboot and show the console prompt again.
To check whether the coredump has been captured, try running the get_core
command:
esp32> get_core
I (77840) mflt: Has coredump with size: 768
Memfault needs the symbols for the firmware in order to analyze the coredump.
The ESP32 SDK demo app symbol file can be found at:
/path/to/examples/esp32/apps/memfault_demo_app/build/memfault-esp32-demo-app.elf
This ELF file contains the symbols (debug information) amongst other things.
More information on Build Ids and uploading Symbol Files can be found here.
To post the collected crash to the Memfault cloud, first join a WiFi network:
esp32> join <SSID> <PASSWORD>
I (116450) connect: Connecting to '<SSID>'
Then post the data:
esp32> post_core
I (12419) mflt: Posting Memfault Data...
I (12419) mflt: Result: 0
New coredumps are only written if the coredump storage area is not already
occupied. Typically coredumps are cleared once they have been posted to the
memfault cloud by using the memfault_platform_coredump_storage_clear
function.
You can invoke this command from the cli by running clear_core
.
The following steps can be used to exercise OTA functionality:
-
Build and flash the default image per the instructions above. Confirm by running
get_device_info
in theidf.py monitor
console that the software version is1.0.0-dev
:esp32> get_device_info I (8358) mflt: S/N: A8032AEBF8E4 I (8358) mflt: SW type: esp32-main I (8358) mflt: SW version: 1.0.1-dev I (8358) mflt: HW version: esp32-proto
-
If not already done, post some data from the device to register it in your Memfault project's fleet:
esp32> join <ssid> <password> ... I (294468) connect: Connected esp32> post_chunks D (26028) mflt: Posting Memfault Data D (33288) mflt: Posting Memfault Data Complete! I (33288) mflt: Result: 0
-
Change the
MEMFAULT_ESP32_MAIN_FIRMWARE_VERSION
to"1.0.1"
inapps/memfault_demo_app/main/memfault_platform_device_info.c
, and rebuild (idf.py build
), but don't load it to the device. We'll use this build as our OTA payload! -
In the Memfault web app, go to "Software->OTA Releases" and create an OTA release for
1.0.1-dev
. Click "Add OTA Payload to Release", selectesp32-proto
as the "Hardware Version" ("Software Type" should auto fill toesp32-main
), and upload theapps/memfault_demo_app/build/memfault-esp32-demo-app.bin
file as the payload (note: this step can also be done with thememfault
cli, see here for more information) -
Deploy the release to your device; add it to a cohort under "Fleet->Cohorts", and set the "Target Release" to the
1.0.1-dev
version just created. -
Back to the esp32 console, run
memfault_ota_check
to confirm the release is available for our device:esp32> memfault_ota_check D (274688) mflt: Checking for OTA Update D (276338) mflt: OTA Update Available D (276338) mflt: Up to date!
-
Now trigger the OTA release. The board will download the release and install it to the inactive OTA slot, then reboot (example output below truncated for brevity):
esp32> memfault_ota_perform D (1455118) mflt: Checking for OTA Update D (1456618) mflt: OTA Update Available I (1456628) mflt: Starting OTA download ... I (1457798) esp_https_ota: Starting OTA... I (1457798) esp_https_ota: Writing to partition subtype 16 at offset 0x1e0000 I (1476958) esp_https_ota: Connection closed ... I (1477758) mflt: OTA Update Complete, Rebooting System ... Rebooting... ...
-
When the console is back up, confirm that the update was applied by checking the Software Version:
esp32> get_device_info I (8358) mflt: S/N: A8032AEBF8E4 I (8358) mflt: SW type: esp32-main I (8358) mflt: SW version: 1.0.1-dev I (8358) mflt: HW version: esp32-proto