Memfault on Bare Metal

Bare Metal reuses the standard nRF Connect SDK Memfault SDK integration (device information, metrics, trace events, coredumps, and chunk export). There is no separate Memfault SDK fork for Bare Metal.

However, Bare Metal runs without Zephyr multithreading (CONFIG_MULTITHREADING=n). Applications typically use a single main loop plus ISRs (SoftDevice events, Bare Metal Timer library callbacks, GPIO, and similar). Memfault APIs can therefore be invoked from more than one execution context even though there are no RTOS threads.

This page describes which Memfault APIs may be called from each execution context, what must be deferred to the main loop, and why.

Execution contexts

On Bare Metal, treat these as distinct callers of Memfault:

Main loop

The cooperative idle loop of the application. Examples: Periodic Memfault heartbeat timer processing, flushing deferred trace events, chunk export.

ISR

Interrupt handlers that can preempt the main loop. Examples: Button handling library debounce timer callbacks, Bare Metal Timer library expiry handlers, SoftDevice event handlers if Memfault is called directly from them.

Fault handler

HardFault and other exception handlers used for coredump collection. This context is effectively exclusive.

The Memfault SDK protects shared data with memfault_lock() and memfault_unlock(). On threaded nRF Connect SDK builds these map to a recursive k_mutex. On Bare Metal they map to an irq_lock()-based critical section when the CONFIG_BM_MEMFAULT_LOCK Kconfig option is enabled.

Platform lock

Enable the CONFIG_BM_MEMFAULT_LOCK Kconfig option (default on Bare Metal Memfault builds) to use subsys/memfault/memfault_platform_lock_bm.c.

This implementation:

  • Disables interrupts for the duration of a Memfault critical section while the main loop holds the lock, preventing ISR re-entry into Memfault.

  • Supports nested memfault_lock() calls, as required by the SDK.

  • Replaces memfault_platform_lock.c from the Memfault Zephyr port when the CONFIG_BM_MEMFAULT_LOCK Kconfig option is enabled (nrf-bm excludes that SDK source from the build so k_mutex is not required).

Disabling the CONFIG_BM_MEMFAULT_LOCK Kconfig option falls back to the default Zephyr Memfault lock and requires a working k_mutex implementation. This is not supported on typical Bare Metal configurations.

Usage rules

The following table summarizes the execution context each Memfault API may be called from, and the reason for each rule.

Memfault API usage on Bare Metal

API / operation

Context

Why

MEMFAULT_METRIC_ADD, MEMFAULT_METRIC_SET_*

ISR or main

Updates in-memory metric values only. Protected by memfault_lock().

MEMFAULT_METRIC_TIMER_START, MEMFAULT_METRIC_TIMER_STOP

ISR or main

Protected by memfault_lock(), so safe in either context. Timestamps are recorded at call time, so call start/stop at the moment the timed activity begins or ends. Do not defer these from an ISR to the main loop if the interval must reflect an ISR event (for example a button press). Calling from the main loop is fine when the timed activity is defined in main-loop context.

MEMFAULT_TRACE_EVENT*

ISR or main

From ISR, the SDK stores a pending event and returns quickly. Call memfault_trace_event_try_flush_isr_event() from the main loop to serialize it.

memfault_metrics_heartbeat_debug_trigger(), memfault_metrics_heartbeat_collect()

Main loop only

Serializes metrics into event storage. Concurrent serialization while the main loop reads storage (for example during chunk export) can corrupt data.

memfault_log_trigger_collection()

Main loop only

Same rationale as heartbeat serialization: writes to shared event storage.

memfault_packetizer_*

Main loop only

Reads and consumes serialized Memfault data for export. When exporting over Bluetooth® LE, Memfault Diagnostic Service (MDS) calls these functions from ble_mds_process(), so the application does not call them directly.

Heartbeat timer callback (MemfaultPlatformTimerCallback)

Main loop only

The callback serializes metrics into event storage, so it must not run in the timer ISR. For how to schedule the callback with the Bare Metal Timer library, see Heartbeat timer.

Coredump capture (HardFault path)

Fault handler

Runs with normal execution stopped; not subject to the ISR/main rules above.

Rules in practice

Call from ISR when the operation is short and either:

  • only touches in-memory SDK state protected by memfault_lock(), or

  • is explicitly designed for ISR use (trace events), or

  • must capture a timestamp at ISR time (for example MEMFAULT_METRIC_TIMER_STOP on a button press).

Call from the main loop when that is where the event or timed activity occurs (for example starting a timer metric when entering a main-loop state).

Defer to the main loop when the operation:

  • serializes data into event storage,

  • reads or exports stored chunks, or

  • performs non-trivial work that must not overlap with export.

Choosing the context for timer metrics

MEMFAULT_METRIC_TIMER_START and MEMFAULT_METRIC_TIMER_STOP may be called from ISR or main loop. Choose the context based on when the timed activity starts or stops, because the SDK records time at the call.

Call MEMFAULT_METRIC_TIMER_STOP() in the ISR when stopping measurement on an ISR event, such as a button press, so that the elapsed time matches the user action. Call start and stop from the main loop when the timed activity is tied to main-loop logic, for example when measuring time spent in a connection handler.

Caution

Do not defer start and stop from the ISR to the main loop only to keep Memfault out of the ISR. That records the main-loop processing time instead of the event time. Defer the heartbeat serialization instead, as shown in Recommended main-loop pattern.

Prerequisites

Memfault remote diagnostics are integrated with nRF Cloud. Before Memfault can decode the data that a Bare Metal application uploads, make sure that the following prerequisites are met:

  • You have a Memfault account and a Memfault project. If you do not already have access, register through the nRF Cloud Memfault registration page, and then create a Memfault project for the fleet you want to monitor.

  • The project key of that project is set with the CONFIG_MEMFAULT_NCS_PROJECT_KEY Kconfig option. Each project has a unique project key, and the key must match the project where you expect the device to appear.

  • The zephyr.elf symbol file is uploaded to Memfault for every software_version that the device reports, as set with the CONFIG_MEMFAULT_NCS_FW_VERSION Kconfig option.

Without a matching symbol file, chunks still upload, but coredumps and trace events cannot be decoded. For an example of how to upload a symbol file, see the testing steps of the Bluetooth: Memfault Diagnostic Service (MDS) sample.

For an overview of Memfault in the nRF Connect SDK, see nRF Cloud powered by Memfault.

Configuration

In addition to the CONFIG_BM_MEMFAULT_LOCK Kconfig option, typical Bare Metal Memfault applications enable:

Integrating Memfault in an application

In addition to the Kconfig options above, a Bare Metal application must provide the following before Memfault can collect and export data.

Memfault configuration files

The Memfault SDK includes three application-provided files by name, so the directory holding them must be on the include path. The Bluetooth: Memfault Diagnostic Service (MDS) sample keeps them in memfault_config and adds that directory from its CMakeLists.txt:

zephyr_include_directories(memfault_config)

All three of the following files must exist, even if some of them are empty. The build fails if any of them is not found on the include path.

  • memfault_platform_config.h - Memfault SDK settings that are not exposed through Kconfig.

  • memfault_metrics_heartbeat_config.def - Application-defined heartbeat metrics.

  • memfault_trace_reason_user_config.def - Application-defined trace reasons.

Fault handler

The SoftDevice handler owns the HardFault_Handler vector and forwards faults that belong to the application to the weak C_HardFault_Handler symbol. Point the Memfault fault handler at that symbol in memfault_platform_config.h:

#define MEMFAULT_EXC_HANDLER_HARD_FAULT C_HardFault_Handler

Without this define, the Memfault fault handler is never reached and no coredump is captured.

Heartbeat timer

With the CONFIG_MEMFAULT_METRICS_TIMER_CUSTOM Kconfig option, the application owns the heartbeat schedule. Implement memfault_platform_metrics_timer_boot(), which the Memfault SDK calls once during initialization with the heartbeat period and a callback:

bool memfault_platform_metrics_timer_boot(uint32_t period_sec,
                                          MemfaultPlatformTimerCallback callback);

Store the callback, start a repeating Bare Metal Timer library for the requested period, and return true. The callback serializes metrics into event storage, so the timer handler must only set a flag and the main loop must invoke the callback when that flag is set.

Operating system name and version

The Memfault SDK reports the operating system it was built against as part of the build ID information. On nRF Connect SDK this defaults to ncs with the nRF Connect SDK version. To report Bare Metal instead, override both macros in memfault_platform_config.h:

#include "ncs_bare_metal_version.h"
#define MEMFAULT_OS_VERSION_NAME "ncs bm"
#define MEMFAULT_OS_VERSION_STRING NCS_BARE_METAL_VERSION_STRING

The Memfault SDK fails to build if only one of the two macros is defined. The ncs_bare_metal_version.h header is generated by the build system from the Bare Metal VERSION file, so this reports the Bare Metal version and not the application version. The application version is reported separately as the Memfault software_version through the CONFIG_MEMFAULT_NCS_FW_VERSION Kconfig option.

Sample

The Bluetooth: Memfault Diagnostic Service (MDS) sample applies the rules on this page together with MDS export.

Dependencies

This integration depends on the Memfault firmware SDK and the nRF Connect SDK Memfault module (nrf/modules/memfault-firmware-sdk), pulled in when the CONFIG_MEMFAULT Kconfig option is enabled.

API documentation

Source files: subsys/memfault/

The Bare Metal Memfault integration does not expose an API of its own. It provides the memfault_lock() and memfault_unlock() platform overrides that the Memfault SDK requires on Bare Metal, and applications call the Memfault SDK API directly.