Skip to main content

Espressif ESP32 ESP-IDF Integration Guide

· One min read

This tutorial will go over integrating the Memfault Firmware SDK into a system that is using Espressif's ESP-IDF for an ESP32 chip. It assumes you already have a working project/toolchain for the ESP32. If you do not, the official getting started guide is a great resource!

The integration is tested against versions of ESP-IDF >=3.3.6 and <=5.2.1. If you run into problems on other ESP-IDF versions, don't hesitate to reach out to

Memfault support!

This integration is tested on the ESP32-WROVER-KIT V4.1, but applies similarly to any ESP32-based board.

tip

An example project demonstrating the result of this integration can be found here, for reference:

https://github.com/memfault/esp32-standalone-example

Memfault also maintains a full demonstration project in the Memfault Firmware SDK, under examples/esp32

Integration Checklist

Follow these steps to integrate the Memfault Firmware SDK into an ESP-IDF project:

Detailed Walkthrough

1. Clone Memfault SDK

Using a Git client, clone the memfault-firmware-sdk repository:

git clone https://github.com/memfault/memfault-firmware-sdk.git

Note, the Memfault SDK does not need to be located within the esp directory, you may place it anywhere.

It might be preferable to add the Memfault SDK as a submodule to your project, so it is tracked as part of the project's git repository:

# add the Memfault SDK as a submodule, in third-party/memfault-firmware-sdk
git submodule add https://github.com/memfault/memfault-firmware-sdk.git third-party/memfault-firmware-sdk

2. Add Memfault SDK to the ESP-IDF CMake Build System

In your top level CMakeLists.txt, you will need to make two changes

  1. Register Memfault esp-idf as a "component"
  2. Add a POST_BUILD command to tag each binary with a build id which Memfault uses to uniquely identify it
note

The POST_BUILD command depends on the python pyelftools package, so you may need to install it by running pip install pyelftools.

The end result should look something like this:

# CMakeLists.txt (top-level)
cmake_minimum_required(VERSION 3.16)

# TODO: Item 1 to add for Memfault
# Update "/path/to" appropriately below
set(MEMFAULT_FIRMWARE_SDK /path/to/memfault-firmware-sdk)
include(${MEMFAULT_FIRMWARE_SDK}/ports/esp_idf/memfault.cmake)

# [...]
project(<YOUR_PROJECT>)

# TODO: Item 2 to add for Memfault
add_custom_command(TARGET ${CMAKE_PROJECT_NAME}.elf
POST_BUILD
COMMAND
python ${MEMFAULT_FIRMWARE_SDK}/scripts/fw_build_id.py ${CMAKE_PROJECT_NAME}.elf)
info

If you are using compact logging there is a slightly different form required, use the snippet found here

See the example ESP32 project in the Memfault SDK for a working example of this step, located at examples/esp32

3. (Optional) Add the Memfault Configuration Files

As of Memfault Firmware SDK 1.4.0, no configuration files are required by default! You can optionally add the necessary files shown in the Memfault SDK <1.4.0 tab if there are settings you wish to override.

4. Enable Coredump Collection in sdkconfig

By default, the esp-idf uses the CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y. You will need to update sdkconfig (either via idf.py menuconfig or manually) to CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH=y. The change you will see in the sdkconfig file will look something like:

diff --git a/sdkconfig b/sdkconfig
index a49446d..83a81a8 100644
--- a/sdkconfig
+++ b/sdkconfig
@@ -274,7 +274,7 @@ CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=160
CONFIG_TRACEMEM_RESERVE_DRAM=0x0
# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set
# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set
-CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y
+CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH=y

This can also be done in sdkconfig.defaults if your project does not version control the sdkconfig file (will require a fullclean build to take effect).

Note: The remainder of the COREDUMP settings, including CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF, do not affect the Memfault coredump. They are specific to the ESP-IDF coredump features.

5. Create a Coredump Flash Partition

You will need to allocate an internal flash partition to store panic data. Edit your partitions.csv file to add a coredump partition. If you do not have a partitions.csv file you can create one from the template below and place it in your top-level project directory where the sdkconfig file is located. Depending on your chip's Flash size you may need to modify the storage partition size in order to fit the coredump partition. Check the Flash chip size in menuconfig, see (Top) → Serial flasher config and check the "Flash Size" entry. Be sure to specify a custom partition file in the sdkconfig (see note below).

Depending on what data is included in the coredump, the size of the coredump partition might also need to be increased to a larger size, like 512K. The memfault_coredump_storage_compute_size_required() function in the Memfault SDK can be used to compute the maximum coredump size, which can vary depending on user configuration.

Note that the default Memfault coredump regions are defined by the memfault_platform_coredump_get_regions() function in the Memfault SDK's ESP-IDF port: ports/esp_idf/memfault/common/memfault_platform_coredump.c, but can be adjusted to suit the user's needs, either via Kconfig options or by implementing a separate memfault_platform_coredump_get_regions() to override the built-in weakly defined one.

# Name,   Type, SubType, Offset,  Size,  Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, fat, , 1M,
coredump, data, coredump,, 256K,
info

The coredump flash partition size must be aligned to 4096 bytes (4KB).

note

Be sure to indicate the actual name you chose for the partition file in the sdkconfig file. This can be done easily by running idf.py menuconfig, selecting (Top) → Partition Table, and setting the name of the "Custom partition CSV file". Optionally, you can edit the sdkconfig file directly (search for the variables prefixed with CONFIG_PARTITION_TABLE_):

CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition-table.csv"

6. (Optional) Implement memfault_platform_get_device_info()

As of Memfault Firmware SDK 1.4.0, the SDK provides a default implementation of the memfault_platform_get_device_info() dependency function. This populates the Memfault Device Info data with MAC address and some default version values.

This default implementation can be overridden using the Kconfig flag CONFIG_MEMFAULT_DEFAULT_GET_DEVICE_INFO=n.

The port implementation memfault_esp_port_get_device_info() can also be used in a custom implementation of memfault_platform_get_device_info() to override the default values, if it's useful to do so.

7. Initializing Memfault

You will need to initialize the SDK from your application. This can be done simply by adding a call to memfault_boot() from your main_app() during system boot. We recommend doing this as early as possible in your initialization process, so Memfault components can begin capturing data. Be sure to first initialize any dependencies used by memfault_platform_get_device_info() as this is called within memfault_boot.

Here's a code example:

#include "memfault/esp_port/core.h"

void app_main() {
memfault_boot();

// Remainder of your main task
}

8. Configure Memfault Project Key

The project key is set via a Kconfig variable, MEMFAULT_PROJECT_KEY.

In ESP-IDF this can be set in a few different places:

  • add a line to sdkconfig.defaults:

    CONFIG_MEMFAULT_PROJECT_KEY="your-project-key"
  • set it in sdkconfig, either via idf.py menuconfig or by directly editing the file

  • set it into an extra sdkconfig file, for example sdkconfig.memfault_project_key, and include the file when building:

    idf.py build -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.memfault_project_key"

9. Regenerate sdkconfig and recompile

Assuming that your project has already been built before adding Memfault you will need to rebuild now that the Memfault SDK has been integrated into your ESP-IDF project.

Note that below we recommend doing a fullclean build. This is not strictly required, but can help pick up all the cmake changes. See the ESP-IDF docs here for details

idf.py fullclean
idf.py menuconfig # Verify your settings if necessary
idf.py build

Testing things out

With UART console

If you built a project with a UART console the Memfault esp-idf port registers some extra CLI commands so you can easily try things out! Flash and connect to the esp32 dev board's UART console.

idf.py flash monitor
note

You may need to select a specific serial port, see the --help for the idf.py tool for details.

As a first step you can try collecting and sending a coredump with the following commands:

# force a crash
esp32> crash 3
# wait for reboot
# join network
esp32> join <SSID> <PASSWORD>
# Post data
esp32> post_chunks

No UART console

If you built a project without a UART console you can test a coredump by inserting an assert that will fail, e.g.

   // [...]
MEMFAULT_ASSERT(0);
Server-side rate limiting will apply to the device you're using to work on the integration process. Once you can see the device on the Memfault Web App, consider enabling Server-Side Developer Mode for it on the Memfault Web App to temporarily bypass these limits.

Testing Data Uploaded to Memfault

Now it's time to see data decoded in the Memfault platform. To do that, we first need to upload the generated symbol file (for ESP-IDF projects, it's usually build/<your project name>.elf, but see the above section on post-build commands for alternate situations), then trigger some test data collection on device, and then upload the data to Memfault for analysis.

Upload Symbol File

At this point, you should be able to generate test events and crashes and push the data to the Memfault UI.

You can confirm the error traces and crashes have arrived successfully by navigating to the "Issues" page- there should be a new issue with the "Symbols Missing" label:

Clicking into the Issue will show the trace and a message that the symbol file is missing. It can be uploaded by clicking the button highlighted below:

After this step, you will see the processed trace in the list of issues!

note

The Issue created when the symbol file is missing can now be set to resolved. It's good practice to always upload a symbol file before devices send any data.

Symbol files can also be uploaded from the Software → Symbol Files tab (follow this deep link to be brought to the symbol file upload point in the UI):

In addition to supporting decoding trace/coredump data, symbol files are also required for decoding Metrics.

tip

You can programmatically upload symbol files with the Memfault CLI tool.

Symbol files should be uploaded to Memfault before devices send any data, to ensure all device data can be decoded.

Sending Data to the Memfault Cloud

At this point, the Memfault SDK is able to recognize crashes, track reboots, and store metrics. Most of this information is stored in RAM, except for coredumps which we keep in flash. To send that information to the cloud we need to call the memfault_esp_port_http_client_post_data() function.

We recommend running it at a periodic interval. If there is no new data to send, the call reduces to a no-op. The same function can also be invoked via the Memfault CLI with the mflt post_chunks command.

int rv = memfault_esp_port_http_client_post_data();
if (rv != 0) {
MEMFAULT_LOG_ERROR("Unable to post Memfault data, error %d", rv);
}

Troubleshooting Data Transfer

If you encounter any issues in your data transfer implementation, Memfault has tools to help debug!

  • To troubleshoot data not getting uploaded or processed correctly by the Memfault cloud, take a look at the Integration Hub → Processing Log view. This provides a filterable, chronological view of recent errors that have occurred while processing received data. See this documentation page for more information on the Integration Hub.
  • A view you can use toview the raw "Chunk" data payloadsthat have arrived for your project.
  • Precanned Data Payloads you can pass through your `user_transport_send_chunk_data()` implementation to test data transfer in isolation.
  • Server-side rate limiting will apply to the device you're using to work on the integration process. Once you can see the device on the Memfault Web App, consider enabling Server-Side Developer Mode for it on the Memfault Web App to temporarily bypass these limits.

ESP-IDF Metrics

The Memfault SDK provides a number of built-in metrics for ESP-IDF projects, including but not limited to the following:

  • idle_task_run_time_percent : single core devices
  • idle0_task_run_time_percent + idle1_task_run_time_percent : dual core devices
  • mbedtls_mem_max_bytes
  • mbedtls_mem_used_bytes
  • tcp_drop_count
  • tcp_rx_count
  • tcp_tx_count
  • udp_drop_count
  • udp_rx_count
  • udp_tx_count
  • wifi_connected_time_ms
  • wifi_disconnect_count
  • wifi_sta_min_rssi
  • wifi_ap_oui
  • heap_free_bytes
  • heap_largest_free_block_bytes
  • heap_allocated_blocks_count
  • heap_min_free_bytes

Frequently Asked Questions

Sizing the Coredump Partition

If your coredump partition size is insufficient to store a full RAM coredump, the Memfault SDK will truncate the coredump to fit the available space. This usually results in a useful coredump (active stack and thread stacks are prioritized in the ESP-IDF port memfault_platform_coredump_get_regions() implementation). If you prefer to capture all of allocated RAM, either:

  1. Increase the coredump partition size to fit the maximum coredump size. You can use the memfault_coredump_storage_check_size() function to determine the maximum coredump size, which can vary depending on user configuration. It's recommended to call that function after system initialization. Note: ESP-IDF projects don't natively permit changing the partition table via an OTA update, so this option is generally useful only before devices are deployed
  2. Configure Memfault to use the inactive OTA slot to store coredumps. The inactive OTA slot is generally only used when downloading a new OTA release, before it's applied, so it's usually safe to re-use the partition for coredumps. Use the CONFIG_MEMFAULT_COREDUMP_USE_OTA_SLOT=y Kconfig flag to enable this mode.
  3. Customize the ESP-IDF port memfault_platform_coredump_get_regions() implementation to store less data in the coredump (for example, removing the .bss, .data, or heap regions). This isn't required, because the Memfault coredump writer will safely truncate to fit available storage, but it will avoid the "Coredump Truncated to fit" warning from the Trace view in the UI.

Assert when initializing ESP-IDF Event Loop

Several ESP-IDF examples use the Event Loop library, and initialize it with this code:

// Create default event loop
ESP_ERROR_CHECK(esp_event_loop_create_default());

Memfault makes use of the default event loop for Wi-Fi metrics (enabled by default, controlled with CONFIG_MEMFAULT_ESP_WIFI_METRICS), and calls esp_event_loop_create_default() during memfault_boot().

The esp_event_loop_create_default() function can be safely called multiple times- but returns ESP_ERR_INVALID_STATE if it's already initialized. The above ESP_ERROR_CHECK macro will cause the application to assert in that case, which is not desirable.

Instead, Memfault recommends using the following pattern to safely initialize the default event loop:

// Try to create default event loop. If it's already created, it returns
// ESP_ERR_INVALID_STATE, equivalent to ESP_OK here.
esp_err_t err = esp_event_loop_create_default();
ESP_ERROR_CHECK(err == ESP_ERR_INVALID_STATE ? ESP_OK : err);

Espressif ESP8266 RTOS Integration Guide

· One min read

Overview

In this guide, we will walk through the steps for integrating the Memfault Firmware SDK into a project using Espressif's ESP8266 RTOS SDK. The integration has been tested against the v3.3 release.

Upon completion of the integration, the following subcomponents will be added to your system!

/img/docs/mcu/reboot-reason-chart.png
/img/docs/mcu/trace-reason-example.png
/img/docs/mcu/logs-with-coredump.png
/img/docs/mcu/metrics-api.png

Integration Steps

important

This tutorial assumes you have a working ESP8266 RTOS SDK environment and are able to flash a board supported by the SDK.

Add memfault-firmware-sdk and prepare port

You will first need to add the memfault-firmware-sdk to your projects.

$ cd ${YOUR_APP_ROOT_DIRECTORY}
$ mkdir -p components/memfault_port/config
$ echo $'COMPONENT_ADD_INCLUDEDIRS := config\n' > components/memfault_port/component.mk
$ touch components/memfault_port/memfault_port.c
$ touch components/memfault_port/config/memfault_platform_config.h
$ touch components/memfault_port/config/memfault_metrics_heartbeat_config.def
$ touch components/memfault_port/config/memfault_trace_reason_user_config.def
$ git submodule add https://github.com/memfault/memfault-firmware-sdk.git \
components/memfault_port/memfault-firmware-sdk

When done, you should see the following directory structure:

components/memfault_port/
├── component.mk
├── config
│ ├── memfault_platform_config.h
│ ├── memfault_metrics_heartbeat_config.def
│ └── memfault_trace_reason_user_config.def
├── memfault-firmware-sdk
└── memfault_port.c

Add EXTRA_COMPONENT_DIRS to Project Makefile

First, update the Makefile for your project to pick up the Memfault ESP8266 port

diff --git a/Makefile b/Makefile
index 952298b..d97ce01 100644
--- a/Makefile
+++ b/Makefile
@@ -5,5 +5,8 @@

+ EXTRA_COMPONENT_DIRS += \
+ components/memfault_port/memfault-firmware-sdk/ports/esp8266_sdk/memfault \
+ components/memfault_port/memfault-firmware-sdk/ports/esp8266_sdk/memfault_freertos

include $(IDF_PATH)/make/project.mk

Apply Patches to ESP8266 RTOS SDK

There are two small updates to make to the ESP8266 RTOS SDK. These can be added using git apply:

$ cd ${IDF_PATH}
$ git apply ${MEMFAULT_FIRMWARE_SDK}/ports/esp8266_sdk/esp8266_v33_rtos.patch

The patch does two things:

  • Installs a facility to capture RTOS task creation
  • Adds a unique identifier to image so Memfault can perform accurate stack unwinds
diff --git a/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h b/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h
index 080ce65c..cca3a354 100644
--- a/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h
+++ b/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h
@@ -31,6 +31,11 @@

#include "sdkconfig.h"

+#ifdef CONFIG_MEMFAULT
+#include "memfault/ports/freertos_trace.h"
+#define INCLUDE_xTaskGetHandle 1
+#endif
+
#ifndef __ASSEMBLER__
#include <stdlib.h>
#include "rom/ets_sys.h"
diff --git a/make/project.mk b/make/project.mk
index c06835b2..681db825 100644
--- a/make/project.mk
+++ b/make/project.mk
@@ -508,7 +508,12 @@ COMPONENT_LINKER_DEPS ?=
$(APP_ELF): $(foreach libcomp,$(COMPONENT_LIBRARIES),$(BUILD_DIR_BASE)/$(libcomp)/lib$(libcomp).a) $(COMPONENT_LINKER_DEPS) $(COMPONENT_PROJECT_VARS)
$(summary) LD $(patsubst $(PWD)/%,%,$@)
$(CC) $(LDFLAGS) -o $@ -Wl,-Map=$(APP_MAP)
-
+ifndef IS_BOOTLOADER_BUILD
+ifdef CONFIG_MEMFAULT
+ $(summary) Adding Unique Build Id
+ $(PYTHON) $(PROJECT_PATH)/components/memfault_port/memfault-firmware-sdk/scripts/fw_build_id.py $@
+endif
+endif
app: $(APP_BIN) partition_table_get_info
ifeq ("$(CONFIG_SECURE_BOOT_ENABLED)$(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)","y") # secure boot enabled, but remote sign app image
@echo "App built but not signed. Signing step via espsecure.py:"

Implement Platform Dependencies

In order to save traces, you will need to implement a dependency function that tells memfault version information. You can copy the following template into the memfault_port.c file created in the first step:

caution

Be sure to fill in the appropriate.

//! @file components/memfault_port/memfault_port.c

#include "memfault/components.h"

#include <stdio.h>

#include "memfault/esp8266_port/core.h"

sMfltHttpClientConfig g_mflt_http_client_config = {
.api_key = "YOUR_PROJECT_KEY",
};

static char s_fw_version[32];

void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) {
if (s_fw_version[0] == 0) {
// initialize version
char build_id[7];
memfault_build_id_get_string(build_id, sizeof(build_id));
snprintf(s_fw_version, sizeof(s_fw_version), "1.0.0+%s", build_id);
}

// platform specific version information. For more details
// see https://mflt.io/version-nomenclature
*info = (sMemfaultDeviceInfo) {
.device_serial = "DEMOSERIAL",
.software_type = "app-fw",
.software_version = s_fw_version,
.hardware_version = "dvt",
};
}

int memfault_platform_boot(void) {
memfault_esp_port_boot();
return 0;
}

Initialize the Memfault platform from app_main()

Add a call to memfault_platform_boot() from your app_main()

//! your_project_main.c

#include "memfault/components.h"

// ...

void app_main()
{

memfault_platform_boot();
// ...

}

Periodically post data to Memfault

From a background task, periodically try sending data to Memfault when the device is connected to Wi-Fi by calling #include "memfault/esp8266_port/http_client.h". This routine will issue an HTTPS request if and only if there is new Memfault data to send.

#include "memfault/esp8266_port/http_client.h"

void your_background_task(void) {
memfault_esp_port_http_client_post_data();
}
tip

For testing, you can also force a post immediately by using the mflt post_chunks CLI command

Force a Crash and Upload to Memfault

Example crashes can be generated using the crash CLI:

esp8266> crash
Saving Memfault Coredump!
...
I (340) mflt: Memfault Build ID: 134cc1627c9a84c36bdba206b6d96940b6aae698
I (362) mflt: Coredumps will be saved at 0x110000 (524288B)
I (366) mflt: ESP Reset Cause 0x7
I (369) mflt: Reset Causes:

This is an example of ESP-IDF console component.
Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
esp8266>
esp8266> join <your_network> <your_network_password>
esp8266> post_chunks
D (24436) mflt: Posting Memfault Data
D (27019) mflt: Posting Memfault Data Complete!
I (27023) mflt: Result: 0

Test Tip

Data can also also be dumped out over the CLI using the mflt export_data CLI command.

esp8266> export_data
I (57880) mflt: MC:CAKmAgEDAQpmYXBwLWZ3CWwxLjAuMCsxMzRjYzEGY2R2dAShAYUZ00oAAAAAG4c=:

The CLI output can then be saved to a file and the "chunk" data can be posted to the Memfault Cloud using the Memfault CLI tool:

$ memfault --project-key ${YOUR_PROJECT_KEY} post-chunk --encoding sdk_data_export cli-output.txt
Found 2 Chunks. Sending Data ...
Success

Upload Symbol File

At this point, you should be able to generate a test trace event and push it to the Memfault UI. You can confirm the issue has arrived successfully by navigating to the "Issues" page. Follow the link pointed to below and upload a symbol file.

After this step, you will see the trace in the list of issues!

tip

You can programmatically upload symbol files with the Memfault CLI tool.

Troubleshooting Data Transfer

If you encounter any issues in your data transfer implementation, Memfault has tools to help debug!

  • To troubleshoot data not getting uploaded or processed correctly by the Memfault cloud, take a look at the Integration Hub → Processing Log view. This provides a filterable, chronological view of recent errors that have occurred while processing received data. See this documentation page for more information on the Integration Hub.
  • A view you can use toview the raw "Chunk" data payloadsthat have arrived for your project.
  • Precanned Data Payloads you can pass through your `user_transport_send_chunk_data()` implementation to test data transfer in isolation.
  • Server-side rate limiting will apply to the device you're using to work on the integration process. Once you can see the device on the Memfault Web App, consider enabling Server-Side Developer Mode for it on the Memfault Web App to temporarily bypass these limits.

nRF Connect SDK Integration Guide

· One min read

In this guide we will walk through the steps for integrating the Memfault Firmware SDK into a project using Nordic Semiconductor's nRF Connect SDK (NCS). All versions of the nRF Connect SDK since v1.6.0 include Memfault when downloaded from Nordic's website, so we suggest using the nRF Connect SDK >= v1.6.0 even though integration with Memfault has been tested as far back as nRF Connect SDK v1.4.0.

This integration is written for the nrf9160-DK, but is similar when targeting other boards with the nRF Connect SDK.

tip

nRF Connect SDK v1.6.0, and all versions since, has built-in support for the Memfault Firmware SDK on nRF9160-based targets. Nordic Semiconductor provides some excellent documentation on how to integrate Memfault in your existing nRF Connect SDK project, together with a sample integration project (source available here). We recommend following both this documentation page as well as Nordic's.

Upon completion of the integration, the following subcomponents will be added to your system!

/img/docs/mcu/reboot-reason-chart.png
/img/docs/mcu/trace-reason-example.png
/img/docs/mcu/logs-with-coredump.png

1. Create a Project

2. Set up the SDK

important

This tutorial assumes you have a working nRF Connect SDK Environment with nRF Connect SDK version >=1.6.0 and are able to flash a board supported by the SDK (i.e nRF91, nRF53, nRF52, etc).

important

Due to changes in behavior of the OTA server and checking of multiple certificates in NCS version v2.4.0, Memfault SDK versions < v1.5.0 are incompatible with NCS versions >= v2.4.0. Therefore, any projects with older versions of NCS and Memfault < v1.5.0 should first upgrade Memfault before upgrading NCS. Please contact us immediately if you encounter any cert-related issues.

Include memfault-firmware-sdk in your west.yml

NCS's west manifest includes the memfault-firmware-sdk repo and is automatically downloaded when running west update. By default, it is downloaded to <nRF Connect SDK path>/modules/lib/memfault-firmware-sdk/.

Update Kconfig options

Add or update the appropriate options in your system's prj.conf:

# Project-specific configuration settings

CONFIG_MEMFAULT=y
CONFIG_MEMFAULT_NCS_PROJECT_KEY="YOUR_PROJECT_KEY"

# On nRF52 or nRF53 projects, and nRF-Connect SDK <2.1.0 (Memfault SDK <
# 0.32.0), this setting is required to build the project (it de-selects the
# built-in Memfault HTTPS root certificate storage, which is only compatible
# with the nRF9160):
CONFIG_MEMFAULT_ROOT_CERT_STORAGE_CUSTOM=y

You can find more details on the available options using menuconfig, guiconfig, and in the Kconfig sources in modules/memfault-firmware-sdk/ports/zephyr/Kconfig.

3. "Hello Memfault"

Next, we'll add the remaining changes needed to produce some Memfault data, such as a Reboot Event or Coredump.

Add Application Config Files

Besides Kconfigs, the Memfault SDK is configured through definitions in 3 files. To start, create empty versions of these files:

$ cd $APPLICATION_DIR
$ mkdir -p config
$ touch config/memfault_platform_config.h
$ touch config/memfault_metrics_heartbeat_config.def
$ touch config/memfault_trace_reason_user_config.def

We will use these files in the following sections to define metrics and set other SDK options. To set up some include path dependencies between your application, NCS/Zephyr, and the Memfault SDK, add this line to your application CMakeLists.txt:

zephyr_include_directories(config)

See the nRF Connect Docs for more information.

Implement Platform Dependencies

The first dependency to complete is our memfault_platform_get_device_info implementation. There are a few options available:

  • MEMFAULT_DEVICE_INFO_BUILTIN: provides a built-in implementation using either dynamic or static values
  • MEMFAULT_DEVICE_INFO_CUSTOM: allows for a more custom implementation, entirely defined by your application

For most setups, we recommend MEMFAULT_DEVICE_INFO_BUILTIN.

For nRF9160 devices, no additional changes are needed as NCS takes care of many defaults.

With this complete, our application should build!

Using the Memfault Shell

By default, the Memfault Firmware SDK provides a set of shell commands to test and exercise different Memfault functions. Some features of the shell include commands to:

  • Trigger asserts and other faults
  • Reboot the device
  • Export Memfault data over the shell via base64 encoding

To confirm our integration, let's start with generating a reboot event to verify the reboot tracking component is working.

  1. Trigger a device reboot with mflt test reboot
  2. Wait for the device to reboot, reconnect, and automatically post any Memfault data using the nRF9160 LTE modem

With this chunk uploaded to Memfault, we can verify the event sent to Memfault by navigating to Fleet → Devices → <YOUR_DEVICE> and observing a new entry in the Reboots swimlane.

4. Manually Capture a Coredump

Next, we'll use the built-in commands to capture a coredump

  1. Trigger a software assert with mflt test assert
  2. Export the data using the previous method, such as over LTE or importing the exported chunks in the Chunks Debug page

After uploading the coredump data, Memfault will need your symbol file to process the sent data.

Upload a Symbol File

The remaining step is to upload your symbol file to allow Memfault to process the received coredump data. Your symbol file is located within your build folder. The default location is <workspace_root>/build/zephyr/zephyr.elf.

Symbol files can be uploaded from the Software → Symbol Files page.

After this step, you will see the trace in the list of issues!

tip

You can programmatically upload symbol files with the Memfault CLI tool.

5. Manually Capture Logs

The Memfault SDK will (sparingly) emit diagnostic logs to alert of integration configuration problems. The logging subsystem can also easily serve as logging infrastructure for your platform if you still need to set up one.

Memfault Logging Kconfigs

The Memfault logging component integrates as a Zephyr logging backend and is controlled via Kconfig. Add the following to your prj.conf: CONFIG_MEMFAULT_LOGGING_ENABLE. This will enable the Memfault logging backend to collect logs sent with Zephyr's LOG_X() and MEMFAULT_LOG_X.

Collecting Logs

Calls to the Zephyr or Memfault logging macros will automatically collect logs. The CLI provides a few commands to test this:

  1. Force some calls to the logging macros: mflt test logs
  2. Trigger a log collection: mflt test log_capture

After executing these commands, the SDK will now contain a serialized capture of these logs. Use the previous export method to send the chunks to Memfault.

6. Configuring Standard Metrics

The Metrics component is a simple and easy way to measure run-time performance and behavior of your device.

Memfault Metrics Kconfigs

The Metrics component is configured via Kconfig and enabled by default.

Built-in Metrics for the NCS

The nRF Connect SDK provides some pre-enabled metrics for the nRF9160:

MetricDescription
ncs_lte_time_to_connect_msThe time it took to connect to the network
ncs_lte_connection_loss_countThe number of connection lost events
ncs_lte_psm_tau_secondsThe assigned PSM tracking area update (TAU)
ncs_lte_psm_active_time_secondsThe active time for PSM
ncs_lte_edrx_interval_msThe assigned eDRX interval
ncs_lte_edrx_ptw_msThe assigned eDRX paging time window (PTW)
ncs_lte_modeThe current RRC state the modem is in (enumeration)
ncs_lte_on_time_msThe time the modem is on
ncs_lte_reset_loop_detected_countThe number of times the modem detects a reset loop on the host processor*
ncs_lte_modem_fw_versionThe version of the modem firmware, e.g. nrf9160_1.3.5
ncs_lte_operatorThe network operator proving service, e.g. AT&T (US), EE (UK)
ncs_lte_snr_decibelsThe signal to noise ratio, an indicator of the signal quality
ncs_lte_rsrp_dbmThe reference signal received power, an indicator of signal quality
ncs_lte_tx_kilobytesThe number of kilobytes transmitted via the modem
ncs_lte_rx_kilobytesThe number of kilobytes received via the modem
ncs_lte_bandThe assigned frequency band (enumeration)**

*If the modem detects a reset loop in the main processor, it will prevent network attaches for 30 minutes. This count will indicate how many times this event occurs, and indicate to developers why their LTE modem was offline

**The band enumerations map to a 3GPP frequency band, which are defined in 3GPP Tech Spec 36.101, and are widely referenced across the web (here, for example).

These metrics are enabled with the Kconfig CONFIG_MEMFAULT_NCS_LTE_METRICS=y.

Additionally, there is built-in metric, ncs_connection_poll_unused_stack, for the LTE connection poll thread stack. This metric is enabled with the Kconfig CONFIG_MEMFAULT_NCS_STACK_METRICS=y.

When setting up a Project in the Memfault app, selecting the nRF91 MCU will pre-populate a few fleet metrics charts visualizing the pre-enabled metrics:

Image showing the pre-populated fleet metric charts

See the list in this file in the NCS repo:

Defining Custom Metrics

Heartbeat metrics allow you to easily monitor your platform and confirm it is operating as expected.

Typical Heartbeat Examples
  • investigate problems that didn't cause a reboot (bad connectivity, network or sensor error rates) and marginality that crops up in a fleet
  • providing leading indicators of problems (rapid battery drain, drop in activity, etc)
  • compare trends across releases (improved connectivity, data efficiency etc)

Best Practices around each metric type that we recommend:

  • Timers - These are used to track time spent in particular states and can be used for debugging connectivity, battery life, and performance issues.
  • Counters - Track counts of a particular event class taking place. Suggested use cases are:
    • Operations your system are performing (number of events serviced by a task, bytes sent over network, bytes written to flash). Alerts can be configured to identify devices operating outside normal thresholds.
    • Counts of events for events that should never happen (i2c bus write error count, flash write error). You can alert if any of these situations are seen.
  • Gauges - These are values sampled at the end of each heartbeat interval. Common items include
    • Battery Metrics (Drop over an hour, current percent)
    • Heap utilization / stack high watermark

Add Metric to memfault_metrics_heartbeat_config.def

//! @file memfault_metrics_heartbeat_config.def
MEMFAULT_METRICS_KEY_DEFINE(MainTaskWakeups, kMemfaultMetricType_Unsigned)

Instrument Code to Update Heartbeat

void my_main_task(void) {
while (1) {
your_rtos_wait_for_event();
MEMFAULT_METRIC_ADD(MainTaskWakeups, 1);
}
}

7. Automatically Upload Data

note
Server-side rate limiting will apply to the device you're using to work on the integration process. Once you can see the device on the Memfault Web App, consider enabling Server-Side Developer Mode for it on the Memfault Web App to temporarily bypass these limits.

In your prj.conf add CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD=y.

Sending data to Memfault over UDP

Memfault servers do not accept UDP transfers. To transfer data from a device that can send UDP datagrams (such as the nRF9160), an additional server is necessary to act as a relay between the device and Memfault servers.

A working sample of a complete set-up can be found here. It features a Python UDP server that relays data to Memfault, and a firmware implementation that includes a UDP client. The device and the Python server use a simple binary encoding format that includes the following sections, separated by a NULL byte:

  • A version section identifying the encoding format for forward compatibility.
  • A Project Key.
  • The device serial.
  • The Memfault chunk.

This configuration allows for a simple, stateless UDP server that just passes data over to Memfault.

Troubleshooting Data Transfer

See the docs on data transfer troubleshooting.

8. Next Steps

Now that the Memfault SDK is integrated on your device, there are many other features and options to explore. Check out these sections of our docs for more information:

Configuring and Invoking an OTA Update

important

Due to changes in behavior of the OTA server and checking of multiple certificates in NCS version v2.4.0, Memfault SDK versions < v1.5.0 are incompatible with NCS versions >= v2.4.0. Therefore, any projects with older versions of NCS and Memfault < v1.5.0 should first upgrade Memfault before upgrading NCS. Please contact contact us immediately if you encounter any cert-related issues.

LTE/Internet Connected Devices (nRF9160)

The NRF Connect sample integration project does not come with OTA support enabled by default. To enable this support a few things need to be added. The update process is invoked with a CLI command, this needs to be enabled. the memfault_platform_config.h file, add this line:

#define CONFIG_MEMFAULT_FOTA_CLI_CMD    1

Add or update the appropriate options in your system's prj.conf:

# The subsystems we need so OTA payloads can be written to
# flash and updated by MCUBoot
CONFIG_DFU_TARGET=y
CONFIG_DFU_TARGET_MCUBOOT=y
CONFIG_IMG_MANAGER=y
CONFIG_FLASH=y
CONFIG_IMG_ERASE_PROGRESSIVELY=y

# For Memfault FOTA, we will use the FOTA_DOWNLOAD API's
# from the nRF Connect SDK which depends on the DOWNLOAD_CLIENT
CONFIG_FOTA_DOWNLOAD=y
CONFIG_DOWNLOAD_CLIENT=y

# Enable printing of file download progress to console
CONFIG_FOTA_DOWNLOAD_PROGRESS_EVT=y
CONFIG_MEMFAULT_FOTA=y
CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE=400
CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=1600

The full prj.conf file can be viewed here.

Confirm you have already built and uploaded the OTA compatible app_update.bin to Memfault with the appropriate semantic version set. The device you wish to update should be running a lower version.

Finally to invoke the OTA process enter the following command:

uart:~$ mflt_nrf fota
<inf> <mflt>: Checking for FOTA
<inf> <mflt>: FOTA Update Available. Starting Download!
<inf> download_client: Setting up TLS credentials, tag 1003
<inf> download_client: Configuring socket timeout (30 s)
...
<inf> <mflt>: FOTA In Progress
<inf> download_client: Downloaded 2048/204467 bytes (1%)
<inf> download_client: Downloaded 4096/204467 bytes (2%)
...
<inf> download_client: Downloaded 204467/204467 bytes (100%)
<inf> download_client: Download complete
<inf> dfu_target_mcuboot: MCUBoot image upgrade scheduled. Reset device to apply
*** Booting Zephyr OS build v2.7.99-ncs1 *** to install update!
I: Starting bootloader
I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
I: Secondary image: magic=good, swap_type=0x2, copy_done=0x3, image_ok=0x3

The device resets automatically, running your new firmware.

Wi-Fi Connected Devices (nRF7002)

Devices using the nRF7002 Wi-Fi module can use the same Memfault OTA configuration settings as the nRF9160. Be aware that when using the nRF7002 with an nRF5340 host processor (as on the nRF7002 DK), the secondary partition used to stage the OTA payload for MCUBoot needs to be placed on external flash, since the nRF5340 does not have enough internal flash to hold both the secondary and primary partitions (see reference here).

Memfault OTA on the nRF7002 requires Memfault SDK 1.6.0+. See here for an example project demonstrating Memfault OTA on the nRF7002:

https://github.com/memfault/nrf7002-memfault-example

BLE Connected Devices (nRF52/nRF53)

For Nordic devices, FOTA can be performed using the normal Nordic-provided DFU tools, sample apps, and libraries.

Follow the below links for details on how to set that up:

The only Memfault-specific piece of that FOTA process is the fetching of the OTA binary payload, which is performed via an HTTP request against Memfault's server, passing the Memfault Project Key and the Device Serial for the target device:

Below is a brief diagram showing how an nRF52, mobile phone, and Memfault's server interact during an OTA update:

Reboot Reasons for HardFaults

For Nordic devices, the issue of obtaining reboot reasons when using TF-M is resolved in nRF Connect SDK v2.6.0. When a fault occurs from non-secure code, Zephyr fault handlers will be called and in turn, the Memfault fault handlers. This feature is enabled by default with the Kconfig flag CONFIG_TFM_ALLOW_NON_SECURE_FAULT_HANDLING=y.

Example projects are provided both by Memfault and Nordic:

Nordic Memfault Documentation:

Frequently Asked Questions (FAQ)

Updating the Memfault SDK

The nRF Connect SDK embeds the Memfault SDK as a module:

nrf/west.yml
loading...

If you would like to update the Memfault SDK without updating the nRF Connect SDK, you can do so by adding the Memfault SDK as a module in your application's west.yml:

west.yml

manifest:
remotes:
- name: nrf-connect-sdk
url-base: https://github.com/nrfconnect
# Add the Memfault GitHub repo
- name: memfault
url-base: https://github.com/memfault

projects:
- name: sdk-nrf
remote: nrf-connect-sdk
path: nrf
revision: 2.3.0
import: true

# Explicitly add the Memfault SDK, to override the version in the sdk-nrf manifest
- name: memfault-firmware-sdk
path: modules/lib/memfault-firmware-sdk
revision: 0.43.3
remote: memfault
self:
path: my_example_application

Zephyr's west tool will use the explicitly listed version of the memfault-firmware-sdk module instead of the one specified by the nrf-connect-sdk module. Reference:

After updating the West Manifest, run 👉west update👈 to pull in the Memfault SDK. Double check that you see the SDK source folder memfault-firmware-sdk under modules/lib/. You can manually check the version of the SDK with the following command (run from the West workspace root):

# open the Memfault Firmware SDK commit in the default web browser:
❯ open https://github.com/memfault/memfault-firmware-sdk/commit/$(git -C modules/lib/memfault-firmware-sdk rev-parse HEAD)

What is the advantage of using the Nordic Connect SDK (NCS) instead of stock Zephyr?

While Zephyr is a powerful RTOS and is used at the base operating system with the Nordic Connect SDK, using the NCS directly offers several advantages for developers targeting Nordic MCUs:

  • Optimized Memfault Integration: Nordic and Memfault collaborate closely to ensure seamless integration between NCS and Memfault. This collaboration helps ensure that the latest Memfault compatibility features are promptly incorporated into the NCS releases.

  • Simplified Memfault Implementation: The NCS provides built-in support for Memfault, making integration straightforward and requiring minimal development effort. This allows developers to quickly leverage Memfault's debugging and remote monitoring capabilities within their nRF MCU projects.

  • Hardware Compatibility: Nordic rigorously tests and validates the NCS for compatibility with their hardware platforms. This reduces the risk of encountering unforeseen hardware-specific issues when developing for nRF MCUs.

  • Up-to-date Zephyr Base: Nordic maintains the NCS by regularly incorporating updates from the mainline Zephyr project. This ensures developers benefit from the latest Zephyr features and bug fixes when using the NCS. Typically, the NCS releases occur 2-4 times per year.

DA1469x SDK Integration Guide

· One min read

Overview

In this guide we will walk through the steps for integrating the Memfault Firmware SDK into a project using Dialog's DA1469x product family. The integration has been tested against the "DA1469x SDK10.0.10.118" release.

Upon completion of the integration, the following subcomponents will be added to your system!

Integration Steps

important

This tutorial assumes you have a working DA1469x environment and are able to flash a board supported by the SDK.

Clone memfault-firmware-sdk into middleware

cd ${DA1469X_SDK_ROOT}/sdk/middleware
mkdir -p memfault
cd memfault
git clone https://github.com/memfault/memfault-firmware-sdk.git

Apply patches to DA1469x SDK

There are a few small patches that need to be applied to the Dialog SDK.

cd ${DA1469X_SDK_ROOT}
patch -p1 < ./sdk/middleware/memfault/memfault-firmware-sdk/ports/dialog/da1469x/gnu-build-id.patch
patch -p1 < ./sdk/middleware/memfault/memfault-firmware-sdk/ports/dialog/da1469x/freertos-config.patch
patch -p1 < ./sdk/middleware/memfault/memfault-firmware-sdk/ports/dialog/da1469x/fault-handlers.patch
note

When running the patch command in Windows environments you may see warning messages like the following which can be safely ignored:

(Stripping trailing CRs from patch; use --binary to disable.)

patch unexpectedly ends in middle of line

Create Memfault configuration files

You will need to create Memfault configuration files. These will be used later to customize the Memfault SDK.

# Navigate to same directory with other configs, i.e custom_config_qspi.h
cd ${YOUR_PROJECT_ROOT}/config/
touch memfault_platform_config.h
touch memfault_trace_reason_user_config.def
touch memfault_metrics_heartbeat_config.def

Add memfault-firmware-sdk to build

The memfault-firmware-sdk includes a script for easily adding all sources, includes, and necessary compiler flags to a project.

python ./sdk/middleware/memfault/memfault-firmware-sdk/scripts/eclipse_patch.py -p ${YOUR_PROJECT_ROOT} -m sdk/middleware/memfault/memfault-firmware-sdk --target-port dialog/da1469x
caution

After running the script, be sure to right click on the project in SmartSnippets Studio and select "Refresh" to pick up the changes.

Update Dialog custom_config setup

Navigate to the custom config you are using for your project (i.e "custom_config_qspi.h") and update the following settings

//! @file custom_config_*.h
#define dg_configUSE_MEMFAULT (1)
#define dg_configDISABLE_BACKGROUND_FLASH_OPS (1)
#define dg_configRETAINED_UNINIT_SECTION_SIZE (192)

// Note: configTIMER_TASK_STACK_DEPTH must be >= 256.
// TODO: Check if you already have a definition in your project.
// Otherwise, add the line below
#define configTIMER_TASK_STACK_DEPTH 256

// Optional: To make use of Dialog debug console logging add:
#define CONFIG_RETARGET (1)

Update Memfault Platform Config

#pragma once
//! @file memfault_platform_config.h

#define MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH 1

// Note: The default location coredumps are saved is NVMS log storage.
// This size can be adjusted depending on the amount of RAM regions collected
// in memfault_platform_coredump_get_regions()
#define MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES (32 * 1024)

#define MEMFAULT_USE_GNU_BUILD_ID 1

// TODO: Navigate to "Project" → "Settings" in the Memfault cloud UI (https://app.memfault.com/)
// and copy the "Project Key" here!
#define MEMFAULT_PROJECT_KEY ""

Implement memfault_platform_get_device_info

Implement the following function in your project. When an issue or metric is captured, this is the metadata that Memfault will collect alongside it and display in the UI.

#include "memfault/components.h"

void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) {
// !FIXME: Populate with platform device information

// IMPORTANT: All strings returned in info must be constant
// or static as they will be used _after_ the function returns

// See https://mflt.io/version-nomenclature for more context
*info = (sMemfaultDeviceInfo) {
// An ID that uniquely identifies the device in your fleet
// (i.e serial number, mac addr, chip id, etc)
// Regular expression defining valid device serials: ^[-a-zA-Z0-9_]+$
.device_serial = "DEMOSERIAL",
// A name to represent the firmware running on the MCU.
// (i.e "ble-fw", "main-fw", or a codename for your project)
.software_type = "app-fw",
// The version of the "software_type" currently running.
.software_version = "1.0.0",
// The revision of hardware for the device. This value must remain
// the same for a unique device.
// (i.e evt, dvt, pvt, or rev1, rev2, etc)
// Regular expression defining valid hardware versions: ^[-a-zA-Z0-9_\.\+]+$
.hardware_version = "dvt1",
};
}

Initialize Memfault

On bootup, make a call to memfault_platform_boot() to start the Memfault subsystem. We recommend doing this from system_init()

#include "memfault/components.h"

static void system_init( void *pvParameters )
{
// ...
#if defined CONFIG_RETARGET
retarget_init();
#endif
// Note: initialize Memfault after retarget_init() so logs will be displayed
memfault_platform_boot();
// ...
}

Confirm Project builds

info

If your project is based on a template from the Dialog SDK, you also need to remove the vApplicationStackOverflowHook implementation. The Memfault SDK implements this function and will automatically capture a coredump with full stack traces when a stack overflow takes place.

At this point, your project should compile and be able to boot. On startup, if you have logging enabled, you should see some messages like the following print when memfault_platform_boot() is called:

[I] MFLT: GNU Build ID: 6e6843f5f7410e0a51a0e62ce991fdd0d519eca7
[I] MFLT: S/N: DEMOSERIAL
[I] MFLT: SW type: da1469x-demo-app
[I] MFLT: SW version: 1.0.0
[I] MFLT: HW version: DA14695

Data Transport

With data collection in place, the final step is to push data to the Memfault cloud for analysis.

Extensive details about how data from the Memfault SDK makes it to the cloud can be found here. In short, all data is published via the same "chunk" REST endpoint.

When sending data over Bluetooth Low Energy, the Memfault Diagnostic GATT Service (MDS) can be used.

Initialize Memfault Diagnostic GATT Service

For the DA1469x, a port for the MDS is provided with the memfault-firmware-sdk. To add the service to your project, simply add a call to mds_boot() from the area of your code which is responsible for registering GATT services, i.e

#include "memfault/ports/ble/mds.h"

void your_ble_task(void) {
ble_peripheral_start();
// ...
mds_boot();
}

Verification & Tuning Steps

With Memfault compiling into your build and a Bluetooth Low Energy transport set up, it's now time to add some initial instrumentation, and test the integration!

Define First Trace Event

Trace events are used for generating alerts in the Memfault UI when unexpected events are encountered. Let's start by creating a generic type for critical errors that have been detected.

--- a/config/memfault_trace_reason_user_config.def
+++ b/config/memfault_trace_reason_user_config.def
@@ -0,0 +1 @@
+MEMFAULT_TRACE_REASON_DEFINE(critical_error)

Define First Heartbeat Metric

Heartbeat metrics allow you to easily monitor your platform and confirm it is operating as expected.

Typical Heartbeat Examples
  • investigate problems that didn't cause a reboot (bad connectivity, network or sensor error rates) and marginality that crops up in a fleet
  • providing leading indicators of problems (rapid battery drain, drop in activity, etc)
  • compare trends across releases (improved connectivity, data efficiency etc)

Best Practices around each metric type that we recommend:

  • Timers - These are used to track time spent in particular states and can be used for debugging connectivity, battery life, and performance issues.
  • Counters - Track counts of a particular event class taking place. Suggested use cases are:
    • Operations your system are performing (number of events serviced by a task, bytes sent over network, bytes written to flash). Alerts can be configured to identify devices operating outside normal thresholds.
    • Counts of events for events that should never happen (i2c bus write error count, flash write error). You can alert if any of these situations are seen.
  • Gauges - These are values sampled at the end of each heartbeat interval. Common items include
    • Battery Metrics (Drop over an hour, current percent)
    • Heap utilization / stack high watermark

Add Metric to memfault_metrics_heartbeat_config.def

//! @file memfault_metrics_heartbeat_config.def
MEMFAULT_METRICS_KEY_DEFINE(MainTaskWakeups, kMemfaultMetricType_Unsigned)

Instrument Code to Update Heartbeat

void my_main_task(void) {
while (1) {
your_rtos_wait_for_event();
MEMFAULT_METRIC_ADD(MainTaskWakeups, 1);
}
}

Add Test Commands to CLI

The Memfault SDK functionality can be easily exercised via CLI test commands.

note

If your platform does not have a CLI, these commands can also be wired up to a button press or called from main() on bootup from a test image

Test platform ports

#include "memfault/components.h"

int test_logging(int argc, char *argv[]) {
MEMFAULT_LOG_DEBUG("Debug log!");
MEMFAULT_LOG_INFO("Info log!");
MEMFAULT_LOG_WARN("Warning log!");
MEMFAULT_LOG_ERROR("Error log!");
return 0;
}
#include "memfault/components.h"

// Runs a sanity test to confirm coredump port is working as expected
int test_coredump_storage(int argc, char *argv[]) {

// Note: Coredump saving runs from an ISR prior to reboot so should
// be safe to call with interrupts disabled.
your_platform_disable_interrupts();
memfault_coredump_storage_debug_test_begin();
your_platform_enable_interrupts();

memfault_coredump_storage_debug_test_finish();
return 0;
}

Test core SDK functionality

#include "memfault/components.h"

// Triggers an immediate heartbeat capture (instead of waiting for timer
// to expire)
int test_heartbeat(int argc, char *argv[]) {
memfault_metrics_heartbeat_debug_trigger();
return 0;
}

int test_trace(int argc, char *argv[]) {
MEMFAULT_TRACE_EVENT_WITH_LOG(critical_error, "A test error trace!");
return 0;
}

//! Trigger a user initiated reboot and confirm reason is persisted
int test_reboot(int argc, char *argv[]) {
memfault_reboot_tracking_mark_reset_imminent(kMfltRebootReason_UserReset, NULL);
memfault_platform_reboot();
}

Test different crash types where a coredump should be captured

#include "memfault/components.h"

int test_assert(int argc, char *argv[]) {
MEMFAULT_ASSERT(0);
return -1; // should never get here
}

int test_fault(void) {
void (*bad_func)(void) = (void *)0xEEEEDEAD;
bad_func();
return -1; // should never get here
}

int test_hang(int argc, char *argv[]) {
while (1) {}
return -1; // should never get here
}

// Dump Memfault data collected to console
int test_export(int argc, char *argv[]) {
memfault_data_export_dump_chunks();
return 0;
}

Post Data to Cloud via Web Bluetooth

Data can be extracted over Bluetooth by implementing a MDS GATT Client in your gateway.

Memfault chunks can also easily be forwarded using Web Bluetooth.

tip

For Web Bluetooth support on some versions of Linux and Windows, you need to navigate to chrome://flags#enable-experimental-web-platform-features and enable "Experimental Web Platform Features". More details can be found here.

To try it out, simply:

  • Open or install a recent version of the Google Chrome Browser
  • Make sure your Bluetooth LE device is advertising
  • Navigate to the Memfault Diagnostic Web Bluetooth App at https://mflt.io/mds, click "Connect", and select your device.
  • Upon connection any data already collected by the memfault-firmware-sdk will be immediately sent and forwarded to Memfault for analysis. While connected, new data will be periodically flushed to Memfault. The default interval data is checked for is every 60 seconds and can be adjusted by adding MDS_POLL_INTERVAL_MS to your memfault_platform_config.h.

For development purposes, data can also be extracted and posted via a CLI or GDB connection. More details can be found below.

Post Chunks via Local Debug Setup

Prior to having an end-to-end transport in place, Memfault data can be exported over any serial connection or via GDB directly.

Post Chunks To Memfault

All data collected by Memfault can be exported as opaque packets called "chunks". To trigger an export, call the test_export command added to your device CLI.

If data has been collected, you will see strings with the format: MC:BASE64_ENCODED_CHUNK: in the dump.

note

It's perfectly fine for other logs to be interleaved with the exported data.

For example:

shell> reboot
[00:00:01] INFO: System Booting!
[00:00:02] INFO: Memfault Build ID: d8d6a047282f025fffa29fa767100f310bc40f80
shell> trace
# CLI command making a call to `memfault_data_export_dump_chunks`
shell> export
[00:00:10] INFO: MC:SFQCpwIBAwEHalRFU1RTRVJJQUwKbXRlc3Qtc29mdHdhcmUJajEuMC4wLXRlcw==:

Copy the logs from the console, navigate to the "Integration Hub / Chunks Debug" view for your project, and paste the logs:

Select "Next" to review the chunks that were identified and (optionally) update the "Device Serial" being used to report data:

Select "Post" to submit the chunks to Memfault. The chunks will appear in the view directly below. Check and make sure there are no "Errors" to address.

(Optional) Automate Chunk Posting

Posting chunks from a local setup can be automated using our CLI tool or GDB script.

note

This strategy can also be used even when an end-to-end transport is in place for local QA testing or in CI/CD test automation setups.

Automated Posting Options
Prerequisite: Project Key from Memfault UI

A Project key will be needed in order to communicate with Memfault's web services. Go to https://app.memfault.com/, navigate to the project you want to use and select 'Settings'→'General'. The 'Project Key' listed is what you will want to use.

With Desktop CLI Tool

Install the Python Memfault CLI Tool

The Memfault Desktop CLI tool. tool is written in Python and published publicly in the Python Package Index (pypi).

To install it, make sure you have a recent version of Python 3.x installed and run:

$ pip3 install memfault-cli
$ memfault --help

Save device logs to file

Start your console with your favorite terminal client. Let the system run for a bit, periodically dumping data to the console by calling memfault_data_export_dump_chunks().

You should see strings with the format: MC:BASE64_ENCODED_CHUNK: in the dump.

note

It's perfectly fine for other logs to be interleaved with the exported data.

For example:

# CLI command recording a reboot reason, then rebooting the system
shell> test_reboot
MFLT:[INFO] GNU Build ID: 3c92e3313fe1c9d00ac8687ce966d5f527a05ed3
MFLT:[INFO] S/N: freertos-example
MFLT:[INFO] SW type: qemu-app
MFLT:[INFO] SW version: 1.0.0
MFLT:[INFO] HW version: qemu-mps2-an385
MFLT:[INFO] Memfault Initialized!
# CLI command capturing a trace event
shell> test_trace
# CLI command making a call to `memfault_data_export_dump_chunks()`
shell> export
[00:00:10] INFO: MC:SFQCpwIBAwEHalRFU1RTRVJJQUwKbXRlc3Qtc29mdHdhcmUJajEuMC4wLXRlcw==:

Save the resulting logs to a file such as logs.txt.

Post chunks from logs with Memfault CLI

Run the desktop Memfault CLI tool on your saved log file. The utility will parse the logs, extract the Memfault data, and post it for processing!

$ memfault --project-key ${YOUR_PROJECT_KEY} post-chunk --encoding sdk_data_export logs.txt
Found 2 Chunks. Sending Data ...
Success
With GDB

Prerequisites

  • You need to have a way to debug your product with GDB as part of your development setup
  • The GDB version you are using needs to have the GDB Python API enabled. (It's generally the default or there is a -py version of GDB which has it included.)
  • You need to compile your firmware with debug symbol information (i.e -g CFLAG)
note

Even if you are using other compilers such as ARMCC or IAR, you can load the ELF (.out, .elf) generated and debug it in GDB as well.

Launch GDB and connect debugger

For example:

$ arm-none-eabi-gdb-py my_app.elf --ex="target remote :3333"
(gdb)

Load Memfault GDB script

Copy and paste and run the following in gdb:

python exec('try:\n from urllib2 import urlopen\nexcept:\n from urllib.request import urlopen'); exec(urlopen('https://app.memfault.com/static/scripts/memfault_gdb.py').read())
tip

Some GDB versions do not support Python scripting by default. If you see a message like "Python scripting is not supported in this copy of GDB", you might be able to run an alternative GDB binary with a -py suffix, for example arm-none-eabi-gdb-py.

Register Handler

Copy the command below and paste it into GDB and hit enter. This will load a handler which automatically posts data to Memfault whenever memfault_data_export_dump_chunks is called.

memfault install_chunk_handler --verbose --project-key <YOUR_PROJECT_KEY>
note

memfault_data_export_dump_chunks() needs to be called by your port as part of a CLI command or periodic task in order for the data to be extracted. For example:

#include "memfault/components.h"

int export_data_cli_command(int argc, char *argv[]) {
memfault_data_export_dump_chunks();
return 0;
}

void some_periodic_task(void) {
memfault_data_export_dump_chunks();
}

Try It Out!

Now, every time memfault_data_export_dump_chunks is called, the data passed as a parameter to the function will automatically be posted to the Memfault cloud. You don't have to do anything else! You will see logs print in the gdb CLI as data is posted:

(gdb) Continuing.
Successfully posted 45 bytes of chunk data
Successfully posted 53 bytes of chunk data
[...]

Troubleshooting Data Transfer

If you encounter any issues in your data transfer implementation, Memfault has tools to help debug!

  • To troubleshoot data not getting uploaded or processed correctly by the Memfault cloud, take a look at the Integration Hub → Processing Log view. This provides a filterable, chronological view of recent errors that have occurred while processing received data. See this documentation page for more information on the Integration Hub.
  • A view you can use toview the raw "Chunk" data payloadsthat have arrived for your project.
  • Precanned Data Payloads you can pass through your `user_transport_send_chunk_data()` implementation to test data transfer in isolation.
  • Server-side rate limiting will apply to the device you're using to work on the integration process. Once you can see the device on the Memfault Web App, consider enabling Server-Side Developer Mode for it on the Memfault Web App to temporarily bypass these limits.

Upload Symbol File

At this point, you should be able to generate test events and crashes and push the data to the Memfault UI.

You can confirm the error traces and crashes have arrived successfully by navigating to the "Issues" page- there should be a new issue with the "Symbols Missing" label:

Clicking into the Issue will show the trace and a message that the symbol file is missing. It can be uploaded by clicking the button highlighted below:

After this step, you will see the processed trace in the list of issues!

note

The Issue created when the symbol file is missing can now be set to resolved. It's good practice to always upload a symbol file before devices send any data.

Symbol files can also be uploaded from the Software → Symbol Files tab (follow this deep link to be brought to the symbol file upload point in the UI):

In addition to supporting decoding trace/coredump data, symbol files are also required for decoding Metrics.

tip

You can programmatically upload symbol files with the Memfault CLI tool.

Symbol files should be uploaded to Memfault before devices send any data, to ensure all device data can be decoded.

ModusToolbox™ SDK for PSoC™ 6

· One min read

This tutorial will go over integrating the Memfault Firmware SDK into a PSoC™ 6 project using Infineon's ModusToolbox™ Software SDK.

note

Note: these instructions were written using ModusToolbox™ release v3.0 and tested against the CY8CKIT-062S2-43012 evaluation board.

These instructions should also apply to any other Infineon PSoC™ 6 family board.

A full example project targeting the CY8CKIT-062S2-43012 development board can be found at https://github.com/memfault/mtb-example-memfault

Overview

This document describes how to integrate the Memfault SDK into a Infineon ModusToolbox™ based project.

Prerequisite

This guide assumes you already have an application running on a PSoC™ 6 development board. At a high level, this will require the following steps.

  1. Download ModusToolbox™ Software SDK from Infineon.

  2. Generate a sample project from that SDK. This can be done from the IDE or using the project-creator-cli binary. This can typically be found in the ModusToolbox™ installation directory, such as /Applications/ModusToolbox/tools_3.0.

    project-creator-cli --board-id CY8CKIT-062S2-43012 \
    --app-id mtb-example-anycloud-udp-server \
    --user-app-name my_example_app
  3. Compile and program application on development board, i,e

    $ cd my_example_app
    $ make getlibs
    $ make build -j8
    $ make program

For further documentation about ModusToolbox™ and how to use it, check out Infineon's website here!

Integrate the Memfault SDK

Add the Memfault Sources and Build Flags

  1. The Memfault SDK is implemented as a ModusToolbox™ middleware library and can be imported using the "library-manager". To launch the library manager for your project, either type make modlibs from the command line or click on "Library Manager" under the tools tab in the IDE. From there, search for "Memfault Firmware SDK" and click "Update"

    Alternatively, you can add the library manually by creating deps/memfault-firmware-sdk.mtb and running make getlibs

    $ echo 'https://github.com/memfault/memfault-firmware-sdk#0.31.0#$$ASSET_REPO$$/memfault-firmware-sdk/0.31.0' > deps/memfault-firmware-sdk.mtb
    $ make getlibs
  2. Add #include "memfault/ports/freertos_trace.h" to your FreeRTOSConfig.h file. By default ModusToolbox™ will place the file in your projects root directory.

    diff --git a/FreeRTOSConfig.h b/FreeRTOSConfig.h
    index 4718e2a..7fc9a78 100644
    --- a/FreeRTOSConfig.h
    +++ b/FreeRTOSConfig.h
    @@ -39,6 +39,8 @@
    #ifndef FREERTOS_CONFIG_H
    #define FREERTOS_CONFIG_H

    +#include "memfault/ports/freertos_trace.h"
    +
  3. Add a unique identifier to project. Memfault will use this to match the appropriate symbol files to decode information published by the memfault-firmware-sdk. This requires a modification to your project's Makefile.

    important

    This step requires the memfault build id package in your python environment, which can be installed with pip:

    pip install mflt-build-id

    Add the following snippet to the bottom of your project's Makefile:

    # Note these must be placed after including the MTB make files as those files define
    # many variables used to define memfault_post_build

    # Target/symbol file of the app
    APP_TARGET_FILE=$(MTB_TOOLS__OUTPUT_CONFIG_DIR)/$(APPNAME).$(MTB_RECIPE__SUFFIX_TARGET)
    # Program/hex file of the app
    APP_PROGRAM_FILE=$(MTB_TOOLS__OUTPUT_CONFIG_DIR)/$(APPNAME).$(MTB_RECIPE__SUFFIX_PROGRAM)

    # Add additional dependency to hex file to ensure mflt_build_id runs before it is built
    $(APP_PROGRAM_FILE): memfault_post_build

    # Custom target to run mflt_build_id after target file is built but before program file
    # is built to ensure correct build ID is included.
    memfault_post_build: $(APP_TARGET_FILE)
    $(PYTHON_PATH) -m mflt_build_id.__init__ $(APP_TARGET_FILE)

    .PHONY: memfault_post_build
  4. Update Makefile with an additional linker script. Add the following .ld file to the build. This will enable the collection of all FreeRTOS task state when a crash takes place.

    diff --git a/Makefile b/Makefile

    -LDFLAGS=
    +LDFLAGS= -T$(SEARCH_memfault-firmware-sdk)/ports/cypress/psoc6/memfault_bss.ld
  5. Implement the following dependency functions in your project following the inline steps below:

    #include "memfault/components.h"

    sMfltHttpClientConfig g_mflt_http_client_config = {
    // retrieve project key from: https://mflt.io/project-key
    .api_key = "YOUR_PROJECT_KEY",
    };

    void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) {
    // !FIXME: Populate with platform device information

    // IMPORTANT: All strings returned in info must be constant
    // or static as they will be used _after_ the function returns

    // See https://mflt.io/version-nomenclature for more context
    *info = (sMemfaultDeviceInfo) {
    // An ID that uniquely identifies the device in your fleet
    // (i.e serial number, mac addr, chip id, etc)
    // Regular expression defining valid device serials: ^[-a-zA-Z0-9_]+$
    .device_serial = "DEMOSERIAL",
    // A name to represent the firmware running on the MCU.
    // (i.e "ble-fw", "main-fw", or a codename for your project)
    .software_type = "app-fw",
    // The version of the "software_type" currently running.
    // "software_type" + "software_version" must uniquely represent
    // a single binary
    .software_version = "1.0.0-dev",
    // The revision of hardware for the device. This value must remain
    // the same for a unique device.
    // (i.e evt, dvt, pvt, or rev1, rev2, etc)
    // Regular expression defining valid hardware versions: ^[-a-zA-Z0-9_\.\+]+$
    .hardware_version = "dvt1",
    };
    }
  6. Initialize Memfault SDK from your main function before starting the FreeRTOS scheduler

    //! @file main.c
    #include "memfault/components.h"

    int main(void) {
    // ...

    memfault_platform_boot();

    // ...
    vTaskStartScheduler();
    }

Verification & Tuning Steps

Add Test Commands to CLI

With Memfault compiling into your build, it's now time to add some initial instrumentation and test the integration! The quickest way to do this is to extend or add a command line interface.

For projects that have a CLI expand here for instructions

The Memfault SDK functionality can be easily exercised via CLI test commands.

note

If your platform does not have a CLI, these commands can also be wired up to a button press or called from main() on bootup from a test image

Test platform ports

#include "memfault/components.h"

int test_logging(int argc, char *argv[]) {
MEMFAULT_LOG_DEBUG("Debug log!");
MEMFAULT_LOG_INFO("Info log!");
MEMFAULT_LOG_WARN("Warning log!");
MEMFAULT_LOG_ERROR("Error log!");
return 0;
}
#include "memfault/components.h"

// Runs a sanity test to confirm coredump port is working as expected
int test_coredump_storage(int argc, char *argv[]) {

// Note: Coredump saving runs from an ISR prior to reboot so should
// be safe to call with interrupts disabled.
your_platform_disable_interrupts();
memfault_coredump_storage_debug_test_begin();
your_platform_enable_interrupts();

memfault_coredump_storage_debug_test_finish();
return 0;
}

Test core SDK functionality

#include "memfault/components.h"

// Triggers an immediate heartbeat capture (instead of waiting for timer
// to expire)
int test_heartbeat(int argc, char *argv[]) {
memfault_metrics_heartbeat_debug_trigger();
return 0;
}

int test_trace(int argc, char *argv[]) {
MEMFAULT_TRACE_EVENT_WITH_LOG(critical_error, "A test error trace!");
return 0;
}

//! Trigger a user initiated reboot and confirm reason is persisted
int test_reboot(int argc, char *argv[]) {
memfault_reboot_tracking_mark_reset_imminent(kMfltRebootReason_UserReset, NULL);
memfault_platform_reboot();
}

Test different crash types where a coredump should be captured

#include "memfault/components.h"

int test_assert(int argc, char *argv[]) {
MEMFAULT_ASSERT(0);
return -1; // should never get here
}

int test_fault(void) {
void (*bad_func)(void) = (void *)0xEEEEDEAD;
bad_func();
return -1; // should never get here
}

int test_hang(int argc, char *argv[]) {
while (1) {}
return -1; // should never get here
}

// Dump Memfault data collected to console
int test_export(int argc, char *argv[]) {
memfault_data_export_dump_chunks();
return 0;
}

If your project does not already have a command line for testing, the memfault demo CLI can be added for quick verification. To do this, copy the following into a memfault_cli_task.c and then call memfault_cli_task_start() from your main routine.

//! @file memfault_cli_task.c

#include "cyhal.h"
#include "memfault/components.h"

#include "cy_retarget_io.h"

#include <FreeRTOS.h>
#include <task.h>

#define MEMFAULT_CLI_TASK_SIZE (1024)
#define MEMFAULT_CLI_TASK_PRIORITY (1)

static int prv_send_char(char c) {
cyhal_uart_putc(&cy_retarget_io_uart_obj, c);
return 0;
}

void memfault_cli_task(void *arg) {
cyhal_syspm_lock_deepsleep();
const sMemfaultShellImpl impl = {
.send_char = prv_send_char,
};
memfault_demo_shell_boot(&impl);

while (1) {
uint32_t num_bytes = cyhal_uart_readable(&cy_retarget_io_uart_obj);
if (num_bytes < 1) {
// Sleep for a little bit if there was not data
vTaskDelay(10);
continue;
}

uint8_t rx_byte;
// data should be available so we should not need to wait
const uint32_t uart_input_timeout_ms = 1;
cy_rslt_t result = cyhal_uart_getc(&cy_retarget_io_uart_obj, &rx_byte, uart_input_timeout_ms);
if (result != CY_RSLT_SUCCESS) {
MEMFAULT_LOG_ERROR("Unexpected UART read error: 0x%x", (int)result);
continue;
}

memfault_demo_shell_receive_char(rx_byte);
}
}

void memfault_cli_task_start(void) {
xTaskCreate(memfault_cli_task, "MFLT CLI", MEMFAULT_CLI_TASK_SIZE,
NULL, MEMFAULT_CLI_TASK_PRIORITY, NULL);
}

At boot, you should now see something like:

[I] Memfault Build ID: 7726fbc695f4c50db091746118ad69468e152341
[I] S/N: DEMOSERIAL
[I] SW type: app-fw
[I] SW version: 1.0.0-dev
[I] HW version: dvt1
[I] Reset Reason, Cy_SysLib_GetResetReason=0x10
[I] Reset Cause:
[I] Software Reset
[I] Memfault Initialized!

mflt>
mflt>
help>
clear_core: Clear an existing coredump
drain_chunks: Flushes queued Memfault data. To upload data see https://mflt.io/posting-chunks-with-gdb
export: Export base64-encoded chunks. To upload data see https://mflt.io/chunk-data-export
get_core: Get coredump info
get_device_info: Get device info
test_assert: Trigger memfault assert
test_busfault: Trigger a busfault
test_hardfault: Trigger a hardfault
test_memmanage: Trigger a memory management fault
test_usagefault: Trigger a usage fault
test_log: Writes test logs to log buffer
test_log_capture: Trigger capture of current log buffer contents
test_reboot: Force system reset and track it with a trace event
test_trace: Capture an example trace event
help: Lists all commands

Post Data to Cloud via Local Debug Setup

Prior to having an end-to-end transport in place, Memfault data can be exported over any serial connection or via GDB directly.

Post Chunks To Memfault

All data collected by Memfault can be exported as opaque packets called "chunks". To trigger an export, call the test_export command added to your device CLI.

If data has been collected, you will see strings with the format: MC:BASE64_ENCODED_CHUNK: in the dump.

note

It's perfectly fine for other logs to be interleaved with the exported data.

For example:

shell> reboot
[00:00:01] INFO: System Booting!
[00:00:02] INFO: Memfault Build ID: d8d6a047282f025fffa29fa767100f310bc40f80
shell> trace
# CLI command making a call to `memfault_data_export_dump_chunks`
shell> export
[00:00:10] INFO: MC:SFQCpwIBAwEHalRFU1RTRVJJQUwKbXRlc3Qtc29mdHdhcmUJajEuMC4wLXRlcw==:

Copy the logs from the console, navigate to the "Integration Hub / Chunks Debug" view for your project, and paste the logs:

Select "Next" to review the chunks that were identified and (optionally) update the "Device Serial" being used to report data:

Select "Post" to submit the chunks to Memfault. The chunks will appear in the view directly below. Check and make sure there are no "Errors" to address.

(Optional) Automate Chunk Posting

Posting chunks from a local setup can be automated using our CLI tool or GDB script.

note

This strategy can also be used even when an end-to-end transport is in place for local QA testing or in CI/CD test automation setups.

Automated Posting Options
Prerequisite: Project Key from Memfault UI

A Project key will be needed in order to communicate with Memfault's web services. Go to https://app.memfault.com/, navigate to the project you want to use and select 'Settings'→'General'. The 'Project Key' listed is what you will want to use.

With Desktop CLI Tool

Install the Python Memfault CLI Tool

The Memfault Desktop CLI tool. tool is written in Python and published publicly in the Python Package Index (pypi).

To install it, make sure you have a recent version of Python 3.x installed and run:

$ pip3 install memfault-cli
$ memfault --help

Save device logs to file

Start your console with your favorite terminal client. Let the system run for a bit, periodically dumping data to the console by calling memfault_data_export_dump_chunks().

You should see strings with the format: MC:BASE64_ENCODED_CHUNK: in the dump.

note

It's perfectly fine for other logs to be interleaved with the exported data.

For example:

# CLI command recording a reboot reason, then rebooting the system
shell> test_reboot
MFLT:[INFO] GNU Build ID: 3c92e3313fe1c9d00ac8687ce966d5f527a05ed3
MFLT:[INFO] S/N: freertos-example
MFLT:[INFO] SW type: qemu-app
MFLT:[INFO] SW version: 1.0.0
MFLT:[INFO] HW version: qemu-mps2-an385
MFLT:[INFO] Memfault Initialized!
# CLI command capturing a trace event
shell> test_trace
# CLI command making a call to `memfault_data_export_dump_chunks()`
shell> export
[00:00:10] INFO: MC:SFQCpwIBAwEHalRFU1RTRVJJQUwKbXRlc3Qtc29mdHdhcmUJajEuMC4wLXRlcw==:

Save the resulting logs to a file such as logs.txt.

Post chunks from logs with Memfault CLI

Run the desktop Memfault CLI tool on your saved log file. The utility will parse the logs, extract the Memfault data, and post it for processing!

$ memfault --project-key ${YOUR_PROJECT_KEY} post-chunk --encoding sdk_data_export logs.txt
Found 2 Chunks. Sending Data ...
Success
With GDB

Prerequisites

  • You need to have a way to debug your product with GDB as part of your development setup
  • The GDB version you are using needs to have the GDB Python API enabled. (It's generally the default or there is a -py version of GDB which has it included.)
  • You need to compile your firmware with debug symbol information (i.e -g CFLAG)
note

Even if you are using other compilers such as ARMCC or IAR, you can load the ELF (.out, .elf) generated and debug it in GDB as well.

Launch GDB and connect debugger

For example:

$ arm-none-eabi-gdb-py my_app.elf --ex="target remote :3333"
(gdb)

Load Memfault GDB script

Copy and paste and run the following in gdb:

python exec('try:\n from urllib2 import urlopen\nexcept:\n from urllib.request import urlopen'); exec(urlopen('https://app.memfault.com/static/scripts/memfault_gdb.py').read())
tip

Some GDB versions do not support Python scripting by default. If you see a message like "Python scripting is not supported in this copy of GDB", you might be able to run an alternative GDB binary with a -py suffix, for example arm-none-eabi-gdb-py.

Register Handler

Copy the command below and paste it into GDB and hit enter. This will load a handler which automatically posts data to Memfault whenever memfault_data_export_dump_chunks is called.

memfault install_chunk_handler --verbose --project-key <YOUR_PROJECT_KEY>
note

memfault_data_export_dump_chunks() needs to be called by your port as part of a CLI command or periodic task in order for the data to be extracted. For example:

#include "memfault/components.h"

int export_data_cli_command(int argc, char *argv[]) {
memfault_data_export_dump_chunks();
return 0;
}

void some_periodic_task(void) {
memfault_data_export_dump_chunks();
}

Try It Out!

Now, every time memfault_data_export_dump_chunks is called, the data passed as a parameter to the function will automatically be posted to the Memfault cloud. You don't have to do anything else! You will see logs print in the gdb CLI as data is posted:

(gdb) Continuing.
Successfully posted 45 bytes of chunk data
Successfully posted 53 bytes of chunk data
[...]

Upload Symbol File

important

With ModusToolbox™ your projects symbol files can be found in the build/ directory, i.e.ModusToolbox

build/CY8CKIT-062S2-43012/Debug/your_application.elf

At this point, you should be able to generate test events and crashes and push the data to the Memfault UI.

You can confirm the error traces and crashes have arrived successfully by navigating to the "Issues" page- there should be a new issue with the "Symbols Missing" label:

Clicking into the Issue will show the trace and a message that the symbol file is missing. It can be uploaded by clicking the button highlighted below:

After this step, you will see the processed trace in the list of issues!

note

The Issue created when the symbol file is missing can now be set to resolved. It's good practice to always upload a symbol file before devices send any data.

Symbol files can also be uploaded from the Software → Symbol Files tab (follow this deep link to be brought to the symbol file upload point in the UI):

In addition to supporting decoding trace/coredump data, symbol files are also required for decoding Metrics.

tip

You can programmatically upload symbol files with the Memfault CLI tool.

Symbol files should be uploaded to Memfault before devices send any data, to ensure all device data can be decoded.

Troubleshooting Data Transfer

If you encounter any issues in your data transfer implementation, Memfault has tools to help debug!

  • To troubleshoot data not getting uploaded or processed correctly by the Memfault cloud, take a look at the Integration Hub → Processing Log view. This provides a filterable, chronological view of recent errors that have occurred while processing received data. See this documentation page for more information on the Integration Hub.
  • A view you can use toview the raw "Chunk" data payloadsthat have arrived for your project.
  • Precanned Data Payloads you can pass through your `user_transport_send_chunk_data()` implementation to test data transfer in isolation.
  • Server-side rate limiting will apply to the device you're using to work on the integration process. Once you can see the device on the Memfault Web App, consider enabling Server-Side Developer Mode for it on the Memfault Web App to temporarily bypass these limits.

Data Transport Steps

With data collection verified locally, the final step is to push data automatically to the Memfault cloud for analysis! Check out the two options below for the one that matches your use case.

Using Wi-Fi Connection Manager (WCM) & HTTPS

If your project makes use of Wi-Fi, data can be posted directly to Memfault using HTTPS. To enable this functionality, make the following changes:

  1. Register Memfault Root certificates with stack
#include "memfault/components.h"
// [...]

//! Load root certificates necessary for talking to Memfault servers
result = cy_tls_load_global_root_ca_certificates(MEMFAULT_ROOT_CERTS_PEM, sizeof(MEMFAULT_ROOT_CERTS_PEM) - 1);
if (result != CY_RSLT_SUCCESS) {
MEMFAULT_LOG_ERROR("cy_tls_load_global_root_ca_certificates failed! rv=0x%x", (int)result);
}
  1. Add periodic call to post data using Memfault HTTP client
#include "memfault/components.h"
// [...]
memfault_http_client_post_chunk();

Post Data to Cloud via Other Transport

Extensive details about how data from the Memfault SDK makes it to the cloud can be found here. In short, the Memfault SDK packetizes data into "chunks" that must be pushed to the Memfault cloud via the chunk REST endpoint.

The Memfault SDK exposes an API that packetizes Memfault data into "chunks". These "chunks" need to be forwarded to the Memfault Cloud (either directly via HTTPs or indirectly via a gateway device such as a computer or mobile application)

tip

If you cannot post data directly using HTTP, no problem at all. In fact, that is one of the most common configurations. In these setups, data can be proxied from the MCU to a device which can perform a HTTP request over transports such as UART, BLE, COAP, LoRa, Zigbee, etc). The size of a "chunk" is fully configurable and can be tuned to best match your transport requirements. All message re-assembly is dealt with by Memfault in the cloud.

#include "memfault/components.h"

bool try_send_memfault_data(void) {
// buffer to copy chunk data into
uint8_t buf[USER_CHUNK_SIZE];
size_t buf_len = sizeof(buf);

bool data_available = memfault_packetizer_get_chunk(buf, &buf_len);
if (!data_available ) {
return false; // no more data to send
}

// send payload collected to chunks endpoint
user_transport_send_chunk_data(buf, buf_len);
return true;
}

void send_memfault_data(void) {
if (!memfault_packetizer_data_available()) {
return false; // no new data to send
}

// [... user specific logic deciding when & how much data to send
while (try_send_memfault_data()) { }
}

NXP MCUXpresso Getting Started Guide for ARM

· One min read

This tutorial will go over integrating the Memfault Firmware SDK into an NXP i.MX RT1060 project using NXP's MCUXpresso IDE.

note

Note: these instructions were written using mcuxpressoide-11.4.0_6224.x86_64 on ubuntu 22.04, using the MIMXRT1060-EVKB

The full example project can be found here: https://github.com/memfault/mcuxpresso-rt1060-example

These instructions should also apply to other NXP RT family boards (ex: MIMXRT1020-EVK), though there may be adjustments needed to the reset logic or linker script.

Overview

This document describes how to add the Memfault SDK to an MCUXpresso project. The strategy documented here follows these high-level steps:

Part 1: generate a sample project

  1. Download MCUXpresso SDK from NXP
  2. Generate a sample project from that SDK

Part 2: add the Memfault SDK

  1. Add the Memfault SDK sources to the project using the eclipse_patch.py script from the Memfault SDK
  2. Configure a few build settings (gnu build id, debug level)
  3. (Optional, for i.MX RT1060 network demo) Add the example HTTPS client port and associated application changes for the i.MX RT1060 LwIP project, and configure Memfault Project Key for uploading Memfault data from the EVK

Part 1: Setup and Verify LwIP Sample Application

note

👉 This section describes setting up the lwip_httpscli_mbedTLS_freertos sample project provided by the NXP SDK.

The integration steps in Part 2 can be applied to other projects (including projects not based on an NXP sample), so skip to that section if necessary.

  1. Install mcuxpresso: https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/mcuxpresso-integrated-development-environment-ide:MCUXpresso-IDE

  2. Download the SDK for the MIMXRT1060-EVKB (requires login to nxp.com): select all to download all components, and select “mcuxpresso” as the target toolchain. This example used the specific SDK version: SDK_2.10.1_MIMXRT1060-EVKB (NOTE: v2.11.1 was not compatible with MCUXpresso v11.4.0)

    Untitled
  3. Import the SDK into MCUXpresso:

    Untitled
  4. To create the sample project, click “Import SDK example(s)...”, then select our board, and select lwip_httpscli_mbedTLS_freertos:

    Untitled
  5. Default settings in the next screen are all OK, except for:

    1. Set library type (and hosting variant): change it to “NewlibNano (none)”
    2. Include semihost HardFault handler: uncheck
  6. Build the sample with “Project→Build all”

  7. Flash the sample with “Run→Debug as...→MCUXpresso IDE LinkServer (inc. CMSIS-DAP) probes”, assuming the EVK has the default CMSIS-DAP debug firmware

  8. Open a serial terminal (example below is using pyserial):

    pyserial-miniterm --raw /dev/ttyACM0 115200
  9. Pressing “Run” in MCUXpresso, the following output should appear in the serial terminal:

    Initializing PHY...
  10. Connect an ethernet cable to a router to see the demo proceed

Part 2: Integrate the Memfault SDK

note

👉 This section describes how to add the Memfault SDK sources. In this example, the Memfault Demo CLI is connected to the debug UART in order to test the Memfault functionality, and an HTTPS client is enabled over the Ethernet connection for uploading Memfault data.

Note that the provided HTTPS client example is specific to the i.MX RT1060 EVK sample app; other boards will potentially require a different connectivity implementation.

Add the Memfault Sources and Build Flags

  1. Download the Memfault SDK. either copy the folder into the project, or preferably, add it as a Git submodule:

    git submodule add https://github.com/memfault/memfault-firmware-sdk.git memfault-firmware-sdk
  2. Run the eclipse patch script to add the Memfault sources (including the "demo" component, which is the test CLI):

    python memfault-firmware-sdk/scripts/eclipse_patch.py --project-dir . --memfault-sdk-dir memfault-firmware-sdk --components core,util,metrics,panics,demo --target-port freertos
  3. Copy in the platform template files:

    cp memfault-firmware-sdk/ports/templates/* source/
  4. Refresh the project (right-click on the Project, “Refresh”)

  5. To enable the Memfault SDK sources in the build, uncheck “Exclude from build” for the “memfault_components” virtual folder:

    Untitled
  6. Add the memfault_platform_ram_backed_coredump.c source file to a new virtual folder

    1. Create a new virtual folder. this is to not conflict with the sources the eclipse_patch.py script operates on, so that script can be re-run in the future (eg if the Memfault SDK is updated) without affecting these port-specific sources, so name it mfltport_components

    2. Add the source file, which is located at memfault-firmware-sdk/ports/panics/src/memfault_platform_ram_backed_coredump.c, by dragging from a file explorer window into the virtual folder:

      Untitled
      Untitled
    3. Right-click on the new virtual folder, select “Properties”, and uncheck “Exclude from build”

  7. Add the following stubs to source/memfault_platform_port.c:

    #include <stdio.h>

    #include "fsl_debug_console.h"

    void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt,
    ...) {
    const char *lvl_str;
    switch (level) {
    case kMemfaultPlatformLogLevel_Debug:
    lvl_str = "D";
    break;

    case kMemfaultPlatformLogLevel_Info:
    lvl_str = "I";
    break;

    case kMemfaultPlatformLogLevel_Warning:
    lvl_str = "W";
    break;

    case kMemfaultPlatformLogLevel_Error:
    lvl_str = "E";
    break;

    default:
    return;
    break;
    }

    va_list args;
    va_start(args, fmt);

    char log_buf[128];
    vsnprintf(log_buf, sizeof(log_buf), fmt, args);

    PRINTF("[%s] MFLT: %s\n", lvl_str, log_buf);

    va_end(args);
    }

    bool memfault_platform_metrics_timer_boot(
    uint32_t period_sec, MemfaultPlatformTimerCallback callback) {
    (void)period_sec, (void)callback;
    // Schedule a timer to invoke callback() repeatedly after period_sec
    return true;
    }

    uint64_t memfault_platform_get_time_since_boot_ms(void) {
    // Return time since boot in ms, this is used for relative timings.
    return 0;
    }

    MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_tracking")
    static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE];

    MEMFAULT_WEAK void memfault_platform_reboot_tracking_boot(void) {
    sResetBootupInfo reset_info = {0};
    memfault_reboot_reason_get(&reset_info);
    memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info);
    }

    void memfault_reboot_reason_get(sResetBootupInfo *info) {
    const uint32_t reset_cause = 0; // TODO: Populate with MCU reset reason
    eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown;

    // TODO: Convert MCU specific reboot reason to memfault enum

    *info = (sResetBootupInfo){
    .reset_reason_reg = reset_cause,
    .reset_reason = reset_reason,
    };
    }
  8. Add memfault_platform_boot(); to main() (located in source/lwip_httpscli_mbedTLS_freertos.c.

  9. Confirm the project now builds.

Add GNU Build ID

  1. Add GNU build id:

    1. Add this flag to source/memfault_platform_config.h:

      #define MEMFAULT_USE_GNU_BUILD_ID 1
    2. Add the following file at source/gnu_build_id.ld:

      /*
      Custom section to be included in the linker
      command file, to support GNU build id.
      */

      SECTIONS
      {
      .note.gnu.build-id :
      {
      __start_gnu_build_id_start = .;
      KEEP(*(.note.gnu.build-id))
      }
      }
      INSERT AFTER .text;
    3. Add the necessary linker flags: right-click project→Properties. in the left pane, open “C/C++ Build”→”Settings”, then select MCU Linker→Miscellaneous, and add the --build-id build flag:

      Untitled
    4. Add a second flag to add the linker script fragment file to the build: -T "${workspace_loc:/${ProjName}/source/gnu_build_id.ld}"

  2. Set Debug Level to -g3 in “MCU C Compiler”→Settings:

    Untitled
  3. Clean and build. flash the project to the board and confirm the startup text appears in the serial console:

    [I] MFLT: GNU Build ID: 63a58c3567c22fd6786adbc4cc186d258e66efe7
    [I] MFLT: S/N: DEMOSERIAL
    [I] MFLT: SW type: app-fw
    [I] MFLT: SW version: 1.0.0
    [I] MFLT: HW version: dvt1
    [I] MFLT: Memfault Initialized!

Enable HTTPS Client and Test

  1. The last step is to pick up the necessary port + https client changes, which can be found under the source/directory in this repo: https://github.com/memfault/mcuxpresso-rt1060-example

    1. Copy the necessary files:

      1. Everything under source/
      2. lwip_httpscli_mbedTLS/httpsclient.c
    2. Configure a Memfault project key by replacing the placeholder text in lwip_httpscli_mbedTLS/httpsclient.c. The project key is located under Settings in the Memfault project, see https://docs.memfault.com/docs/mcu/export-chunks-over-console#grab-project-key-from-memfault-ui

      // Memfault project key
      const char *memfault_project_key = "<YOUR PROJECT KEY HERE>";
    3. Flashing this should show a new post_chunks console command when running help:

      [I] MFLT: GNU Build ID: 63a58c3567c22fd6786adbc4cc186d258e66efe7
      [I] MFLT: S/N: DEMOSERIAL
      [I] MFLT: SW type: app-fw
      [I] MFLT: SW version: 1.0.0
      [I] MFLT: HW version: dvt1
      [I] MFLT: Memfault Initialized!

      mflt> help
      get_core: Get coredump info
      clear_core: Clear an existing coredump
      crash: Trigger a crash
      trigger_logs: Trigger capture of current log buffer contents
      drain_chunks: Flushes queued Memfault data. To upload data see https://mflt.io/posting-chunks-with-gdb
      trace: Capture an example trace event
      get_device_info: Get device info
      reboot: Reboot system and tracks it with a trace event
      export: Export base64-encoded chunks. To upload data see https://mflt.io/chunk-data-export
      post_chunks: Upload chunks to Memfault
      help: Lists all commands
  2. Test out the demo commands, eg crash, reboot, post_chunks. The post_chunks command requires an ethernet cable to be connected, with network access to the internet.

    note

    Note: be sure to detach the debugger before running the crash or reboot commands. Depending on configuration, the debugger may prevent the board from resetting correctly!

  3. To see decoded data on app.memfault.com, the Symbol File will need to be uploaded. typically the symbol file will be a .axf file named from the project, for example Debug/mcuxpresso-rt1060-example.axf. symbol files can be uploaded through the Memfault web ui or using the Memfault CLI tool.

Troubleshooting Data Transfer

If you encounter any issues in your data transfer implementation, Memfault has tools to help debug!

  • To troubleshoot data not getting uploaded or processed correctly by the Memfault cloud, take a look at the Integration Hub → Processing Log view. This provides a filterable, chronological view of recent errors that have occurred while processing received data. See this documentation page for more information on the Integration Hub.
  • A view you can use toview the raw "Chunk" data payloadsthat have arrived for your project.
  • Precanned Data Payloads you can pass through your `user_transport_send_chunk_data()` implementation to test data transfer in isolation.
  • Server-side rate limiting will apply to the device you're using to work on the integration process. Once you can see the device on the Memfault Web App, consider enabling Server-Side Developer Mode for it on the Memfault Web App to temporarily bypass these limits.

Additional Notes

Data Cache

The Cortex-M7 chip on the i.MX RT10xx series chips has an instruction and data cache. The Memfault SDK relies on a minimum of 1 variable stored in non-initialized (.noinit) RAM when the system encounters a crash. If the RAM-backed coredump storage Memfault component is used, that data also should be located in a non-initialized section of RAM.

In order to ensure data integrity of the non-initialized data when the chip reboots after a crash, it's important to flush the D-cache. The CMSIS API to perform that flush is:

SCB_CleanDCache();

This call should be inserted before the chip is reset, typically in memfault_platform_reboot() just before NVIC_SystemReset():

void memfault_platform_reboot(void) {
// flush the data cache before issuing the reboot, to ensure the saved reboot
// reason data or RAM-backed coredump data is written out
SCB_CleanDCache();

NVIC_SystemReset();
while (1) { } // unreachable
}

For reference, see this white paper from Microchip:

http://ww1.microchip.com/downloads/en/DeviceDoc/Managing-Cache-Coherency-on-Cortex-M7-Based-MCUs-DS90003195A.pdf

And the CMSIS API documentation:

https://www.keil.com/pack/doc/CMSIS/Core/html/group__Dcache__functions__m7.html

Pinnacle™ 100 Modem Integration Guide

· One min read

In this guide we will walk through the steps for integrating the Memfault Firmware SDK into any project using Ezurio's Pinnacle™ 100 Cellular Modem (i.e Sentrius™ MG100).

tip

The Pinnacle-100-Firmware has built-in support for the Memfault Firmware SDK. Ezurio has released some excellent documentation on how to integrate Memfault and the AWS Out-of-Box demo includes a full Memfault integration that can be tried out. We recommend following both this documentation page as well as Ezurio's.

Upon completion of the integration, the following subcomponents will be added to your system!

Integration Steps

This tutorial assumes you have a working environment for building the Pinnacle 100 Firmware (such as an an application based on the AWS Out-of-Box Demo

Create a Memfault Account

The integration requires a Memfault cloud account to process data collected by the Memfault Firmware SDK. You can create one for free from here!

Update Kconfig options

Add or update the appropriate options in your system's prj.conf:

# Project-specific configuration settings

CONFIG_MEMFAULT_NCS_PROJECT_KEY="YOUR_PROJECT_KEY"
CONFIG_MEMFAULT_NCS_FW_TYPE="pinnacle-fw"

Upload Symbol File

At this point, you should be able to generate test events and crashes and push the data to the Memfault UI.

You can confirm the error traces and crashes have arrived successfully by navigating to the "Issues" page- there should be a new issue with the "Symbols Missing" label:

Clicking into the Issue will show the trace and a message that the symbol file is missing. It can be uploaded by clicking the button highlighted below:

After this step, you will see the processed trace in the list of issues!

note

The Issue created when the symbol file is missing can now be set to resolved. It's good practice to always upload a symbol file before devices send any data.

Symbol files can also be uploaded from the Software → Symbol Files tab (follow this deep link to be brought to the symbol file upload point in the UI):

In addition to supporting decoding trace/coredump data, symbol files are also required for decoding Metrics.

tip

You can programmatically upload symbol files with the Memfault CLI tool.

Symbol files should be uploaded to Memfault before devices send any data, to ensure all device data can be decoded.

Publish data to the Memfault cloud

Server-side rate limiting will apply to the device you're using to work on the integration process. Once you can see the device on the Memfault Web App, consider enabling Server-Side Developer Mode for it on the Memfault Web App to temporarily bypass these limits.

HTTPS

The integration will automatically push data to Memfault over HTTPS when an LTE connection becomes available prior to connecting to AWS IoT allowing one to immediately get information about unexpected resets as well as debug any connectivity issues with AWS IoT.

MQTT

After a connection with AWS IoT is established, Memfault data is published to the following topic.

prod/<board>/<device_id>/memfault/<memfault_project_key>/chunk

Forwarding data to Memfault from AWS IoT is very easy! You just need to configure an AWS IoT Rule for the topic and connect it to an AWS Lambda function. This can either be done manually in the AWS console or programmatically using CloudFormation.

caution

These steps assume you have installed the AWS CLI and have set up credentials to access your AWS instance.

  1. Copy and paste the contents below into cfnTemplate_memfault.yml
  2. Deploy the configuration
$ aws cloudformation deploy --template cfnTemplate_memfault.yml --stack-name memfault-pinnacle-100-chunk-proxy --capabilities CAPABILITY_NAMED_IAM

Waiting for changeset to be created..
Waiting for stack create/update to complete

cfnTemplate_memfault.yml file contents

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Memfault Pinnacle 100 Chunk Proxy
Parameters:
ApplicationName:
Type: String
Default: memfault-chunk-proxy

Resources:
LambdaIotRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${ApplicationName}-lambda-iot-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: !Sub /${ApplicationName}/
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSIoTFullAccess
Policies:
- PolicyName: LambdaIotPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- arn:aws:logs:*:*:log-group:/aws/lambda/*

#
# Proxy Memfault "chunks" to Memfault cloud for processing
# For more details about data transport see https://mflt.io/data-to-cloud
#
MemfaultProxyFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: "MemfaultPinnacle100ProxyFunction"
Code:
ZipFile: |
const https = require('https')
exports.https = https

exports.handler = (event, context, callback) => {
const data = Buffer.from(event.data, 'base64');
// Topic Format: prod/<board>/<device_id>/memfault/<memfault_project_key>/chunk
const topicParams = event.topic.split('/')
const deviceSerial = topicParams[2]

const options = {
hostname: 'chunks.memfault.com',
port: 443,
path: `/api/v0/chunks/${deviceSerial}`,
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',
'Content-Length': data.length,
'Memfault-Project-Key': topicParams[4]
}
}

const req = https.request(options, res => {
const response = {
statusCode: res.statusCode
};
callback(null, response);
})

req.on('error', error => {
console.error(error)
})

req.write(data)
req.end()
}
Handler: "index.handler"
Role: !GetAtt LambdaIotRole.Arn
Runtime: nodejs12.x
Timeout: 15
Environment:
Variables:
ApplicationName: !Ref ApplicationName

MemfaultProxyFunctionLogGroup:
Type: AWS::Logs::LogGroup
DependsOn: MemfaultProxyFunction
Properties:
RetentionInDays: 14
LogGroupName: !Join ["", ["/aws/lambda/", !Ref MemfaultProxyFunction]]

MemfaultProxyFunctionPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt
- MemfaultProxyFunction
- Arn
Principal: iot.amazonaws.com
SourceArn: !Sub arn:aws:iot:${AWS::Region}:${AWS::AccountId}:rule/${MemfaultProxyRule}

MemfaultProxyRule:
Type: AWS::IoT::TopicRule
Properties:
RuleName: "MemfaultPinnacle100ProxyRule"
TopicRulePayload:
AwsIotSqlVersion: "2016-03-23"
RuleDisabled: false
Sql:
!Sub SELECT encode(*, 'base64') AS data, topic() AS topic FROM
'prod/+/+/memfault/#'
Actions:
- Lambda:
FunctionArn: !GetAtt MemfaultProxyFunction.Arn

Troubleshooting

If you encounter any issues in your data transfer implementation, Memfault has tools to help debug!

  • To troubleshoot data not getting uploaded or processed correctly by the Memfault cloud, take a look at the Integration Hub → Processing Log view. This provides a filterable, chronological view of recent errors that have occurred while processing received data. See this documentation page for more information on the Integration Hub.
  • A view you can use toview the raw "Chunk" data payloadsthat have arrived for your project.
  • Precanned Data Payloads you can pass through your `user_transport_send_chunk_data()` implementation to test data transfer in isolation.
  • Server-side rate limiting will apply to the device you're using to work on the integration process. Once you can see the device on the Memfault Web App, consider enabling Server-Side Developer Mode for it on the Memfault Web App to temporarily bypass these limits.

ARM Cortex-M Integration Guide

· One min read

The following guide will walk you through step-by-step how to integrate and test the Memfault SDK for a Cortex-M device using the GNU GCC, Clang, IAR, ARM MDK, or TI ARM Compiler.

Adding the Memfault Firmware SDK to your device will provide rich diagnostics, including:

Example coredump collected with Memfault
Device metrics collected with Memfault
Reboot reasons collected with Memfault
Trace Events collected with Memfault

(Optionally) Collect a coredump capture using GDB

If you use GDB, a full coredump can be captured by just using the debugger! This can be useful during development to take advantage of Memfault's analyzers when a device crashes or hangs.

To perform the capture, navigate to http://app.memfault.com/ and select the "Issues" page in the Memfault UI, click on the "Manual Upload" button, and click on "walk-through on how to upload your first coredump". From there you can follow the guided steps to perform a capture. At the end you will see an analysis of your system state.

1. Create a Project

2. Set up the SDK

Prepare folder for memfault-firmware-sdk & port

The memfault-firmware-sdk is a self-contained C SDK you will need to include into your project.

Create a folder to hold memfault-firmware-sdk as well as the configuration and porting files to your platform.

cd ${YOUR_PROJECT_DIR}
mkdir -p third_party/memfault

cd third_party/memfault

# Add memfault repo as submodule, subtree, or copy in as source directly
git submodule add https://github.com/memfault/memfault-firmware-sdk.git memfault-firmware-sdk

cp memfault-firmware-sdk/ports/templates/* .

When you are done, you should have the following directory structure in your project:

memfault/
//...
├── memfault-firmware-sdk (submodule)
| # Files where port / glue layer to your platform will be implemented
├── memfault_platform_port.c
|
| # Configuration Headers
├── memfault_metrics_heartbeat_config.def
└── memfault_trace_reason_user_config.def
└── memfault_platform_log_config.h
└── memfault_platform_config.h

Add Sources to Build System

Based on the build system you are using, expand the appropriate tab below and follow the steps to add the sources to your target

MEMFAULT_PORT_ROOT := <YOUR_PROJECT_ROOT>/third_party/memfault
MEMFAULT_SDK_ROOT := $(MEMFAULT_PORT_ROOT)/memfault-firmware-sdk

MEMFAULT_COMPONENTS := core util panics metrics
include $(MEMFAULT_SDK_ROOT)/makefiles/MemfaultWorker.mk

<YOUR_SRC_FILES> += \
$(MEMFAULT_COMPONENTS_SRCS) \
$(MEMFAULT_PORT_ROOT)/memfault_platform_port.c

<YOUR_INCLUDE_PATHS> += \
$(MEMFAULT_COMPONENTS_INC_FOLDERS) \
$(MEMFAULT_SDK_ROOT)/ports/include \
$(MEMFAULT_PORT_ROOT)
info

Be sure to update YOUR_SRC_FILES, YOUR_INCLUDE_PATHS, and YOUR_PROJECT_ROOT accordingly for your system above!

Core Dependencies

Open the memfault_platform_port.c copied into your third_party/memfault folder and fill out the stub implementations accordingly.

Initialize Memfault Subsystem On Bootup

From your main routine, add a call to memfault_platform_boot() prior to the startup of an RTOS or baremetal while loop.

#include "memfault/components.h"

// ...

int main(void) {
// ...
memfault_platform_boot();
// ...
}

Reboot Tracking Dependencies

Memfault has a module for tracking and reporting what resets are taking place on your platform.

Implement the following function for your MCU. The ports listed in other tabs serve as a good reference.

//! @file memfault_platform_port.c

void memfault_reboot_reason_get(sResetBootupInfo *info) {
const uint32_t reset_cause = 0; // TODO: Populate with MCU reset reason
eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown;

// TODO: Convert MCU specific reboot reason to memfault enum

*info = (sResetBootupInfo) {
.reset_reason_reg = reset_cause,
.reset_reason = reset_reason,
};
}

Allocate noinit region & Collect Reboot Info

Add the following to your memfault_platform_port.c

//! @file memfault_platform_port.c

MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_tracking")
static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE];

void memfault_platform_reboot_tracking_boot(void) {
sResetBootupInfo reset_info = { 0 };
memfault_reboot_reason_get(&reset_info);
memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info);
}
note

It's expected that s_reboot_tracking is placed in "noinit" RAM. That is, a region of RAM not initialized on bootup or used by your bootloaders. This can be achieved by adding a "noinit" section to your linker script.

GNU GCC Example

Add the following to your .ld file:

 .noinit (NOLOAD): { KEEP(*(*.noinit.mflt*)) } >RAM
ARM MDK Example

Add the following to your .sct file:

; Within previously defined Load Region, modify NOINIT_RAM address and length as necessary
NOINIT_RAM 0x20010000 UNINIT 0x00000200 { ;no init section
*(.noinit.mflt*)
}
IAR Example

Add the following to your .icf file:

do not initialize
{
section .noinit,
section .stack,
section .heap,
/* Add line to a do not initialize directive */
rw section .noinit.mflt*,
};

Add a Unique Identifier to target

Memfault has facilities for capturing a unique identifier automatically as part of the build process. This information is used by Memfault's cloud infrastructure to ensure the symbol file uploaded matches the data reported in a coredump.

note

A longer discussion about firmware versioning best practices and build identifiers can be found here.

Memfault has two ways to add a Build ID to a target. Select the appropriate one below based on your toolchain setup.

note

Requires use of the GNU GCC compiler.

  1. Add -Wl,--build-id to flags passed to linker via GCC
  2. Add #define MEMFAULT_USE_GNU_BUILD_ID 1 to third_party/memfault/memfault_platform_config.h
  3. Add the following snippet to your projects linker script (.ld file) where "<YOUR_FLASH_SECTION>" below will match the name of the MEMORY section that read-only data and text is placed in.
    .note.gnu.build-id :
{
__start_gnu_build_id_start = .;
KEEP(*(.note.gnu.build-id))
} > <YOUR_FLASH_SECTION>
info

Be sure to update <YOUR_FLASH_SECTION> to match the name of the section .text is placed in!

The .note.gnu.build-id output section name is used to locate the build id when the symbol file is uploaded. The __start_gnu_build_id_start identifier is used at compile time by the SDK for populating the build id.

See the FreeRTOS example app in the Memfault SDK for a reference implementation!

At this point, your project should compile and be able to boot. On startup you should see some messaging like the following print when memfault_platform_boot() is called:

[I] Memfault Build ID: cefaeb9407ab0dac7a5efe9fd510618ee21e9df7
[I] S/N: MFLT0123
[I] SW type: app-fw
[I] SW version: 1.0.0+cefaeb940
[I] HW version: dvt1
[I] Reset Reason, RESETREAS=0x4
[I] Reset Causes:
[I] Software
[I] Memfault Initialized!

3. "Hello Memfault" Manually Upload Reboot Event

Add Test Commands to CLI

The Memfault SDK functionality can be easily exercised via CLI test commands.

note

If your platform does not have a CLI, these commands can also be wired up to a button press or called from main() on bootup from a test image

Test platform ports

#include "memfault/components.h"

int test_logging(int argc, char *argv[]) {
MEMFAULT_LOG_DEBUG("Debug log!");
MEMFAULT_LOG_INFO("Info log!");
MEMFAULT_LOG_WARN("Warning log!");
MEMFAULT_LOG_ERROR("Error log!");
return 0;
}
#include "memfault/components.h"

// Runs a sanity test to confirm coredump port is working as expected
int test_coredump_storage(int argc, char *argv[]) {

// Note: Coredump saving runs from an ISR prior to reboot so should
// be safe to call with interrupts disabled.
your_platform_disable_interrupts();
memfault_coredump_storage_debug_test_begin();
your_platform_enable_interrupts();

memfault_coredump_storage_debug_test_finish();
return 0;
}

Test core SDK functionality

#include "memfault/components.h"

// Triggers an immediate heartbeat capture (instead of waiting for timer
// to expire)
int test_heartbeat(int argc, char *argv[]) {
memfault_metrics_heartbeat_debug_trigger();
return 0;
}

int test_trace(int argc, char *argv[]) {
MEMFAULT_TRACE_EVENT_WITH_LOG(critical_error, "A test error trace!");
return 0;
}

//! Trigger a user initiated reboot and confirm reason is persisted
int test_reboot(int argc, char *argv[]) {
memfault_reboot_tracking_mark_reset_imminent(kMfltRebootReason_UserReset, NULL);
memfault_platform_reboot();
}

Test different crash types where a coredump should be captured

#include "memfault/components.h"

int test_assert(int argc, char *argv[]) {
MEMFAULT_ASSERT(0);
return -1; // should never get here
}

int test_fault(void) {
void (*bad_func)(void) = (void *)0xEEEEDEAD;
bad_func();
return -1; // should never get here
}

int test_hang(int argc, char *argv[]) {
while (1) {}
return -1; // should never get here
}

// Dump Memfault data collected to console
int test_export(int argc, char *argv[]) {
memfault_data_export_dump_chunks();
return 0;
}

Post Data to Cloud via Local Debug Setup

Prior to having an end-to-end transport in place, Memfault data can be exported over any serial connection or via GDB directly.

Post Chunks To Memfault

All data collected by Memfault can be exported as opaque packets called "chunks". To trigger an export, call the test_export command added to your device CLI.

If data has been collected, you will see strings with the format: MC:BASE64_ENCODED_CHUNK: in the dump.

note

It's perfectly fine for other logs to be interleaved with the exported data.

For example:

shell> reboot
[00:00:01] INFO: System Booting!
[00:00:02] INFO: Memfault Build ID: d8d6a047282f025fffa29fa767100f310bc40f80
shell> trace
# CLI command making a call to `memfault_data_export_dump_chunks`
shell> export
[00:00:10] INFO: MC:SFQCpwIBAwEHalRFU1RTRVJJQUwKbXRlc3Qtc29mdHdhcmUJajEuMC4wLXRlcw==:

Copy the logs from the console, navigate to the "Integration Hub / Chunks Debug" view for your project, and paste the logs:

Select "Next" to review the chunks that were identified and (optionally) update the "Device Serial" being used to report data:

Select "Post" to submit the chunks to Memfault. The chunks will appear in the view directly below. Check and make sure there are no "Errors" to address.

(Optional) Automate Chunk Posting

Posting chunks from a local setup can be automated using our CLI tool or GDB script.

note

This strategy can also be used even when an end-to-end transport is in place for local QA testing or in CI/CD test automation setups.

Automated Posting Options
Prerequisite: Project Key from Memfault UI

A Project key will be needed in order to communicate with Memfault's web services. Go to https://app.memfault.com/, navigate to the project you want to use and select 'Settings'→'General'. The 'Project Key' listed is what you will want to use.

With Desktop CLI Tool

Install the Python Memfault CLI Tool

The Memfault Desktop CLI tool. tool is written in Python and published publicly in the Python Package Index (pypi).

To install it, make sure you have a recent version of Python 3.x installed and run:

$ pip3 install memfault-cli
$ memfault --help

Save device logs to file

Start your console with your favorite terminal client. Let the system run for a bit, periodically dumping data to the console by calling memfault_data_export_dump_chunks().

You should see strings with the format: MC:BASE64_ENCODED_CHUNK: in the dump.

note

It's perfectly fine for other logs to be interleaved with the exported data.

For example:

# CLI command recording a reboot reason, then rebooting the system
shell> test_reboot
MFLT:[INFO] GNU Build ID: 3c92e3313fe1c9d00ac8687ce966d5f527a05ed3
MFLT:[INFO] S/N: freertos-example
MFLT:[INFO] SW type: qemu-app
MFLT:[INFO] SW version: 1.0.0
MFLT:[INFO] HW version: qemu-mps2-an385
MFLT:[INFO] Memfault Initialized!
# CLI command capturing a trace event
shell> test_trace
# CLI command making a call to `memfault_data_export_dump_chunks()`
shell> export
[00:00:10] INFO: MC:SFQCpwIBAwEHalRFU1RTRVJJQUwKbXRlc3Qtc29mdHdhcmUJajEuMC4wLXRlcw==:

Save the resulting logs to a file such as logs.txt.

Post chunks from logs with Memfault CLI

Run the desktop Memfault CLI tool on your saved log file. The utility will parse the logs, extract the Memfault data, and post it for processing!

$ memfault --project-key ${YOUR_PROJECT_KEY} post-chunk --encoding sdk_data_export logs.txt
Found 2 Chunks. Sending Data ...
Success
With GDB

Prerequisites

  • You need to have a way to debug your product with GDB as part of your development setup
  • The GDB version you are using needs to have the GDB Python API enabled. (It's generally the default or there is a -py version of GDB which has it included.)
  • You need to compile your firmware with debug symbol information (i.e -g CFLAG)
note

Even if you are using other compilers such as ARMCC or IAR, you can load the ELF (.out, .elf) generated and debug it in GDB as well.

Launch GDB and connect debugger

For example:

$ arm-none-eabi-gdb-py my_app.elf --ex="target remote :3333"
(gdb)

Load Memfault GDB script

Copy and paste and run the following in gdb:

python exec('try:\n from urllib2 import urlopen\nexcept:\n from urllib.request import urlopen'); exec(urlopen('https://app.memfault.com/static/scripts/memfault_gdb.py').read())
tip

Some GDB versions do not support Python scripting by default. If you see a message like "Python scripting is not supported in this copy of GDB", you might be able to run an alternative GDB binary with a -py suffix, for example arm-none-eabi-gdb-py.

Register Handler

Copy the command below and paste it into GDB and hit enter. This will load a handler which automatically posts data to Memfault whenever memfault_data_export_dump_chunks is called.

memfault install_chunk_handler --verbose --project-key <YOUR_PROJECT_KEY>
note

memfault_data_export_dump_chunks() needs to be called by your port as part of a CLI command or periodic task in order for the data to be extracted. For example:

#include "memfault/components.h"

int export_data_cli_command(int argc, char *argv[]) {
memfault_data_export_dump_chunks();
return 0;
}

void some_periodic_task(void) {
memfault_data_export_dump_chunks();
}

Try It Out!

Now, every time memfault_data_export_dump_chunks is called, the data passed as a parameter to the function will automatically be posted to the Memfault cloud. You don't have to do anything else! You will see logs print in the gdb CLI as data is posted:

(gdb) Continuing.
Successfully posted 45 bytes of chunk data
Successfully posted 53 bytes of chunk data
[...]

4. Manually Capture a Coredump

Upload Symbol File

At this point, you should be able to generate test events and crashes and push the data to the Memfault UI.

You can confirm the error traces and crashes have arrived successfully by navigating to the "Issues" page- there should be a new issue with the "Symbols Missing" label:

Clicking into the Issue will show the trace and a message that the symbol file is missing. It can be uploaded by clicking the button highlighted below:

After this step, you will see the processed trace in the list of issues!

note

The Issue created when the symbol file is missing can now be set to resolved. It's good practice to always upload a symbol file before devices send any data.

Symbol files can also be uploaded from the Software → Symbol Files tab (follow this deep link to be brought to the symbol file upload point in the UI):

In addition to supporting decoding trace/coredump data, symbol files are also required for decoding Metrics.

tip

You can programmatically upload symbol files with the Memfault CLI tool.

Symbol files should be uploaded to Memfault before devices send any data, to ensure all device data can be decoded.

Check Exception Handler Implementations

Memfault provides exception handler implementations that need to be invoked via the system's vector table. The SDK by default provides implementations for these handlers using the CMSIS standard names:

  • HardFault_Handler
  • MemoryManagement_Handler
  • BusFault_Handler
  • UsageFault_Handler
  • NMI_Handler
  • MemfaultWatchdog_Handler

Typically the default implementations will be weakly defined, so the Memfault implementations will automatically be used. It may be necessary to mark the default implementation with the appropriate weak annotation, or delete the default implementations altogether so the Memfault implementations can be used.

If the entries in the vector table for those handlers are named differently, either change the names to match the CMSIS standard ones, or set the necessary config options in memfault_platform_config.h to override the names.

Implement Coredump Storage Dependency

When the system faults or asserts crash information can be recorded and sent to Memfault after the device reboots. In order for this information to be saved, it must be persisted to a storage area that persists across a device reset.

Implement Coredump Storage Area

Coredumps can be saved in a .noinit region of SRAM, internal flash, external flash, or even streamed out over a peripheral to another MCU when a coredump takes place.

Custom Port Template

//! @file memfault_platform_port.c

void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) {
*info = (sMfltCoredumpStorageInfo) {
.size = /* size of coredump storage area */,
};
}

bool memfault_platform_coredump_storage_read(uint32_t offset, void *data,
size_t read_len) {
}

bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) {
}

bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data,
size_t data_len) {
}

void memfault_platform_coredump_storage_clear(void) {
}

RTOS Port Files

The Memfault SDK includes pre-canned integrations for RTOS environments. Expand the tab for the one applies to your system below in order to pick them up.

FreeRTOS Specific Sources

Add FreeRTOS Specific Sources To Compilation

  ${MEMFAULT_SDK_ROOT}/ports/freertos/src/memfault_core_freertos.c
${MEMFAULT_SDK_ROOT}/ports/freertos/src/memfault_freertos_ram_regions.c
${MEMFAULT_SDK_ROOT}/ports/freertos/src/memfault_panics_freertos.c

Register Memfault Task Tracing in FreeRTOSConfig.h

//! @file FreeRTOSConfig.h
#pragma once

#include "memfault/ports/freertos_trace.h"
tip

While in the file, we recommend using the following settings to catch issues:

#define configCHECK_FOR_STACK_OVERFLOW 2
void vAssertCalled(const char *file, int line);
#define configASSERT(x) if ((x) == 0) vAssertCalled( __FILE__, __LINE__ )

Initialize FreeRTOS Port on Boot

#include "memfault/components.h"
+ #include "memfault/ports/freertos.h"

//...

int memfault_platform_boot(void) {
+ memfault_freertos_port_boot();
+
memfault_build_info_dump();

Optional: Configure FreeRTOS Task Capture Size

If the platform is configured to not capture all of RAM (due to storage or bandwidth limitations), it's still possible to capture the active FreeRTOS tasks as part of a coredump. See this file for details on that implementation.

With that in place, the Memfault SDK will selectively capture the running tasks during a coredump by saving both the Task Control Block (TCB) structure as well as a limited backtrace for each task.

By default, as of 0.39.0, the Memfault SDK will capture a truncated copy of each TCB, which saves space in the coredump. See below for more details on this.

Details

TCB structure capture size Under some configurations, the TCB structures consume significant amounts of coredump space (300-1000 bytes per TCB). To mitigate this problem, as of the Memfault Firmware SDK v0.37.1, a configuration option MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE is available for fine-tuning the captured FreeRTOS TCB size. This can usually be set to 100 bytes, but please confirm for your system by examining the full size of the TCB_t data structure; for example, using the pahole tool as shown below. Memfault requires the struct members through pcTaskName to decode the task.

# note- the 'tskTaskControlBlock' is the actual type name for the 'TCB_t' struct
❯ pahole -C 'tskTaskControlBlock' ./build/memfault-esp32-demo-app.elf.memfault_log_fmt

The unused xNewLib_reent member below is 240 bytes out of the total 352 byte struct size.

struct tskTaskControlBlock {
volatile StackType_t * pxTopOfStack; /* 0 4 */
xMPU_SETTINGS xMPUSettings; /* 4 4 */
ListItem_t xStateListItem; /* 8 20 */
ListItem_t xEventListItem; /* 28 20 */
UBaseType_t uxPriority; /* 48 4 */
StackType_t * pxStack; /* 52 4 */
char pcTaskName[16]; /* 56 16 */
/* --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- */
BaseType_t xCoreID; /* 72 4 */
StackType_t * pxEndOfStack; /* 76 4 */
UBaseType_t uxTCBNumber; /* 80 4 */
UBaseType_t uxTaskNumber; /* 84 4 */
UBaseType_t uxBasePriority; /* 88 4 */
UBaseType_t uxMutexesHeld; /* 92 4 */
void * pvThreadLocalStoragePointers[1]; /* 96 4 */
TlsDeleteCallbackFunction_t pvThreadLocalStoragePointersDelCallback[1]; /* 100 4 */
struct _reent xNewLib_reent; /* 104 240 */
/* --- cacheline 5 boundary (320 bytes) was 24 bytes ago --- */
volatile volatile uint32_t ulNotifiedValue; /* 344 4 */
volatile volatile uint8_t ucNotifyState; /* 348 1 */
uint8_t ucStaticallyAllocated; /* 349 1 */
uint8_t ucDelayAborted; /* 350 1 */

/* size: 352, cachelines: 6, members: 20 */
/* padding: 1 */
/* last cacheline: 32 bytes */
};

If the default capture size is too small, it can be adjusted using the MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE config value in memfault_platform_config.h.

Register Assert Handler

Memfault Firmware SDK v1.4.1 automatically hooks into the C library assert handlers:

  • __assert_func (Newlib C library)
  • __aeabi_assert (IAR and ARM C libraries)

If you are using a different C library, you will need to hook into the assert handler, see the tab for "Memfault SDK <1.4.1" for more details.

tip

Memfault advises against calling assert() directly from a project. Instead, we suggest using the MEMFAULT_ASSERT(x) macro provided by the Memfault SDK. Benefits include:

  • making it easier to swap out behavior for different environments such as compile code for a unit test versus a real target.
  • leading to substantial code space savings by enabling the removal of assert conditions passed via the failedexpr arg.

5. Manually Capture Logs

Logging Dependency

The Memfault SDK will (sparingly) emit diagnostic logs to alert of integration configuration problems. The logging subsystem can also easily serve as logging infrastructure for your own platform if you do not yet have one set up.

Based on your setup, choose one of the options below:

Platform does logging via Macros
  1. Add #define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 1 to third_party/memfault/memfault_platform_config.h
  2. Remap Memfault logging macros to platform logging macros by adding the following macros to the memfault_platform_log_config.h file created earlier.
//! @file memfault_platform_log_config.h
#pragma once

#define MEMFAULT_LOG_DEBUG(fmt, ...) YOUR_PLATFORM_DEBUG_LOG( fmt, ## __VA_ARGS__)
#define MEMFAULT_LOG_INFO(fmt, ...) YOUR_PLATFORM_INFO_LOG( fmt, ## __VA_ARGS__)
#define MEMFAULT_LOG_WARN(fmt, ...) YOUR_PLATFORM_WARN_LOG( fmt, ## __VA_ARGS__)
#define MEMFAULT_LOG_ERROR(fmt, ...) YOUR_PLATFORM_ERROR_LOG( fmt, ## __VA_ARGS__)
Platform has no logging or uses printf()

Implement memfault_platform_log() in third_party/memfault/memfault_platform_port.c. For example,

#include "memfault/components.h"

void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) {
va_list args;
va_start(args, fmt);

char log_buf[128];
vsnprintf(log_buf, sizeof(log_buf), fmt, args);

const char *lvl_str;
switch (level) {
case kMemfaultPlatformLogLevel_Debug:
lvl_str = "D";
break;

case kMemfaultPlatformLogLevel_Info:
lvl_str = "I";
break;

case kMemfaultPlatformLogLevel_Warning:
lvl_str = "W";
break;

case kMemfaultPlatformLogLevel_Error:
lvl_str = "E";
break;

default:
break;
}

vsnprintf(log_buf, sizeof(log_buf), fmt, args);

your_platform_printf("[%s] MFLT: %s\n", lvl_str, log_buf);
}
note

If you are using the Memfault Demo CLI in your project, see the information on that page for other dependencies.

6. Configuring Standard Metrics

note

To process device data, Memfault requires a matching uploaded Symbol File. See "Upload Symbol File for details.

Timer Dependencies

The memfault metric subsystem requires a repeating timer in order to collect "heartbeats" and a time since boot

Implement the following function for your MCU. The ports listed in other tabs serve as a good reference.

//! @file memfault_platform_port.c

bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MemfaultPlatformTimerCallback callback) {
// Schedule a timer to invoke callback() repeatedly after period_sec
return true;
}

uint64_t memfault_platform_get_time_since_boot_ms(void) {
// Return time since boot in ms, this is used for relative timings.
return 0;
}

Configure Custom Metrics

note

To process device data, Memfault requires a matching uploaded Symbol File. See "Upload Symbol File for details.

Define First Heartbeat

Heartbeat metrics allow you to easily monitor your platform and confirm it is operating as expected.

Typical Heartbeat Examples
  • investigate problems that didn't cause a reboot (bad connectivity, network or sensor error rates) and marginality that crops up in a fleet
  • providing leading indicators of problems (rapid battery drain, drop in activity, etc)
  • compare trends across releases (improved connectivity, data efficiency etc)

Best Practices around each metric type that we recommend:

  • Timers - These are used to track time spent in particular states and can be used for debugging connectivity, battery life, and performance issues.
  • Counters - Track counts of a particular event class taking place. Suggested use cases are:
    • Operations your system are performing (number of events serviced by a task, bytes sent over network, bytes written to flash). Alerts can be configured to identify devices operating outside normal thresholds.
    • Counts of events for events that should never happen (i2c bus write error count, flash write error). You can alert if any of these situations are seen.
  • Gauges - These are values sampled at the end of each heartbeat interval. Common items include
    • Battery Metrics (Drop over an hour, current percent)
    • Heap utilization / stack high watermark

Add Metric to memfault_metrics_heartbeat_config.def

//! @file memfault_metrics_heartbeat_config.def
MEMFAULT_METRICS_KEY_DEFINE(MainTaskWakeups, kMemfaultMetricType_Unsigned)

Instrument Code to Update Heartbeat

void my_main_task(void) {
while (1) {
your_rtos_wait_for_event();
MEMFAULT_METRIC_ADD(MainTaskWakeups, 1);
}
}

7. Automatically Upload Data

Post Data to Cloud via Device Transport

Extensive details about how data from the Memfault SDK makes it to the cloud can be found here. In short, the Memfault SDK packetizes data into "chunks" that must be pushed to the Memfault cloud via the chunk REST endpoint.

The Memfault SDK exposes an API that packetizes Memfault data into "chunks". These "chunks" need to be forwarded to the Memfault Cloud (either directly via HTTPs or indirectly via a gateway device such as a computer or mobile application)

tip

If you cannot post data directly using HTTP, no problem at all. In fact, that is one of the most common configurations. In these setups, data can be proxied from the MCU to a device which can perform a HTTP request over transports such as UART, BLE, COAP, LoRa, Zigbee, etc). The size of a "chunk" is fully configurable and can be tuned to best match your transport requirements. All message re-assembly is dealt with by Memfault in the cloud.

#include "memfault/components.h"

bool try_send_memfault_data(void) {
// buffer to copy chunk data into
uint8_t buf[USER_CHUNK_SIZE];
size_t buf_len = sizeof(buf);

bool data_available = memfault_packetizer_get_chunk(buf, &buf_len);
if (!data_available ) {
return false; // no more data to send
}

// send payload collected to chunks endpoint
user_transport_send_chunk_data(buf, buf_len);
return true;
}

void send_memfault_data(void) {
if (!memfault_packetizer_data_available()) {
return false; // no new data to send
}

// [... user specific logic deciding when & how much data to send
while (try_send_memfault_data()) { }
}

Troubleshooting Data Transfer

See the docs on data transfer troubleshooting.

8. Next Steps

Now that the Memfault SDK is integrated on your device, there are many other features and options to explore. Check out these sections of our docs for more information:

Embedded Integration Guide

· One min read

The following guide will walk you through step-by-step how to integrate and test the Memfault SDK for a Cortex-M device using the GNU GCC, Clang, IAR, ARM MDK, or TI ARM Compiler.

Adding the Memfault Firmware SDK to your device will provide rich diagnostics, including:

Example coredump collected with Memfault
Device metrics collected with Memfault
Reboot reasons collected with Memfault
Trace Events collected with Memfault

(Optionally) Collect a coredump capture using GDB

If you use GDB, a full coredump can be captured by just using the debugger! This can be useful during development to take advantage of Memfault's analyzers when a device crashes or hangs.

To perform the capture, navigate to http://app.memfault.com/ and select the "Issues" page in the Memfault UI, click on the "Manual Upload" button, and click on "walk-through on how to upload your first coredump". From there you can follow the guided steps to perform a capture. At the end you will see an analysis of your system state.

1. Create a Project

2. Set up the SDK

Prepare folder for memfault-firmware-sdk & port

The memfault-firmware-sdk is a self-contained C SDK you will need to include into your project.

Create a folder to hold memfault-firmware-sdk as well as the configuration and porting files to your platform.

cd ${YOUR_PROJECT_DIR}
mkdir -p third_party/memfault

cd third_party/memfault

# Add memfault repo as submodule, subtree, or copy in as source directly
git submodule add https://github.com/memfault/memfault-firmware-sdk.git memfault-firmware-sdk

cp memfault-firmware-sdk/ports/templates/* .

When you are done, you should have the following directory structure in your project:

memfault/
//...
├── memfault-firmware-sdk (submodule)
| # Files where port / glue layer to your platform will be implemented
├── memfault_platform_port.c
|
| # Configuration Headers
├── memfault_metrics_heartbeat_config.def
└── memfault_trace_reason_user_config.def
└── memfault_platform_log_config.h
└── memfault_platform_config.h

Add Sources to Build System

Based on the build system you are using, expand the appropriate tab below and follow the steps to add the sources to your target

MEMFAULT_PORT_ROOT := <YOUR_PROJECT_ROOT>/third_party/memfault
MEMFAULT_SDK_ROOT := $(MEMFAULT_PORT_ROOT)/memfault-firmware-sdk

MEMFAULT_COMPONENTS := core util panics metrics
include $(MEMFAULT_SDK_ROOT)/makefiles/MemfaultWorker.mk

<YOUR_SRC_FILES> += \
$(MEMFAULT_COMPONENTS_SRCS) \
$(MEMFAULT_PORT_ROOT)/memfault_platform_port.c

<YOUR_INCLUDE_PATHS> += \
$(MEMFAULT_COMPONENTS_INC_FOLDERS) \
$(MEMFAULT_SDK_ROOT)/ports/include \
$(MEMFAULT_PORT_ROOT)
info

Be sure to update YOUR_SRC_FILES, YOUR_INCLUDE_PATHS, and YOUR_PROJECT_ROOT accordingly for your system above!

Core Dependencies

Open the memfault_platform_port.c copied into your third_party/memfault folder and fill out the stub implementations accordingly.

Initialize Memfault Subsystem On Bootup

From your main routine, add a call to memfault_platform_boot() prior to the startup of an RTOS or baremetal while loop.

#include "memfault/components.h"

// ...

int main(void) {
// ...
memfault_platform_boot();
// ...
}

Reboot Tracking Dependencies

Memfault has a module for tracking and reporting what resets are taking place on your platform.

Implement the following function for your MCU. The ports listed in other tabs serve as a good reference.

//! @file memfault_platform_port.c

void memfault_reboot_reason_get(sResetBootupInfo *info) {
const uint32_t reset_cause = 0; // TODO: Populate with MCU reset reason
eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown;

// TODO: Convert MCU specific reboot reason to memfault enum

*info = (sResetBootupInfo) {
.reset_reason_reg = reset_cause,
.reset_reason = reset_reason,
};
}

Allocate noinit region & Collect Reboot Info

Add the following to your memfault_platform_port.c

//! @file memfault_platform_port.c

MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_tracking")
static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE];

void memfault_platform_reboot_tracking_boot(void) {
sResetBootupInfo reset_info = { 0 };
memfault_reboot_reason_get(&reset_info);
memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info);
}
note

It's expected that s_reboot_tracking is placed in "noinit" RAM. That is, a region of RAM not initialized on bootup or used by your bootloaders. This can be achieved by adding a "noinit" section to your linker script.

GNU GCC Example

Add the following to your .ld file:

 .noinit (NOLOAD): { KEEP(*(*.noinit.mflt*)) } >RAM
ARM MDK Example

Add the following to your .sct file:

; Within previously defined Load Region, modify NOINIT_RAM address and length as necessary
NOINIT_RAM 0x20010000 UNINIT 0x00000200 { ;no init section
*(.noinit.mflt*)
}
IAR Example

Add the following to your .icf file:

do not initialize
{
section .noinit,
section .stack,
section .heap,
/* Add line to a do not initialize directive */
rw section .noinit.mflt*,
};

Add a Unique Identifier to target

Memfault has facilities for capturing a unique identifier automatically as part of the build process. This information is used by Memfault's cloud infrastructure to ensure the symbol file uploaded matches the data reported in a coredump.

note

A longer discussion about firmware versioning best practices and build identifiers can be found here.

Memfault has two ways to add a Build ID to a target. Select the appropriate one below based on your toolchain setup.

note

Requires use of the GNU GCC compiler.

  1. Add -Wl,--build-id to flags passed to linker via GCC
  2. Add #define MEMFAULT_USE_GNU_BUILD_ID 1 to third_party/memfault/memfault_platform_config.h
  3. Add the following snippet to your projects linker script (.ld file) where "<YOUR_FLASH_SECTION>" below will match the name of the MEMORY section that read-only data and text is placed in.
    .note.gnu.build-id :
{
__start_gnu_build_id_start = .;
KEEP(*(.note.gnu.build-id))
} > <YOUR_FLASH_SECTION>
info

Be sure to update <YOUR_FLASH_SECTION> to match the name of the section .text is placed in!

The .note.gnu.build-id output section name is used to locate the build id when the symbol file is uploaded. The __start_gnu_build_id_start identifier is used at compile time by the SDK for populating the build id.

See the FreeRTOS example app in the Memfault SDK for a reference implementation!

At this point, your project should compile and be able to boot. On startup you should see some messaging like the following print when memfault_platform_boot() is called:

[I] Memfault Build ID: cefaeb9407ab0dac7a5efe9fd510618ee21e9df7
[I] S/N: MFLT0123
[I] SW type: app-fw
[I] SW version: 1.0.0+cefaeb940
[I] HW version: dvt1
[I] Reset Reason, RESETREAS=0x4
[I] Reset Causes:
[I] Software
[I] Memfault Initialized!

3. "Hello Memfault" Manually Upload Reboot Event

Add Test Commands to CLI

The Memfault SDK functionality can be easily exercised via CLI test commands.

note

If your platform does not have a CLI, these commands can also be wired up to a button press or called from main() on bootup from a test image

Test platform ports

#include "memfault/components.h"

int test_logging(int argc, char *argv[]) {
MEMFAULT_LOG_DEBUG("Debug log!");
MEMFAULT_LOG_INFO("Info log!");
MEMFAULT_LOG_WARN("Warning log!");
MEMFAULT_LOG_ERROR("Error log!");
return 0;
}
#include "memfault/components.h"

// Runs a sanity test to confirm coredump port is working as expected
int test_coredump_storage(int argc, char *argv[]) {

// Note: Coredump saving runs from an ISR prior to reboot so should
// be safe to call with interrupts disabled.
your_platform_disable_interrupts();
memfault_coredump_storage_debug_test_begin();
your_platform_enable_interrupts();

memfault_coredump_storage_debug_test_finish();
return 0;
}

Test core SDK functionality

#include "memfault/components.h"

// Triggers an immediate heartbeat capture (instead of waiting for timer
// to expire)
int test_heartbeat(int argc, char *argv[]) {
memfault_metrics_heartbeat_debug_trigger();
return 0;
}

int test_trace(int argc, char *argv[]) {
MEMFAULT_TRACE_EVENT_WITH_LOG(critical_error, "A test error trace!");
return 0;
}

//! Trigger a user initiated reboot and confirm reason is persisted
int test_reboot(int argc, char *argv[]) {
memfault_reboot_tracking_mark_reset_imminent(kMfltRebootReason_UserReset, NULL);
memfault_platform_reboot();
}

Test different crash types where a coredump should be captured

#include "memfault/components.h"

int test_assert(int argc, char *argv[]) {
MEMFAULT_ASSERT(0);
return -1; // should never get here
}

int test_fault(void) {
void (*bad_func)(void) = (void *)0xEEEEDEAD;
bad_func();
return -1; // should never get here
}

int test_hang(int argc, char *argv[]) {
while (1) {}
return -1; // should never get here
}

// Dump Memfault data collected to console
int test_export(int argc, char *argv[]) {
memfault_data_export_dump_chunks();
return 0;
}

Post Data to Cloud via Local Debug Setup

Prior to having an end-to-end transport in place, Memfault data can be exported over any serial connection or via GDB directly.

Post Chunks To Memfault

All data collected by Memfault can be exported as opaque packets called "chunks". To trigger an export, call the test_export command added to your device CLI.

If data has been collected, you will see strings with the format: MC:BASE64_ENCODED_CHUNK: in the dump.

note

It's perfectly fine for other logs to be interleaved with the exported data.

For example:

shell> reboot
[00:00:01] INFO: System Booting!
[00:00:02] INFO: Memfault Build ID: d8d6a047282f025fffa29fa767100f310bc40f80
shell> trace
# CLI command making a call to `memfault_data_export_dump_chunks`
shell> export
[00:00:10] INFO: MC:SFQCpwIBAwEHalRFU1RTRVJJQUwKbXRlc3Qtc29mdHdhcmUJajEuMC4wLXRlcw==:

Copy the logs from the console, navigate to the "Integration Hub / Chunks Debug" view for your project, and paste the logs:

Select "Next" to review the chunks that were identified and (optionally) update the "Device Serial" being used to report data:

Select "Post" to submit the chunks to Memfault. The chunks will appear in the view directly below. Check and make sure there are no "Errors" to address.

(Optional) Automate Chunk Posting

Posting chunks from a local setup can be automated using our CLI tool or GDB script.

note

This strategy can also be used even when an end-to-end transport is in place for local QA testing or in CI/CD test automation setups.

Automated Posting Options
Prerequisite: Project Key from Memfault UI

A Project key will be needed in order to communicate with Memfault's web services. Go to https://app.memfault.com/, navigate to the project you want to use and select 'Settings'→'General'. The 'Project Key' listed is what you will want to use.

With Desktop CLI Tool

Install the Python Memfault CLI Tool

The Memfault Desktop CLI tool. tool is written in Python and published publicly in the Python Package Index (pypi).

To install it, make sure you have a recent version of Python 3.x installed and run:

$ pip3 install memfault-cli
$ memfault --help

Save device logs to file

Start your console with your favorite terminal client. Let the system run for a bit, periodically dumping data to the console by calling memfault_data_export_dump_chunks().

You should see strings with the format: MC:BASE64_ENCODED_CHUNK: in the dump.

note

It's perfectly fine for other logs to be interleaved with the exported data.

For example:

# CLI command recording a reboot reason, then rebooting the system
shell> test_reboot
MFLT:[INFO] GNU Build ID: 3c92e3313fe1c9d00ac8687ce966d5f527a05ed3
MFLT:[INFO] S/N: freertos-example
MFLT:[INFO] SW type: qemu-app
MFLT:[INFO] SW version: 1.0.0
MFLT:[INFO] HW version: qemu-mps2-an385
MFLT:[INFO] Memfault Initialized!
# CLI command capturing a trace event
shell> test_trace
# CLI command making a call to `memfault_data_export_dump_chunks()`
shell> export
[00:00:10] INFO: MC:SFQCpwIBAwEHalRFU1RTRVJJQUwKbXRlc3Qtc29mdHdhcmUJajEuMC4wLXRlcw==:

Save the resulting logs to a file such as logs.txt.

Post chunks from logs with Memfault CLI

Run the desktop Memfault CLI tool on your saved log file. The utility will parse the logs, extract the Memfault data, and post it for processing!

$ memfault --project-key ${YOUR_PROJECT_KEY} post-chunk --encoding sdk_data_export logs.txt
Found 2 Chunks. Sending Data ...
Success
With GDB

Prerequisites

  • You need to have a way to debug your product with GDB as part of your development setup
  • The GDB version you are using needs to have the GDB Python API enabled. (It's generally the default or there is a -py version of GDB which has it included.)
  • You need to compile your firmware with debug symbol information (i.e -g CFLAG)
note

Even if you are using other compilers such as ARMCC or IAR, you can load the ELF (.out, .elf) generated and debug it in GDB as well.

Launch GDB and connect debugger

For example:

$ arm-none-eabi-gdb-py my_app.elf --ex="target remote :3333"
(gdb)

Load Memfault GDB script

Copy and paste and run the following in gdb:

python exec('try:\n from urllib2 import urlopen\nexcept:\n from urllib.request import urlopen'); exec(urlopen('https://app.memfault.com/static/scripts/memfault_gdb.py').read())
tip

Some GDB versions do not support Python scripting by default. If you see a message like "Python scripting is not supported in this copy of GDB", you might be able to run an alternative GDB binary with a -py suffix, for example arm-none-eabi-gdb-py.

Register Handler

Copy the command below and paste it into GDB and hit enter. This will load a handler which automatically posts data to Memfault whenever memfault_data_export_dump_chunks is called.

memfault install_chunk_handler --verbose --project-key <YOUR_PROJECT_KEY>
note

memfault_data_export_dump_chunks() needs to be called by your port as part of a CLI command or periodic task in order for the data to be extracted. For example:

#include "memfault/components.h"

int export_data_cli_command(int argc, char *argv[]) {
memfault_data_export_dump_chunks();
return 0;
}

void some_periodic_task(void) {
memfault_data_export_dump_chunks();
}

Try It Out!

Now, every time memfault_data_export_dump_chunks is called, the data passed as a parameter to the function will automatically be posted to the Memfault cloud. You don't have to do anything else! You will see logs print in the gdb CLI as data is posted:

(gdb) Continuing.
Successfully posted 45 bytes of chunk data
Successfully posted 53 bytes of chunk data
[...]

4. Manually Capture a Coredump

Upload Symbol File

At this point, you should be able to generate test events and crashes and push the data to the Memfault UI.

You can confirm the error traces and crashes have arrived successfully by navigating to the "Issues" page- there should be a new issue with the "Symbols Missing" label:

Clicking into the Issue will show the trace and a message that the symbol file is missing. It can be uploaded by clicking the button highlighted below:

After this step, you will see the processed trace in the list of issues!

note

The Issue created when the symbol file is missing can now be set to resolved. It's good practice to always upload a symbol file before devices send any data.

Symbol files can also be uploaded from the Software → Symbol Files tab (follow this deep link to be brought to the symbol file upload point in the UI):

In addition to supporting decoding trace/coredump data, symbol files are also required for decoding Metrics.

tip

You can programmatically upload symbol files with the Memfault CLI tool.

Symbol files should be uploaded to Memfault before devices send any data, to ensure all device data can be decoded.

Check Exception Handler Implementations

Memfault provides exception handler implementations that need to be invoked via the system's vector table. The SDK by default provides implementations for these handlers using the CMSIS standard names:

  • HardFault_Handler
  • MemoryManagement_Handler
  • BusFault_Handler
  • UsageFault_Handler
  • NMI_Handler
  • MemfaultWatchdog_Handler

Typically the default implementations will be weakly defined, so the Memfault implementations will automatically be used. It may be necessary to mark the default implementation with the appropriate weak annotation, or delete the default implementations altogether so the Memfault implementations can be used.

If the entries in the vector table for those handlers are named differently, either change the names to match the CMSIS standard ones, or set the necessary config options in memfault_platform_config.h to override the names.

Implement Coredump Storage Dependency

When the system faults or asserts crash information can be recorded and sent to Memfault after the device reboots. In order for this information to be saved, it must be persisted to a storage area that persists across a device reset.

Implement Coredump Storage Area

Coredumps can be saved in a .noinit region of SRAM, internal flash, external flash, or even streamed out over a peripheral to another MCU when a coredump takes place.

Custom Port Template

//! @file memfault_platform_port.c

void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) {
*info = (sMfltCoredumpStorageInfo) {
.size = /* size of coredump storage area */,
};
}

bool memfault_platform_coredump_storage_read(uint32_t offset, void *data,
size_t read_len) {
}

bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) {
}

bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data,
size_t data_len) {
}

void memfault_platform_coredump_storage_clear(void) {
}

RTOS Port Files

The Memfault SDK includes pre-canned integrations for RTOS environments. Expand the tab for the one applies to your system below in order to pick them up.

FreeRTOS Specific Sources

Add FreeRTOS Specific Sources To Compilation

  ${MEMFAULT_SDK_ROOT}/ports/freertos/src/memfault_core_freertos.c
${MEMFAULT_SDK_ROOT}/ports/freertos/src/memfault_freertos_ram_regions.c
${MEMFAULT_SDK_ROOT}/ports/freertos/src/memfault_panics_freertos.c

Register Memfault Task Tracing in FreeRTOSConfig.h

//! @file FreeRTOSConfig.h
#pragma once

#include "memfault/ports/freertos_trace.h"
tip

While in the file, we recommend using the following settings to catch issues:

#define configCHECK_FOR_STACK_OVERFLOW 2
void vAssertCalled(const char *file, int line);
#define configASSERT(x) if ((x) == 0) vAssertCalled( __FILE__, __LINE__ )

Initialize FreeRTOS Port on Boot

#include "memfault/components.h"
+ #include "memfault/ports/freertos.h"

//...

int memfault_platform_boot(void) {
+ memfault_freertos_port_boot();
+
memfault_build_info_dump();

Optional: Configure FreeRTOS Task Capture Size

If the platform is configured to not capture all of RAM (due to storage or bandwidth limitations), it's still possible to capture the active FreeRTOS tasks as part of a coredump. See this file for details on that implementation.

With that in place, the Memfault SDK will selectively capture the running tasks during a coredump by saving both the Task Control Block (TCB) structure as well as a limited backtrace for each task.

By default, as of 0.39.0, the Memfault SDK will capture a truncated copy of each TCB, which saves space in the coredump. See below for more details on this.

Details

TCB structure capture size Under some configurations, the TCB structures consume significant amounts of coredump space (300-1000 bytes per TCB). To mitigate this problem, as of the Memfault Firmware SDK v0.37.1, a configuration option MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE is available for fine-tuning the captured FreeRTOS TCB size. This can usually be set to 100 bytes, but please confirm for your system by examining the full size of the TCB_t data structure; for example, using the pahole tool as shown below. Memfault requires the struct members through pcTaskName to decode the task.

# note- the 'tskTaskControlBlock' is the actual type name for the 'TCB_t' struct
❯ pahole -C 'tskTaskControlBlock' ./build/memfault-esp32-demo-app.elf.memfault_log_fmt

The unused xNewLib_reent member below is 240 bytes out of the total 352 byte struct size.

struct tskTaskControlBlock {
volatile StackType_t * pxTopOfStack; /* 0 4 */
xMPU_SETTINGS xMPUSettings; /* 4 4 */
ListItem_t xStateListItem; /* 8 20 */
ListItem_t xEventListItem; /* 28 20 */
UBaseType_t uxPriority; /* 48 4 */
StackType_t * pxStack; /* 52 4 */
char pcTaskName[16]; /* 56 16 */
/* --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- */
BaseType_t xCoreID; /* 72 4 */
StackType_t * pxEndOfStack; /* 76 4 */
UBaseType_t uxTCBNumber; /* 80 4 */
UBaseType_t uxTaskNumber; /* 84 4 */
UBaseType_t uxBasePriority; /* 88 4 */
UBaseType_t uxMutexesHeld; /* 92 4 */
void * pvThreadLocalStoragePointers[1]; /* 96 4 */
TlsDeleteCallbackFunction_t pvThreadLocalStoragePointersDelCallback[1]; /* 100 4 */
struct _reent xNewLib_reent; /* 104 240 */
/* --- cacheline 5 boundary (320 bytes) was 24 bytes ago --- */
volatile volatile uint32_t ulNotifiedValue; /* 344 4 */
volatile volatile uint8_t ucNotifyState; /* 348 1 */
uint8_t ucStaticallyAllocated; /* 349 1 */
uint8_t ucDelayAborted; /* 350 1 */

/* size: 352, cachelines: 6, members: 20 */
/* padding: 1 */
/* last cacheline: 32 bytes */
};

If the default capture size is too small, it can be adjusted using the MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE config value in memfault_platform_config.h.

Register Assert Handler

Memfault Firmware SDK v1.4.1 automatically hooks into the C library assert handlers:

  • __assert_func (Newlib C library)
  • __aeabi_assert (IAR and ARM C libraries)

If you are using a different C library, you will need to hook into the assert handler, see the tab for "Memfault SDK <1.4.1" for more details.

tip

Memfault advises against calling assert() directly from a project. Instead, we suggest using the MEMFAULT_ASSERT(x) macro provided by the Memfault SDK. Benefits include:

  • making it easier to swap out behavior for different environments such as compile code for a unit test versus a real target.
  • leading to substantial code space savings by enabling the removal of assert conditions passed via the failedexpr arg.

5. Manually Capture Logs

Logging Dependency

The Memfault SDK will (sparingly) emit diagnostic logs to alert of integration configuration problems. The logging subsystem can also easily serve as logging infrastructure for your own platform if you do not yet have one set up.

Based on your setup, choose one of the options below:

Platform does logging via Macros
  1. Add #define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 1 to third_party/memfault/memfault_platform_config.h
  2. Remap Memfault logging macros to platform logging macros by adding the following macros to the memfault_platform_log_config.h file created earlier.
//! @file memfault_platform_log_config.h
#pragma once

#define MEMFAULT_LOG_DEBUG(fmt, ...) YOUR_PLATFORM_DEBUG_LOG( fmt, ## __VA_ARGS__)
#define MEMFAULT_LOG_INFO(fmt, ...) YOUR_PLATFORM_INFO_LOG( fmt, ## __VA_ARGS__)
#define MEMFAULT_LOG_WARN(fmt, ...) YOUR_PLATFORM_WARN_LOG( fmt, ## __VA_ARGS__)
#define MEMFAULT_LOG_ERROR(fmt, ...) YOUR_PLATFORM_ERROR_LOG( fmt, ## __VA_ARGS__)
Platform has no logging or uses printf()

Implement memfault_platform_log() in third_party/memfault/memfault_platform_port.c. For example,

#include "memfault/components.h"

void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) {
va_list args;
va_start(args, fmt);

char log_buf[128];
vsnprintf(log_buf, sizeof(log_buf), fmt, args);

const char *lvl_str;
switch (level) {
case kMemfaultPlatformLogLevel_Debug:
lvl_str = "D";
break;

case kMemfaultPlatformLogLevel_Info:
lvl_str = "I";
break;

case kMemfaultPlatformLogLevel_Warning:
lvl_str = "W";
break;

case kMemfaultPlatformLogLevel_Error:
lvl_str = "E";
break;

default:
break;
}

vsnprintf(log_buf, sizeof(log_buf), fmt, args);

your_platform_printf("[%s] MFLT: %s\n", lvl_str, log_buf);
}
note

If you are using the Memfault Demo CLI in your project, see the information on that page for other dependencies.

6. Configuring Standard Metrics

note

To process device data, Memfault requires a matching uploaded Symbol File. See "Upload Symbol File for details.

Timer Dependencies

The memfault metric subsystem requires a repeating timer in order to collect "heartbeats" and a time since boot

Implement the following function for your MCU. The ports listed in other tabs serve as a good reference.

//! @file memfault_platform_port.c

bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MemfaultPlatformTimerCallback callback) {
// Schedule a timer to invoke callback() repeatedly after period_sec
return true;
}

uint64_t memfault_platform_get_time_since_boot_ms(void) {
// Return time since boot in ms, this is used for relative timings.
return 0;
}

Configure Custom Metrics

note

To process device data, Memfault requires a matching uploaded Symbol File. See "Upload Symbol File for details.

Define First Heartbeat

Heartbeat metrics allow you to easily monitor your platform and confirm it is operating as expected.

Typical Heartbeat Examples
  • investigate problems that didn't cause a reboot (bad connectivity, network or sensor error rates) and marginality that crops up in a fleet
  • providing leading indicators of problems (rapid battery drain, drop in activity, etc)
  • compare trends across releases (improved connectivity, data efficiency etc)

Best Practices around each metric type that we recommend:

  • Timers - These are used to track time spent in particular states and can be used for debugging connectivity, battery life, and performance issues.
  • Counters - Track counts of a particular event class taking place. Suggested use cases are:
    • Operations your system are performing (number of events serviced by a task, bytes sent over network, bytes written to flash). Alerts can be configured to identify devices operating outside normal thresholds.
    • Counts of events for events that should never happen (i2c bus write error count, flash write error). You can alert if any of these situations are seen.
  • Gauges - These are values sampled at the end of each heartbeat interval. Common items include
    • Battery Metrics (Drop over an hour, current percent)
    • Heap utilization / stack high watermark

Add Metric to memfault_metrics_heartbeat_config.def

//! @file memfault_metrics_heartbeat_config.def
MEMFAULT_METRICS_KEY_DEFINE(MainTaskWakeups, kMemfaultMetricType_Unsigned)

Instrument Code to Update Heartbeat

void my_main_task(void) {
while (1) {
your_rtos_wait_for_event();
MEMFAULT_METRIC_ADD(MainTaskWakeups, 1);
}
}

7. Automatically Upload Data

Post Data to Cloud via Device Transport

Extensive details about how data from the Memfault SDK makes it to the cloud can be found here. In short, the Memfault SDK packetizes data into "chunks" that must be pushed to the Memfault cloud via the chunk REST endpoint.

The Memfault SDK exposes an API that packetizes Memfault data into "chunks". These "chunks" need to be forwarded to the Memfault Cloud (either directly via HTTPs or indirectly via a gateway device such as a computer or mobile application)

tip

If you cannot post data directly using HTTP, no problem at all. In fact, that is one of the most common configurations. In these setups, data can be proxied from the MCU to a device which can perform a HTTP request over transports such as UART, BLE, COAP, LoRa, Zigbee, etc). The size of a "chunk" is fully configurable and can be tuned to best match your transport requirements. All message re-assembly is dealt with by Memfault in the cloud.

#include "memfault/components.h"

bool try_send_memfault_data(void) {
// buffer to copy chunk data into
uint8_t buf[USER_CHUNK_SIZE];
size_t buf_len = sizeof(buf);

bool data_available = memfault_packetizer_get_chunk(buf, &buf_len);
if (!data_available ) {
return false; // no more data to send
}

// send payload collected to chunks endpoint
user_transport_send_chunk_data(buf, buf_len);
return true;
}

void send_memfault_data(void) {
if (!memfault_packetizer_data_available()) {
return false; // no new data to send
}

// [... user specific logic deciding when & how much data to send
while (try_send_memfault_data()) { }
}

Troubleshooting Data Transfer

See the docs on data transfer troubleshooting.

8. Next Steps

Now that the Memfault SDK is integrated on your device, there are many other features and options to explore. Check out these sections of our docs for more information: