nRF5 SDK for Thread and Zigbee v3.2.0
Adding dynamic multiprotocol Thread support to BLE examples

Table of Contents

Applications demonstrating the Bluetooth peripheral functionality can be extended by adding Thread protocol support to them. In this way, dynamic multiprotocol functionality can be achieved.

For reference, see <InstallFolder>\examples\multiprotocol\ble_thread\ble_thread_dyn_mtd_coap_cli, which is a modified version of the ble_app_uart example.

For more information about multiprotocol support, see Multiprotocol support with BLE/Bluetooth.

Modification in sdk_config.h

Add the following settings to sdk_config.h in order to configure the Thread stack:

// <h> openthread
//==========================================================
// <h> openthread - OpenThread stack
//==========================================================
// <o> THREAD_PANID - 802.15.4 PAN ID used by Thread <0-65535>
// <i> 802.15.4 PAN ID used by Thread. Defaults to 0xABCD.
#ifndef THREAD_PANID
#define THREAD_PANID 43981
#endif
// <o> THREAD_CHANNEL - 802.15.4 channel used by Thread <11-26>
// <i> 802.15.4 channel used by Thread. Defaults to 11.
#ifndef THREAD_CHANNEL
#define THREAD_CHANNEL 11
#endif
// </h>
//==========================================================
// </h>
//==========================================================

Modify these settings if they appear in sdk_config.h:

#define APP_UART_DRIVER_INSTANCE 1
#define UART0_ENABLED 0
#define UART1_ENABLED 1

Libraries

nRF5 SDK for Thread provides precompiled OpenThread libraries that must be added to the project.

The default set of libraries for an FTD Thread device that uses CLI is the following:

Note
You might need to use another set of libraries, depending on the Thread use case that you aim for. See OpenThread libraries.

Modification in GCC Makefile

Add the following snippet to the Makefile of a BLE application:

# Libraries common to all targets
LIB_FILES += \
$(SDK_ROOT)/external/openthread/lib/gcc/libopenthread-cli-ftd.a \
$(SDK_ROOT)/external/openthread/lib/gcc/libopenthread-ftd.a \
$(SDK_ROOT)/external/openthread/lib/gcc/libopenthread-nrf52840-softdevice-sdk.a \
$(SDK_ROOT)/external/openthread/lib/gcc/libopenthread-diag.a \
$(SDK_ROOT)/external/openthread/lib/gcc/libmbedcrypto.a \
$(SDK_ROOT)/external/nrf_cc310/lib/libnrf_cc310_0.9.12.a \

To use the Thread API, you must add header include paths. To do this, add the following line inside of the INC_FOLDERS section:

$(SDK_ROOT)/components/thread/utils \
$(SDK_ROOT)/external/openthread/include \

Add the path to the Thread utils source file in the SRC_FILES section:

$(SDK_ROOT)/components/thread/utils/thread_utils.c \

For reference, see this Makefile that is already ported: <InstallFolder>\examples\multiprotocol\ble_thread\ble_thread_dyn_mtd_coap_cli\pca10056\s140\armgcc\Makefile

Modification in IAR project

To add libraries under the IAR compiler, click Project -> Add Files. Then, choose the file type of Library/Object Files and navigate to \external\openthread\lib\iar.

Select all required libraries according to the above section. Note that IAR additionally requires the libopenthread-platform-utils.a library.

To use the Thread API, you must add header include paths. To do this, click Project -> Options and choose the C/C++ Compiler section. In the Preprocessor tab, navigate to the Additional include directory section. Add the path to the \external\openthread\include and \components\thread\utils folders.

After the folder paths are added, depending on your project location, the following records will be visible:

$PROJ_DIR$\..\..\..\..\..\..\components\thread\utils
$PROJ_DIR$\..\..\..\..\..\..\external\openthread\include

You must also add the Thread utils source file. Select Project -> Add Files and add the \components\thread\utils\thread_utils.c source file.

The final step is to unselect the Perform C++ Virtual Function Elimination option in Project -> Options -> Linker -> Optimizations.

For reference, see the IAR project that is already ported: <InstallFolder>\examples\multiprotocol\ble_thread\ble_thread_dyn_mtd_coap_cli\ble_thread_dyn_mtd_coap_cli.eww.

Modification in Keil 5 project

To add libraries under the Keil 5 compiler, add a new group to the project by clicking on the project, selecting the Add Group option, and creating a group named 'Thread'.

Add all required libraries according to above section. Click on the new group and select Add Existing Files to Group 'Thread'. Then, choose the file type of Toolchain Library file and select all required libraries from \external\openthread\lib\keil and \external\nrf_cc310\lib. Note that Keil 5 additionally requires the libopenthread-platform-utils.a library.

To use the Thread API, you must add header include paths. To do this, click on the project, select Options for Target'nrf52840', and choose the C/C++ section. In the Include Paths section, add the path to the \external\openthread\include and \components\thread\utils folders.

After folder paths are added, depending on your project location, the following records will be visible:

..\..\..\..\..\..\components\thread\utils
..\..\..\..\..\..\external\openthread\include

You must also add the Thread utils source file. Click on the Thread group and select Add Existing Files to Group 'Thread'. Select the \components\thread\utils\thread_utils.c source file.

Keil 5 does not use linker scripts. Therefore, it does not require any modifications.

For reference, see this Keil 5 project that is already ported: <InstallFolder>\examples\multiprotocol\ble_thread\ble_thread_dyn_mtd_coap_cli\pca10056\s140\arm5_no_packs\ble_thread_dyn_mtd_coap_cli_pca10056_s140.uvprojx.

Modification in SEGGER Embedded Studio project

To add libraries under the SES, click Project -> Add Existing File.... Then, choose the file type of All files and navigate to \external\openthread\lib\gcc. You also need to add the GCC version of the CC310 library from \external\nrf_cc310\lib.

Select all needed libraries as listed in the GCC section.

To use the Thread API, you need to add headers include paths. To do that, click on the project, select Edit options, and choose the Preprocessor section. In the User Include Directories section, add the path to the \external\openthread\include and \components\thread\utils folders:

$(ProjectDir)/../../../../../../components/thread/utils
$(ProjectDir)/../../../../../../external/openthread/include

The include paths might vary depending on your project location.

You must also add the Thread utils source files. Click Project -> Add Existing File..., navigate to \components\thread\utils, and select the thread_utils.c and thread_assert.c source files.

For reference, see the SES project that is already ported: <InstallFolder>\examples\multiprotocol\ble_thread\ble_thread_dyn_mtd_coap_cli\pca10056\s140\ses\ble_thread_dyn_mtd_coap_cli_pca10056_s140.emProject.

Modification in the linker script

Thread requires a special block in the flash memory to store protocol-specific data, such as Active and Pending Dataset or Parent/Child information. This data is necessary for synchronization after reset.

Modification in GCC linker script

It is recommended to use the example linker script that is already ported: <InstallFolder>\external\openthread\linker_scripts\openthread_nrf52840_multiprotocol.ld. To change the linker file, click Project -> Options and choose the Linker section. On the Config tab, select the appropriate linker file in the Linker configuration file section.

Note that it might be possible to adjust the RAM start of the application, which depends on the SoftDevice configuration. To ensure that the RAM start is set correctly, set the ORIGIN and LENGTH variables of the RAM section to what is set in the original example.

For example:

RAM (rwx) : ORIGIN = 0x20002a98, LENGTH = 0x3d568

Modification in IAR linker script

It is recommended to use the example linker script that is already ported: <InstallFolder>\external\openthread\linker_scripts\openthread_nrf52840_multiprotocol.icf

Note that it may be possible to adjust the RAM start of the application, which depends on the SoftDevice configuration. To ensure that the RAM start is set correctly, set the __ICFEDIT_region_RAM_start__ and __ICFEDIT_region_RAM_end__ variables to what is set in the original example.

For example:

define symbol __ICFEDIT_region_RAM_start__ = 0x20002a98;
define symbol __ICFEDIT_region_RAM_end__ = 0x2003ffff;

Modification in SES linker script

You must add the following section at the end of the flash_placement.xml file for the OpenThread persistent data:

...
</MemorySegment>
<MemorySegment name="ot_flash_data" start="0x000fc000" size="0x4000">
<ProgramSection alignment="4" keep="Yes" load="No" name=".ot_flash_data" address_symbol="__start_ot_flash_data" end_symbol="__stop_ot_flash_data" start = "0x000fc000" size="0x4000" />
</MemorySegment>
</Root>

You must also modify the project file manually to export new linker symbols correctly. Open ble_app_uart_pca10056_s140.emProject with a text editor and replace the following lines:

linker_section_placement_macros="FLASH_PH_START=0x0;FLASH_PH_SIZE=0x100000;RAM_PH_START=0x20000000;RAM_PH_SIZE=0x40000;FLASH_START=0x26000;FLASH_SIZE=0xda000;RAM_START=0x20002a98;RAM_SIZE=0x3d568"
linker_section_placements_segments="FLASH RX 0x0 0x100000;RAM RWX 0x20000000 0x40000"

Replace the lines with the following:

linker_section_placement_macros="FLASH_PH_START=0x0;FLASH_PH_SIZE=0x100000;RAM_PH_START=0x20000000;RAM_PH_SIZE=0x40000;FLASH_START=0x26000;FLASH_SIZE=0xda000;RAM_START=0x20002a98;RAM_SIZE=0x3d568"
linker_section_placements_segments="FLASH RX 0x0 0x100000;RAM RWX 0x20000000 0x40000;ot_flash_data RX 0x000fc000 0x4000"

Modification in main.c

Make sure you apply the following changes:

  1. At the top of the main.c file, add all necessary includes, depending on the Thread use case:
    #include "thread_utils.h"
    #include <openthread/platform/platform-softdevice.h>
  2. Ensure that the application registers the SoC event handler of the SoftDevice (using the NRF_SDH_SOC_OBSERVER macro). If the handler is not already registered, add the following snippet in the BLE stack initialization function (after NRF_SDH_BLE_OBSERVER):
    // Register a handler for SOC events.
    NRF_SDH_SOC_OBSERVER(m_soc_observer, NRF_SDH_SOC_STACK_OBSERVER_PRIO, soc_evt_handler, NULL);
  3. Inside of the soc_evt_handler function, add OpenThread's SoftDevice platform handler called otSysSoftdeviceSocEvtHandler.
    /**@brief Function for handling SOC events.
    * @param[in] sys_evt SoC stack event.
    * @param[in] p_context Unused.
    */
    static void soc_evt_handler(uint32_t sys_evt, void * p_context)
    {
    UNUSED_PARAMETER(p_context);
    otSysSoftdeviceSocEvtHandler(sys_evt);
    }
  4. Add the function for initializing the Thread protocol. Note that it depends on your Thread use case. If you wish to handle Thread device state changes, you must add an appropriate handler.
    static void thread_state_changed_callback(uint32_t flags, void * p_context)
    {
    // Example specific functionality.
    }
    Initialization:
    /**@brief Function for initializing the Thread Stack
    */
    static void thread_instance_init(void)
    {
    thread_configuration_t thread_configuration =
    {
    .radio_mode = THREAD_RADIO_MODE_RX_ON_WHEN_IDLE,
    .autocommissioning = true,
    };
    thread_init(&thread_configuration);
    thread_state_changed_callback_set(thread_state_changed_callback);
    }
    For reference on how to add for example the CoAP functionality, see: <InstallFolder>\examples\multiprotocol\ble_thread\ble_thread_dyn_mtd_coap_cli\main.c.
  5. Call the thread_instance_init function inside of the application main function.
    Note
    This function must be called after the SoftDevice has been initialized.
  6. Add the following snippet inside the main loop of your application:
    // Enter main loop.
    while (true)
    {
    thread_process();
    }
    If the application enters the power_manage function, which calls the sd_app_evt_wait SoftDevice API function, modify the above snippet to:
    // Enter main loop.
    while (true)
    {
    thread_process();
    if (NRF_LOG_PROCESS() == false)
    {
    thread_sleep();
    }
    }
    Note
    If the FDS module is used to manage application flash memory, you must modify the FDS_VIRTUAL_PAGES_RESERVED value in sdk_config.h to reserve 4 flash pages for the Thread stack (for example, set to 4 for default FDS virtual page size).

Parameters of Timeslot API

Timeslot API has been tested with a set of default parameters on the nRF52840 Development Kit. These parameters are preset when the PlatformInit function is invoked.

However, in case your application needs to use a different set of parameters for the Timeslot API, the library exposes the PlatformSoftdeviceRaalConfig function to change the default Timeslot API parameters.

One of the parameters is the crystal accuracy in PPM units, which by default is set to 25 PPM. The application can use the PlatformSoftdeviceRaalConfig function to change the PPM value of the currently used crystal.

Instructions on how to select other parameters for specific applications will be available in the subsequent releases of nRF5 SDK for Thread.

Restrictions

Note that OpenThread's hardware requirements must be met in transformed examples. See Hardware support and requirements.


Documentation feedback | Developer Zone | Subscribe | Updated