Skip to content

System Integration

Review MiraMesh hardware requirements here and here.

Bare metal system

Using MiraMesh in a bare metal environment is straight forward. Call miramesh_init (with the wakeup callbacks doing nothing), configure the memory usage (see mem_usage API), implement miramesh_lock/miramesh_unlock as two empty functions and call your application logic as well as miramesh_run_once in a loop.

For power saving, the loop and wakeup-callbacks have to cooperate so miramesh_run_once is only called when really needed (after the wakeup functions have run) and otherwise call __WFE in the loop. This can be implemented with a simple flag.

There is an example of this in the examples/miramesh folder.

An operating system with preemptive threads

An OS integrating MiraMesh must implement the functions miramesh_lock and miramesh_unlock, they are used to prevent simultanious access to MiraMesh's internal data. The lock need to support recursive calls. For FreeRTOS a RecursiveMutex is a good choice.

As with a bare metal system, miramesh_init needs to be called before any other mira function. The wakeup callbacks are used to wake up the thread/task that should call the miramesh_run_once function. For FreeRTOS a good strategy is to have a task wait with xTaskNotifyWait and the wakeup callbacks call xTaskNotify/xTaskNotifyFromISR to wake it up. When the task wakes up it calls miramesh_run_once before sleeping again.

The priority of the task running miramesh_run_once need to be such that it gets a chance to run once for every packet that the system wants to send or receive. For network rate 0, that means once every 10ms.

There is an example of using MiraMesh with FreeRTOS in the examples/miramesh folder and on GitHub.

An operating system with cooprative threading

When using an OS with cooperative threading a lock is not needed, otherwise it works more or less like an integration on a system with preemtive threads. When miramesh_run_once has run, the thread should yield control to the other threads.

How to verify a working integration.

Connect the node to a known working node, i.e. the examples/miraos/network_receiver or network_sender examples. See that the mesh node connects with the mira_net_get_state function within a few minutes.

Measure the time between the wakeup callbacks being called and when miramesh_run_once is called. When it takes longer than 10ms, packets risk being dropped and the network performance becomes worse so it shouldn't happen frequently.

Monitor the tx_dropped/tx_failed/rx_missed_slots values of the result from mira_diag_mac_get_statistics. If those values often increase, it indicates that something blocks miramesh' IRQs too much.




How many PPIs used by MiraMesh



How many PPI groups used by MiraMesh



Name Type Description
api_lock void(*)(void) Take the big lock before running MiraMesh code.

The function needs to be reentrant by the same thread and the lock should not be released until miramesh_unlock has been called just as many times.

api_unlock void(*)(void) Release the big lock after running MiraMesh code.

The function needs to be reentrant by the same thread and the lock should not be released until miramesh_unlock has been called just as many times as miramesh_lock has been called.

wakeup_from_app_callback void(*)(void) Called when MiraMesh wants miramesh_run_once to be called.

This callback is called from app/task context.

!!! note

miramesh_run_once can not be called from the callback.

wakeup_from_irq_callback void(*)(void) Called when MiraMesh wants miramesh_run_once to be called.

This callback is called from IRQ context. Callbacks may occur from any IRQ level, including highest used by radio. This may be higher than maximum allowed IRQ level for RTOS calls, and measures may need to be taken to properly call RTOS API.

In case of FreeRTOS, an example is available at GitHub

!!! note

miramesh_run_once can't be called from the callback.


Name Type Description
ppi_idx uint8_t[MIRAMESH_SYS_NUM_PPIS_USED] The PPIs to use
ppi_group_idx uint8_t[MIRAMESH_SYS_NUM_PPI_GROUPS_USED] The PPI groups to use
rtc uint8_t The RTC to use, 2 for RTC2
rtc_irq_prio uint8_t The prio of the RTC IRQ to use
swi uint8_t The SWI/EGU to use beside SWI1
swi_irq_prio uint8_t The SWI prio. Should be of higher priority than rtc_irq_prio


Name Type Description
start const uint8_t * pointer to the certificate/license pool
end const uint8_t * pointer to the end of the certificate/license pool


Name Type Description
callback miramesh_callback_cfg_t Needed callbacks for OS integration
hardware miramesh_hardware_cfg_t Describes needed hardware resources
certificate miramesh_certificate_cfg_t Describes the certificate location



mira_status_t miramesh_init(const miramesh_config_t *config, const mira_net_frontend_config_t *frontend_cfg);

Init the MiraMesh system.

This function must be called before any other MiraMesh functions are called. It must also be called before any SoftDevice protocol is used on nrf52 platforms.

During the call it will use the api_lock/api_unlock callbacks multiple times to verify that they can be used recusively.


Name Description
config The system configuration
frontend_cfg Frontend configuration, NULL means no frontend config

Return value

Name Description
MIRA_SUCCESS Successfully initialized MiraMesh.
MIRA_RADIO_ERROR A softdevice protocol has already been started or invalid frontend cfg.
MIRA_ERROR_INVALID_VALUE Incorrect config values.


void miramesh_handle_sd_event(uint32_t evt_id);

Routes events from SoftDevice to MiraMesh.

This function is used by nRF52 targets to process recieved events.

Normally used like this:

#include "miramesh.h"
#include "miramesh_sys.h"
#include "nrf_sdh_soc.h"

static void sd_evt_observer(uint32_t evt_id, void *ctx) {

NRF_SDH_SOC_OBSERVER(m_sd_evt_miramesh, 0, sd_evt_observer, 0);
For that to work the nRF5 SDK's nrf_sdh_soc.c file needs to be part of the build.


void miramesh_rtc_irq_handler(void);

The IRQ handler for the RTC events.

This function should be called from the RTCx_IRQHandler


void miramesh_swi_irq_handler(void);

The IRQ handler for the configurable SWI events.

This function should be called from the SWIx_EGUx_IRQHandler or put in the isr_vector directly.


void miramesh_swi1_irq_handler(void);

The IRQ handler for the SWI1 events.

This function should be called from the SWI1_EGU1_IRQHandler or put in the isr_vector directly.


void miramesh_run_once(void);

Run the system once.

This function should be called repeatedly. After running once the thread should sleep until miramesh_wakeup_from_IRQ or miramesh_wakeup_from_API have been called.


while(1) {
    if(xTaskNotifyWait(0, 1, NULL, portMAX_DELAY) == pdPASS) {

Back to top