Skip to main content

Nordic nRF Connect SDK Integration Guide

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).

Memfault supports the below versions of nRF-Connect SDK. If a version is not included below, please contact us and we'll let you know the status!

  • ✅ >= v1.9.2, <= v3.1.0 tested and supported

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

tip

nRF Connect SDK 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 on GitHub). 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

Supported NCS Versions

Memfault targets supporting new NCS releases as they arrive. If a version is not included below, please contact us and we'll let you know the status!

  • ✅ >= v1.9.2, <= v2.8.0 tested and supported

1. Create a Project

info

The following assumes you already have a Memfault account. If you don't yet have an account, register with the following links given your platform:

Create a Project and get a Project Key

Go to app.memfault.com and from the "Select A Project" dropdown, click on "Create Project" to setup your first project. Choose a name that reflects your product, such as "smart-sink-dev".

Once you've created your project, you'll be automatically taken to an page that includes your project key. Copy the key and follow the rest of this guide.

2. Set up the SDK

important

This tutorial assumes you have a working nRF Connect SDK Environment with nRF Connect SDK version >=1.9.2 and are able to flash a board supported by the SDK (i.e nRF91, nRF52, nRF53, nRF54, 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

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

west.yml

- name: memfault-firmware-sdk
path: modules/lib/memfault-firmware-sdk
revision: 1.28.0
remote: memfault


Therefore, adding Memfault to your project only requires adding some Kconfig settings to the application!

To update the Memfault SDK without updating the nRF Connect SDK, you can add 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: 3.0.2
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: 1.28.0
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)

Update Kconfig options

Add or update the appropriate options in your system's prj.conf (Memfault project key here):

# Project-specific configuration settings

CONFIG_MEMFAULT=y
CONFIG_MEMFAULT_NCS_PROJECT_KEY="YOUR_PROJECT_KEY"

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.

info

CONFIG_MEMFAULT_NCS_PROJECT_KEY is optional in nRF Connect SDK >=v3.0.0 for any device that does not use MDS or the Memfault HTTP client to upload chunks to the Memfault cloud, and instead relies on an upstream device to forward data.

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.

info

Implement a custom memfault_platform_get_device_info() if the device info fields cannot be set into the static Kconfig values (i.e. in prj.conf or the command line). Set CONFIG_MEMFAULT_DEVICE_INFO_CUSTOM=y in prj.conf, and implement memfault_platform_get_device_info() in your application.

A stub memfault_platform_get_device_info() function looks like this, for reference:

#include "memfault/core/platform/device_info.h"

void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) {
// !FIXME: Populate with platform device information
//
// *NOTE* All fields must be populated, and the values assigned to the fields
// must have static lifetime: the data is accessed when this function returns.
// In this example, the fields are string literals, which are placed either
// inline into .text data tables, or in .rodata, and the pointers are valid
// for the lifetime of the program
//
// See https://mflt.io/version-nomenclature for more context
*info = (sMemfaultDeviceInfo) {
// Set the device serial to a unique value.
// It is typically set to a unique identifier like a serial number
// or MAC address.
// This is used to de-deduplicate data in Memfault cloud
.device_serial = "DEMOSERIAL",
// Set the device software type.
// It can be simply "app" for a single-chip device, otherwise it
// should match the component name, eg "ble", "sensor" etc.
// This is used to filter devices in the Memfault UI
.software_type = "app-main",
// Set the device software version.
// If using Memfault OTA, this should exactly match the OTA Release
// Version name for the installed image
.software_version = "1.0.0-dev",
// Set the device hardware revision.
// This is used to filter/group devices in the Memfault UI
.hardware_version = "evt",
};
}

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_modeLTE mode. 0 = None, 7 = LTE-M, 9 = NB-IoT
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 (LTE band reference, 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 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.

Using the Built-in Memfault Data Transport

nRF9160 and nRF7002 devices have direct internet access, and can upload Memfault diagnostic data over HTTPS. To enable automatic data uploads, add the following to your prj.conf:

CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD=y

The shell command mflt post_chunks can be used to manually trigger an upload over HTTPS.

Sending data to Memfault over UDP

Please refer to our documentation on Memfault and UDP.

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 us immediately if you encounter any cert-related issues.

Before proceeding, configure OTA Kconfigs for your NCS version:

note

In NCS v3.0.0, the download client library was deprecated in favor of the downloader library. For more information on migrating to the downloader library, see the v3.0.0 migration guide.

CONFIG_MEMFAULT_FOTA=y

# Enable testing FOTA via a CLI command
CONFIG_MEMFAULT_FOTA_CLI_CMD=y

# Subsystems to write OTA payloads to flash and update via MCUBoot
CONFIG_DFU_TARGET=y
CONFIG_DFU_TARGET_MCUBOOT=y
CONFIG_IMG_MANAGER=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_STREAM_FLASH=y
CONFIG_IMG_ERASE_PROGRESSIVELY=y

# Use NCS downloader APIs
CONFIG_FOTA_DOWNLOAD=y
CONFIG_DOWNLOADER=y
CONFIG_DOWNLOADER_MAX_FILENAME_SIZE=400
CONFIG_DOWNLOADER_STACK_SIZE=1600

# Print download progress to console
CONFIG_FOTA_DOWNLOAD_PROGRESS_EVT=y

Perform a full rebuild to pick up the new settings:

west build --pristine=always --board nrf9160dk_nrf9160_ns@1.0.0 nrf/samples/net/https_client

Flash the board, and using the mflt_nrf get_latest_url command, confirm that an OTA check can be performed:

uart:~$ mflt_nrf get_latest_url
[00:00:25.525,970] ‹err> mflt: Unable to fetch OTA url, rv=0

Navigate to the Processing Log and confirm the OTA check event was recorded:

Screenshot of a Processing Log showing a OTA check for update event received

note

The Processing Log is a great place to start when testing any device operation that interacts with Memfault.

Now rebuild the firmware with a newer Software Version, using these Kconfig settings:

CONFIG_MEMFAULT_NCS_FW_VERSION_STATIC=y
# The default version will look like "0.0.1+c85ef3". This version is
# set to "newer" (higher precedence) to trigger an OTA.
CONFIG_MEMFAULT_NCS_FW_VERSION="0.0.2"

Build your firmware with the later version, and upload the OTA payload and matching symbols using the Memfault CLI:

memfault --project YOUR_PROJECT --org YOUR_ORG_SLUG \
--org-token YOUR_ORG_TOKEN upload-ota-payload \
--hardware-version nrf9160dk --software-version 0.0.2 \
--software-type nrf91ns-fw build/https_client/zephyr/app_update.bin
memfault --project YOUR_PROJECT --org YOUR_ORG_SLUG \
--org-token YOUR_ORG_TOKEN upload-symbols \
--software-type nrf91ns-fw --software-version 0.0.2 \
build/https_client/zephyr/zephyr.elf
tip

The Memfault CLI requires installation and authentication. See the CLI installation guide and Organization Auth Token.

Go to Software → Releases in Memfault and find your new Release and click Activate.

Screenshot of a Activate Release modal

Use the mflt_nrf get_latest_url command on the device to check that the OTA is activated:

# If not OTA is available, rv=0
uart:~$ mflt_nrf get_latest_url
uart:~$ [00:02:35.962,371] <err> mflt: Unable to fetch OTA url, rv=0
# After activating the OTA, a payload URL is returned
uart:~$ mflt_nrf get_latest_url
Download URL: 'https://ota-cdn.memfault.com/693/130/14605210130?token=1K0Di8MzovgDIJxyO7Z09olIjD_d5_Uj3278KO1gCaA&expires=1714172400'

Use the mflt_nrf fota command to execute the OTA:

# The FOTA client will try a sequence of TLS certs during the download,
# so there may be "Unable to connect, errno 114" responses, these are
# normal.

uart:~$ mflt_nrf fota
[00:03:00.426,910] <inf> mflt: Checking for FOTA
uart:~$ [00:03:02.689,514] <inf> mflt: FOTA Update Available. Starting Download!
[00:03:02.690,338] <inf> download_client: Downloading: https://ota-cdn.memfault.com/693/130/14605211234?token=1K0Di8MzovgDIJxyO7Z09olIjD_d5_Uj3278KO1gCaA&expires=1714172400 [0]
[00:03:02.690,399] <inf> mflt: FOTA In Progress
[00:03:02.900,024] <inf> download_client: Setting up TLS credentials, sec tag count 3
[00:03:02.900,146] <inf> download_client: Connecting to 2400:52e0:1a00::718:1
[00:03:02.900,360] <err> download_client: Unable to connect, errno 114
[00:03:03.006,317] <inf> download_client: Setting up TLS credentials, sec tag count 3
[00:03:03.006,439] <inf> download_client: Connecting to 169.150.236.100
[00:03:04.660,308] <inf> download_client: Downloaded 1024/250791 bytes (0%)
[00:03:05.184,417] <inf> download_client: Downloaded 2048/250791 bytes (0%)
[00:03:05.411,560] <inf> download_client: Downloaded 3072/250791 bytes (1%)
...
[00:04:17.788,665] <inf> download_client: Downloaded 248832/250791 bytes (99%)
*** Booting nRF Connect SDK d96769faceca ***
I: Starting bootloader

After the update downloads and installs, the board will reboot. Use the mflt get_device_info command to check the new version was installed:

uart:~$ mflt get_device_info
[00:00:09.159,088] <inf> mflt: S/N: 352656106683967
[00:00:09.159,149] <inf> mflt: SW type: nrf91ns-fw
[00:00:09.159,210] <inf> mflt: SW version: 0.0.2
[00:00:09.159,271] <inf> mflt: HW version: nrf9160dk

Celebrate, your device has completed an OTA! 🎉

tip

See a full example app that enables OTA using MCUBoot in the Memfault SDK.

Reboot Reasons for HardFaults

For Nordic devices, the issue of obtaining reboot reasons when using TF-M is resolved in nRF Connect SDK v2.8.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:

west.yml

- name: memfault-firmware-sdk
path: modules/lib/memfault-firmware-sdk
revision: 1.28.0
remote: memfault


Therefore, adding Memfault to your project only requires adding some Kconfig settings to the application!

To update the Memfault SDK without updating the nRF Connect SDK, you can add 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: 3.0.2
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: 1.28.0
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)

Generating a Software Bill of Materials (SBOM)

Follow the guide here to generate an SBOM for your NCS project:

https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/scripts/west_commands/sbom/README.html

Specifically, after setting up the ncs-sbom west command and building your project, run this command to generate an SBOM in SPDX format:

west ncs-sbom -d build --output-spdx sbom.spdx
note

This command can take several minutes to complete (>15 minutes) depending on the size of your project and the number of dependencies.

The .spdx file can then be uploaded to Memfault's SBOM management system.

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.

Is a Project Key required for devices that have their chunks uploaded by an upstream device?

CONFIG_MEMFAULT_NCS_PROJECT_KEY is optional in nRF Connect SDK >=v3.0.0 for any device that does not use MDS or the Memfault HTTP client to upload chunks to the Memfault cloud, and instead relies on an upstream device to forward data. For nRF Connect SDK < v3.0.0, a non-zero Project Key is required even if it is not used. Set it to a dummy string such as "unset" to avoid a compiler error.

How do I override the NCS definition of memfault_metrics_heartbeat_collect_data() in order to add custom metric collection at the end of a heartbeat interval?

NCS provides an implementation of memfault_metrics_heartbeat_collect_data() to collect built-in BLE, stack, and LTE metrics. In NCS versions >=2.6.0, users can set CONFIG_MEMFAULT_NCS_IMPLEMENT_METRICS_COLLECTION=n to define their own version of this function, and call into the NCS API to still do NCS metric aggregation:

memfault_platform_port.c
#include "memfault_ncs_metrics.h"
#include "memfault/components.h"

void memfault_metrics_heartbeat_collect_data(void)
{
// Set custom metric values
// ...

// Call NCS metric collection
memfault_ncs_metrics_collect_data();
}

For NCS versions <2.6.0, wrap a function in the Memfault SDK that's called just after memfault_metrics_heartbeat_collect_data(). In your CMakelists.txt file add:

zephyr_ld_options(-Wl,--wrap=memfault_metrics_heartbeat_serialize)

Then, provide the wrapping:

memfault_platform_port.c
#include "memfault/components.h"

//! Wrap a function that's called just after memfault_metrics_heartbeat_collect_data(),
//! to enable collecting custom metrics during heartbeat collection.
extern bool __real_memfault_metrics_heartbeat_serialize(const sMemfaultEventStorageImpl *storage_impl);
bool __wrap_memfault_metrics_heartbeat_serialize(const sMemfaultEventStorageImpl *storage_impl) {
// Set custom metric values
// ...

return __real_memfault_metrics_heartbeat_serialize(storage_impl);
}