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.cfrom the Memfault Zephyr port when theCONFIG_BM_MEMFAULT_LOCKKconfig option is enabled (nrf-bm excludes that SDK source from the build sok_mutexis 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.
API / operation |
Context |
Why |
|---|---|---|
|
ISR or main |
Updates in-memory metric values only.
Protected by |
|
ISR or main |
Protected by |
|
ISR or main |
From ISR, the SDK stores a pending event and returns quickly.
Call |
|
Main loop only |
Serializes metrics into event storage. Concurrent serialization while the main loop reads storage (for example during chunk export) can corrupt data. |
|
Main loop only |
Same rationale as heartbeat serialization: writes to shared event storage. |
|
Main loop only |
Reads and consumes serialized Memfault data for export.
When exporting over Bluetooth® LE, Memfault Diagnostic Service (MDS) calls these functions from |
Heartbeat timer callback ( |
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(), oris explicitly designed for ISR use (trace events), or
must capture a timestamp at ISR time (for example
MEMFAULT_METRIC_TIMER_STOPon 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.
Recommended main-loop pattern
When Memfault is used from ISRs, the main loop must regularly drain the work that cannot be done in ISR context.
The pattern is the same in every case: the ISR records what happened and sets a flag, and the main loop performs the serialization.
For example, the memfault_metrics_heartbeat_debug_trigger() function serializes metrics into event storage, so it must run in the main loop and not in the ISR that triggered the heartbeat.
The memfault_metrics_timer_process() function and the memfault_heartbeat_pending flag are application-defined, not Memfault SDK API.
The Bluetooth: Memfault Diagnostic Service (MDS) sample implements both.
/* Application state, written in an ISR and read in the main loop. */
static volatile bool memfault_heartbeat_pending;
/* ISR context: record the event, defer the serialization. */
MEMFAULT_METRIC_TIMER_STOP(button_elapsed_time_ms);
memfault_heartbeat_pending = true;
/* Main loop: run the deferred work. */
memfault_metrics_timer_process(); /* CONFIG_MEMFAULT_METRICS_TIMER_CUSTOM */
memfault_trace_event_try_flush_isr_event();
if (memfault_heartbeat_pending) {
memfault_heartbeat_pending = false;
memfault_metrics_heartbeat_debug_trigger();
}
ble_mds_process(&mds); /* CONFIG_BLE_MDS */
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_KEYKconfig option. Each project has a unique project key, and the key must match the project where you expect the device to appear.The
zephyr.elfsymbol file is uploaded to Memfault for everysoftware_versionthat the device reports, as set with theCONFIG_MEMFAULT_NCS_FW_VERSIONKconfig 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:
CONFIG_MEMFAULT- Enable the Memfault SDK.CONFIG_MEMFAULT_METRICS_TIMER_CUSTOM- Use Bare Metal Timer library to schedule periodic heartbeat wakeups; defer the heartbeat callback to the main loop instead of using a Zephyrk_work/k_timerin the default Memfault port.CONFIG_MEMFAULT_REBOOT_REASON_GET_BASIC- Basic reboot-reason collection compatible with all Bare Metal board variants.CONFIG_MEMFAULT_COREDUMP_COLLECT_KERNEL_REGION=nandCONFIG_MEMFAULT_COREDUMP_COLLECT_TASKS_REGIONS=n- No Zephyr kernel or task regions on bare metal.Optional: enable the
CONFIG_BLE_MDSKconfig option to export over Bluetooth LE. See Memfault Diagnostic Service (MDS) for MDS-specific options.
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
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.
For the Memfault SDK API, see the Memfault SDK documentation.
For exporting Memfault data over Bluetooth LE, see Memfault Diagnostic Service API reference.