Skip to main content

nRF Connect SDK Integration Guide

In this guide we will walk through the steps for integrating the Memfault Firmware SDK into a project using the nRF Connect SDK. The integration has been tested against the releases ranging from v1.4.0 up to v1.6.0.

tip

nRF Connect SDK v1.6.0 has built-in support for the Memfault Firmware SDK on nRF9160-based targets. Nordic Semiconductor has released some excellent documentation on how to integrate Memfault in your existing nRF Connect SDK project, together with a sample integration project. 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!

Integration Steps#

important

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

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 such as "smart-sink-dev".

Once you've created your project, you'll be automatically taken to an integration guide which includes your project key. Copy it to follow the rest of the guide.

Include memfault-firmware-sdk in the build#

The SDK is part of the West manifest in nRF Connect SDK 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 selections in prj.con#

Add the appropriate options to your system's prj.conf:

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

You can skip this section.

Create trace reasons definition file#

The trace event module within the SDK makes it easy to track errors in a way that requires less storage than full coredump traces and also allows the system to keep running after capturing the event. The program counter, return address and a custom "reason" are saved.

The list of custom reasons is defined in a separate file that you need to create.

The file must be named memfault_trace_reason_user_config.def and placed in a directory included in your project.

$ cd $YOUR_PROJECT_ROOT
$ mkdir -p config
$ touch config/memfault_trace_reason_user_config.def

And then in your project's CMakeLists.txt add:

zephyr_include_directories(config)

To start, we recommend adding a couple reasons for error paths in your codebase (such as peripheral bus read/write failures, transport errors and unexpected timeouts).

Here is what the memfault_trace_reason_user_config.def file should look like:

// your_project/config/memfault_trace_reason_user_config.def
MEMFAULT_TRACE_REASON_DEFINE(your_custom_error_reason_1)
// i.e
MEMFAULT_TRACE_REASON_DEFINE(critical_log)
MEMFAULT_TRACE_REASON_DEFINE(flash_error)
// ...

Add Additional Config Files#

Two other config files are required to build with the Memfault SDK:

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

See the nRF Connect Docs for more information.

Generate Some Trace Events#

Next, we'll need to use the MEMFAULT_TRACE_EVENT macro to capture a trace event when an error occurs.

Note that it is perfectly fine to use the same reason in different places. Because the program counter and return address are captured in the trace event, you will be able to see the two topmost frames (function name, source file and line) in Memfault's Issue UI and distinguish between the two.

The nRF Connect SDK port also exposes a mflt trace CLI command which can be used to generate events for test purposes:

uart:~$ mflt trace
<dbg> <mflt>: Trace Event Generated!

You can also start to add trace events for error paths you are interested in tracking:

#include "memfault/core/trace_event.h"
// [ ...]
void ble_le_process_ll_pkt(...) {
// ...
if (invalid_msg_id) {
MEMFAULT_TRACE_EVENT(bt_invalid_msg_id);
// ...
}
// ..
}

Log metadata can also be added to a trace event to capture additional info:

void record_temperature(...) {
int rv = spi_flash_erase(...);
if (rv != 0) {
MEMFAULT_TRACE_EVENT_WITH_LOG(flash_error, "Flash Erase Failure: rv=%d, spi_err=0x%x",
spi_bus_get_status());
}
}

Publish data to the Memfault cloud#

(nRF9160) HTTPS#

When HTTP is available, you can use the Memfault integration to post data from the CLI (mflt post_chunks) or by adding a periodic task:

#include "memfault/nrfconnect_port/http.h"
void some_periodic_task(void) {
memfault_nrfconnect_port_post_data();
}

Bluetooth#

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.

A typical integration looks like this:

#include "memfault/core/data_packetizer.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) {
// [... user specific logic deciding when & how much data to send
while (try_send_memfault_data()) { }
}

Force a Crash and Upload to Memfault#

Example crashes can be generated using the mflt CLI:

uart:~$ mflt crash
# ...
*** Booting Zephyr OS build v2.4.0-ncs1 ***
<inf> <mflt>: GNU Build ID: ef460cafbcf67633d470c125a7cc8fd02ed97538
Memfault Demo App Started!
uart:~$ mflt get_core
<inf> <mflt>: Has coredump with size: 3764

On nRF91 you can post directly to the cloud:

uart:~$ mflt post_chunks
<dbg> <mflt>: Response Complete: Parse Status 0 HTTP Status 202!
<dbg> <mflt>: Body: Accepted
<dbg> <mflt>: No more data to send

Test Tip#

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

uart:~$ mflt export_data
<inf> <mflt>: MC:SFQCpwIBAwEHalRFU1RTRVJJQUwKbXRlc3Qtc29mdHdhcmUJajEuMC4wLXRlcw==:
<inf> <mflt>: MC:gCx0Bm10ZXN0LWhhcmR3YXJlBKEBoXJjaHVua190ZXN0X3N1Y2Nlc3MBMeQ=:

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 succesfully 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 programatically upload symbol files with the Memfault CLI tool.

Troubleshooting Data Transfer#

If you run into any issues with your data transfer implementation, we have tools to help!

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.

Useful links#