nRF5 SDK for Thread and Zigbee v2.0.0
Adding dynamic multiprotocol Zigbee support to BLE examples

Applications demonstrating the Bluetooth peripheral functionality can be extended by adding Zigbee protocol support to them, to achieve dynamic multiprotocol functionality.

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

Modification in sdk_config.h

Add the following settings to sdk_config.h to configure the Zigbee stack:

// <h> zigbee
//==========================================================
// <h> zb_error - ZBOSS error handling helpers
//==========================================================
// <q> ZB_ERROR_TO_STRING_ENABLED - Include functions for mapping ZBOSS errors to string
#ifndef ZB_ERROR_TO_STRING_ENABLED
#define ZB_ERROR_TO_STRING_ENABLED 0
#endif
// <q> ZB_ERROR_PRINT_TO_LOG - Print ZBOSS errors to NRF LOG
#ifndef ZB_ERROR_PRINT_TO_LOG
#define ZB_ERROR_PRINT_TO_LOG 0
#endif
// </h>
//==========================================================
// <h> zigbee_stack - ZBOSS ZigBee stack
//==========================================================
// <o> ZIGBEE_CHANNEL - 802.15.4 channel used by ZigBee <11-26>
// <i> 802.15.4 channel used by ZigBee. Defaults to 16.
#ifndef ZIGBEE_CHANNEL
#define ZIGBEE_CHANNEL 16
#endif
// <o> ZIGBEE_TRACE_LEVEL - Trace level of Zigbee stack logs. <0-4>
// <i> Trace level of Zigbee stack binary logs. Possible values: 0 - disable, 1 - error, 2 - warning, 3 - info, 4 - debug. Disabled by default.
#ifndef ZIGBEE_TRACE_LEVEL
#define ZIGBEE_TRACE_LEVEL 0
#endif
// <s> ZIGBEE_TRACE_MASK - Trace mask of Zigbee stack logs.
// <i> Selectively enable Zigbee binary trace logs. Possible values (logical or): TRACE_SUBSYSTEM_ZCL, TRACE_SUBSYSTEM_ZDO, TRACE_SUBSYSTEM_NWK. Defaults to 0x0000.
#ifndef ZIGBEE_TRACE_MASK
#define ZIGBEE_TRACE_MASK 0x0000
#endif
// <s> ZIGBEE_TIMER_INSTANCE - nRF timer instance used by Zigbee stack
#ifndef ZIGBEE_TIMER_INSTANCE
#define ZIGBEE_TIMER_INSTANCE NRF_DRV_TIMER_INSTANCE(3)
#endif
// </h>
//==========================================================
// </h>
//==========================================================

Modify these settings if they appear in sdk_config.h:

#define TIMER3_ENABLED 1
#define FDS_VIRTUAL_PAGES_RESERVED 0x30

Add the following define to the compiler options:

ZB_TRACE_LEVEL

Libraries

nRF5 SDK for Zigbee provides precompiled libraries which must be added to the project.

The default set of libraries for an Zigbee router or coordinator device is:

For the Zigbee end device the following set of libraries should be used:

Additionally one of the following defines, indicating the device type should be added to the compiler options:

There is no need to add the additional define for Zigbee coordinator device.

Modification in GCC Makefile

Add the below snippet, replacing libzboss library with the selected variant, to the makefile of a BLE application:

# Zigbee stack libraries
LIB_FILES += \
$(SDK_ROOT)/external/zboss/lib/gcc/libzboss.a \
$(SDK_ROOT)/external/zboss/lib/gcc/nrf_radio_driver_softdevice.a \

Add the below snippet to compile all platform-dependent source files, required by Zigbee stack. To do that, add the following lines inside of the SRC_FILES section:

$(SDK_ROOT)/external/zboss/zb_error/zb_error_to_string.c \
$(SDK_ROOT)/external/zboss/osif/zb_nrf52840_common.c \
$(SDK_ROOT)/external/zboss/osif/zb_nrf52840_nrf_logger.c \
$(SDK_ROOT)/external/zboss/osif/zb_nrf52840_nvram.c \
$(SDK_ROOT)/external/zboss/osif/zb_nrf52840_sdk_config_deps.c \
$(SDK_ROOT)/external/zboss/osif/zb_nrf52840_timer.c \

In order to use the Zigbee API, you need to add headers include paths. To do that, add the following lines inside of the INC_FOLDERS section:

$(SDK_ROOT)/external/zboss/osif \
$(SDK_ROOT)/external/zboss/zb_error \
$(SDK_ROOT)/external/zboss/include \
$(SDK_ROOT)/external/zboss/include/ha \
$(SDK_ROOT)/external/zboss/include/osif \
$(SDK_ROOT)/external/zboss/include/zcl \
$(SDK_ROOT)/external/zboss/include/zll \

For reference, see the makefile that already supports dynamic Zigbee/BLE: <InstallFolder>\examples\multiprotocol\ble_zigbee\ble_zigbee_dynamic_light_switch_nus\pca10056\s140\armgcc\Makefile.

Modification in IAR project

To add libraries under the IAR compiler:

  1. Click Project -> Add Files.
  2. Choose the file type of Library/Object Files.
  3. Navigate to \external\zboss\lib\iar.
  4. Select all needed libraries according to the section above.
  5. Add all platform-dependent source files, required by Zigbee stack.

To add source files under the IAR compiler:

  1. Click Project -> Add Files.
  2. Choose the file type of Source Files.
  3. Navigate to \external\zboss\osif.
  4. Select the following files:
    zb_nrf52840_common.c
    zb_nrf52840_nrf_logger.c
    zb_nrf52840_nvram.c
    zb_nrf52840_sdk_config_deps.c
    zb_nrf52840_timer.c
  5. Add the Zigbee stack error parsing module, located under \external\zboss\zb_error:
    zb_error_to_string.c

To use the Zigbee API, you must add headers include paths:

  1. Click Project -> Options.
  2. Choose the C/C++ Compiler section.
  3. In the Preprocessor tab, navigate to the Additional include directory section.
  4. Add the path to the \external\zboss\osif and the \external\zboss\include folder, with all subdirectories inside it.

After the folder path is added, depending on your project location, the following records are visible.

$PROJ_DIR$\..\..\..\..\..\..\..\..\external\zboss\zb_error
$PROJ_DIR$\..\..\..\..\..\..\..\..\external\zboss\osif
$PROJ_DIR$\..\..\..\..\..\..\..\..\external\zboss\include
$PROJ_DIR$\..\..\..\..\..\..\..\..\external\zboss\include\ha
$PROJ_DIR$\..\..\..\..\..\..\..\..\external\zboss\include\osif
$PROJ_DIR$\..\..\..\..\..\..\..\..\external\zboss\include\zcl
$PROJ_DIR$\..\..\..\..\..\..\..\..\external\zboss\include\zll

For reference, see the IAR project that already supports dynamic Zigbee/BLE: <InstallFolder>\examples\multiprotocol\ble_zigbee\ble_zigbee_dynamic_light_switch_nus\ble_zigbee_dynamic_light_switch_nus.eww

Modification in main.c

  1. At the top of the main.c file, add all necessary includes, depending on the Zigbee use case.
    #include "zboss_api.h"
    #include "zb_error_handler.h"
  2. After the section which contains defines and static variables, add the following lines:
    /**< Scan only one, predefined channel to find the coordinator. */
    #define IEEE_CHANNEL_MASK (1l << ZIGBEE_CHANNEL)
    /**< Do not erase NVRAM to save the network parameters after device reboot or power-off. */
    #define ERASE_PERSISTENT_CONFIG ZB_FALSE
    static zb_ieee_addr_t m_ieee_addr;
  3. Define all clusters and endpoints that should be present on your Zigbee device:
    /**< Source endpoint used to control light bulb. */
    #define LIGHT_SWITCH_ENDPOINT 1
    static zb_uint8_t m_attr_zcl_version = ZB_ZCL_VERSION;
    static zb_uint8_t m_attr_power_source = ZB_ZCL_BASIC_POWER_SOURCE_UNKNOWN;
    static zb_uint16_t m_attr_identify_time = 0;
    /* Declare attribute list for Basic cluster. */
    ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST(basic_attr_list, &m_attr_zcl_version, &m_attr_power_source);
    /* Declare attribute list for Identify cluster. */
    ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST(identify_attr_list, &m_attr_identify_time);
    /* Declare cluster list for Dimmer Switch device (Identify, Basic, Scenes, Groups, On Off, Level Control). */
    /* Only clusters Identify and Basic have attributes. */
    basic_attr_list,
    identify_attr_list);
    /* Declare endpoint for Dimmer Switch device. */
    LIGHT_SWITCH_ENDPOINT,
    dimmer_switch_clusters);
    /* Declare application's device context (list of registered endpoints) for Dimmer Switch device. */
    ZB_HA_DECLARE_DIMMER_SWITCH_CTX(dimmer_switch_ctx, dimmer_switch_ep);
  4. Add the function for initializing the Zigbee protocol. Note that it depends on your use case.
    /***************************************************************************************************
    * @section Initialization of Zigbee.
    */
    /**@brief Function for initializing the Zigbee Stack
    */
    static void zigbee_init(void)
    {
    uint64_t factoryAddress;
    /* Read long address from FICR. */
    factoryAddress = (uint64_t)NRF_FICR->DEVICEID[0] << 32;
    factoryAddress |= NRF_FICR->DEVICEID[1];
    memcpy(m_ieee_addr, &factoryAddress, sizeof(factoryAddress));
    /* Set ZigBee stack logging level and traffic dump subsystem. */
    ZB_SET_TRACE_LEVEL(ZIGBEE_TRACE_LEVEL);
    ZB_SET_TRACE_MASK(ZIGBEE_TRACE_MASK);
    ZB_SET_TRAF_DUMP_OFF();
    /* Initialize ZigBee stack. */
    ZB_INIT("light_switch");
    /* Set up Zigbee protocol main parameters. */
    zb_set_long_address(&m_ieee_addr);
    zb_set_network_ed_role(IEEE_CHANNEL_MASK);
    zb_set_nvram_erase_at_start(ERASE_PERSISTENT_CONFIG);
    ZB_SET_ED_TIMEOUT(ED_AGING_TIMEOUT_64MIN);
    ZB_SET_KEEPALIVE_TIMEOUT(ZB_MILLISECONDS_TO_BEACON_INTERVAL(3000));
    /* Initialize application context structure. */
    UNUSED_RETURN_VALUE(ZB_MEMSET(&m_device_ctx, 0, sizeof(light_switch_ctx_t)));
    /* Register dimmer switch device context (endpoints). */
    ZB_AF_REGISTER_DEVICE_CTX(&dimmer_switch_ctx);
    /** Start Zigbee Stack. */
    zb_err_code = zboss_start();
    ZB_ERROR_CHECK(zb_err_code);
    }
    For reference on how to add the dimmable switch functionality (for example) see: <InstallFolder>\examples\multiprotocol\ble_zigbee\ble_zigbee_dynamic_light_switch_nus\main.c
  5. Add the function for handling Zigbee events:
    /**@brief ZigBee stack event handler.
    * @param[in] param Reference to ZigBee stack buffer used to pass arguments (signal).
    */
    {
    zb_zdo_app_signal_hdr_t * p_sg_p = NULL;
    switch(sig)
    {
    if (status == RET_OK)
    {
    NRF_LOG_INFO("Joined network successfully");
    }
    else
    {
    NRF_LOG_ERROR("Failed to join network. Status: %d", status);
    }
    break;
    if (status != RET_OK)
    {
    NRF_LOG_WARNING("Production config is not present or invalid");
    }
    break;
    default:
    /* Unhandled signal. For more information see: zb_zdo_app_signal_type_e and zb_ret_e */
    NRF_LOG_INFO("Unhandled signal %d. Status: %d", sig, status);
    }
    if (param)
    {
    ZB_FREE_BUF_BY_REF(param);
    }
    }
  6. Call the zigbee_init function inside of the application main function.
    Note
    This function must be called after the SoftDevice has been initialized.
  7. Add the following snippet inside the main loop of your application:

For more information about defining and implementing Zigbee device, see Programming principles.

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 802.15.4 radio driver is initialized.

However, in case your application needs to use a different set of parameters for the Timeslot API, the 802.15.4 radio driver exposes the nrf_raal_softdevice_config() 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 may use the nrf_raal_softdevice_config() 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 and Zigbee.

Restrictions

Note that Zigbee's hardware requirements must be met in transformed examples. Refer to the Zigbee hardware requirements page.


Documentation feedback | Developer Zone | Subscribe | Updated