Common¶
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.
Operating systems with preemptive threads¶
FreeRTOS¶
There are examples of running MiraMesh with FreeRTOS here.
The examples uses a RecursiveMutex for the miramesh_lock/miramesh_unlock callbacks.
They have a task that waits with xTaskNotifyWait and then runs miramesh_run_once before waiting again. The wakeup callbacks call xTaskNotify/xTaskNotifyFromISR to wake it up.
Zephyr¶
There is a west module for integrating MiraMesh with Zephyr.
There is an example of using it here.
Others¶
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.
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.
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.
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.
Front End Module (FEM) support¶
If the hardware has a FEM, MiraMesh needs to be configured to handle it. If another protocol stack (like BLE via the SoftDevice Controller) is used, either it needs to be configured to control the FEM properly too, or MiraMesh can in some cases be used to control the FEM for it too.
On nRF52 targets with the SoftDevice (nrf52*ble-net), the FEM is put in bypass mode by MiraMesh when the BLE stack is using the radio.
On targets with SoftDevice Controller (from NCS), the stack can be configured to let MiraMesh controll the FEM.
On nRF52, SWI1/EGU1 IRQ needs to be triggered before the radio is used. On nRF54L, a channel on EGU10 needs to be triggered before the radio is used.
With the BLE protocol and Zephyr, that can be done with the following code snippets.
#include <bluetooth/hci_vs_sdc.h>
#include <hal/nrf_egu.h>
#define EGU_NODE DT_ALIAS(egu)
#define NRF_EGU ((NRF_EGU_Type *) DT_REG_ADDR(EGU_NODE))
This only needs to be done once:
// Connect the IRQ:
IRQ_DIRECT_CONNECT(DT_IRQN(EGU_NODE), 5, miramesh_egu_irq_handler, 0);
nrf_egu_int_enable(NRF_EGUx, NRF_EGU_INT_TRIGGERED0);
NVIC_EnableIRQ(DT_IRQN(EGU_NODE));
miramesh_swi1_irq_handler
instead of miramesh_egu_irq_handler
on nRF52. On nRF54L: Change TRIGGERED0
to the channel configured in miramesh's egu_event
config.
// Configure event task to trigger NRF_EGU_TASK_TRIGGER0:
sdc_hci_cmd_vs_set_event_start_task_t cmd_params = {
.handle_type = SDC_HCI_VS_SET_EVENT_START_TASK_HANDLE_TYPE_ADV,
.task_address = nrf_egu_task_address_get(DT_IRQN(EGU_NODE), NRF_EGU_TASK_TRIGGER0),
};
int err = hci_vs_sdc_set_event_start_task(&cmd_params);
if (err) {
LOG_ERR("Error for command hci_vs_sdc_set_event_start_task() (%d)\n",
err);
}
TRIGGERED0
to the channel configured in miramesh's egu_event
config.
This should done for every type in sdc_hci_vs_set_event_start_task_handle_type
, not only SDC_HCI_VS_SET_EVENT_START_TASK_HANDLE_TYPE_ADV.
This configuration must done after the first time BLE has been used, after advertisment has been started for example.
There is a ready made function for this in the Zephyr MiraMesh integration. See its README.md file for its documentation.
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.
Types¶
miramesh_certificate_cfg_t¶
Alias for backwards compability
Structs¶
miramesh_callback_cfg_t¶
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. |
miramesh_rng_cfg_t¶
Name | Type | Description |
---|---|---|
rng_gen_init |
void(*)(void) |
Initializatoin of the RNG. NOTE: If initialization is not required, set this to NULL |
rng_gen_u16 |
uint16_t(*)(void) |
This function will be called when MiraMesh requires random numbers. NOTE: Numbers generated by the provided generator needs to be cryptographically secure. |
miramesh_factory_config_t¶
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 |
miramesh_config_t¶
Note
The struct miramesh_config_t can be updated with more fields in future versions. Therefore, it is recommended that the entire memory block of the structure is zeroed out before setting the desired fields.
Name | Type | Description |
---|---|---|
callback |
miramesh_callback_cfg_t |
Needed callbacks for OS integration |
rng |
miramesh_rng_cfg_t |
Optional custom rng function, set both struct members to NULL to use internal rng |
hardware |
miramesh_hardware_cfg_t |
Describes needed hardware resources |
factory_config |
miramesh_factory_config_t |
Describes the factory_config location |
certificate |
miramesh_certificate_cfg_t |
Backwards compatible alias to factory_config |
@13 |
union miramesh_config_t::@12 |
|
platform_callback |
miramesh_platform_specific_callback_cfg_t |
Functions¶
miramesh_init¶
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.
Parameters¶
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. |
miramesh_is_init¶
Is MiraMesh initialized.
Return value¶
Name | Description |
---|---|
MIRA_TRUE | MiraMesh is initialized |
MIRA_FALSE | MiraMesh is not initialzied |
Return value¶
mira_bool_t
miramesh_run_once¶
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.
Example: