nRF5 SDK for Thread and Zigbee v2.0.0
Programming principles

Table of Contents

The Zigbee SDK provides the following services for the application developer:

Application structure

The SDK provides header files and libraries for C programming language. This means that applications must be implemented using C or C++.

The typical application source must:

Zigbee type definitions

The stack uses a set of platform-independent type definitions such as:

The framework includes a set of platform-independent type definitions such as common C types (char, integer, void etc.), and specific types for core and Zigbee functionality.

All ZBOSS type names start with the prefix "zb_".

Zigbee stack header files

The application must include the header file zboss_api.h, and include it before any other ZBOSS header files. This file contains most of the required routines to start the application.

Zigbee stack multitasking (scheduler)

The stack uses a cooperative multitasking model. The main component that provides multitasking capabilities is the scheduler.

Callbacks

There is no "task" abstraction in the scheduler; it uses "callback" instead. The callback has to match one of the following types:

Running a task in the scheduler simply executes a callback function.

Each callback function can schedule one or more callback functions for execution through the scheduler. The scheduled callbacks are stored in the internal scheduler queue. This queue is handled in the scheduler main loop: the callbacks are executed one-by-one in FIFO order. If there is currently no callback to execute, the scheduler can put the device into sleep mode (if so configured).

Callbacks are scheduled for execution with:

A common practice is to specify a buffer reference as the first (or as the single) parameter for the callback. Scheduling a callback does not block the currently running function, as it is a fast non-blocking operation. More than one callback can be scheduled in succession. If there is no other callback in the queue, the scheduled callback is executed immediately after the current callback is finished.

Timer alarms

The timer functionality uses the scheduler infrastructure and is implemented with alarms. An alarm is a callback that is scheduled for execution with a defined time delay. The time delay is specified during the scheduling of the alarm and guarantees that the alarm callback function is called not earlier than specified by the delay parameter.

Note
Precise timing is not guaranteed: the alarm callback can be called later than the defined time delay.

There are API primitives provided to work with the alarms:

Scheduler API

The following table lists the Scheduler API macros:

Macro Parameters Return value Description
ZB_SCHEDULE_CALLBACK(func, param) zb_callback_t func – function to execute.
zb_uint8_t param – callback parameter – usually a reference to a memory buffer.
RET_OK or error code. Schedule a callback function with one parameter for execution.
ZB_SCHEDULE_CALLBACK2(func, param, user_param) zb_callback_t func – function to execute.
zb_uint8_t param – callback parameter.
zb_uint16_t user_param – user parameter.
RET_OK or error code. Schedule a callback function with two parameters for execution.
ZB_SCHEDULE_ALARM(func, param, timeout_bi) zb_callback_t func – function to execute.
zb_uint8_t param – callback parameter.
zb_time_t timeout_bi – timeout, in beacon intervals.
RET_OK or error code. Schedule an alarm for execution in the main scheduler loop: a callback with one parameter to be executed after a delay defined by the timeout value.
ZB_SCHEDULE_ALARM_CANCEL(func, param) zb_callback_t func – scheduled function to cancel.
zb_uint8_t param – callback parameter.
RET_OK or error code. Cancel previously scheduled alarm.
ZB_SCHEDULE_ALARM_CANCEL_AND_GET_BUF(func, param, p_param) zb_callback_t func – scheduled function to cancel.
zb_uint8_t param – callback parameter.
zb_uint8_t p_param – returned parameter.
RET_OK or error code.Cancel previously scheduled alarm and return a parameter defined while scheduling. This API is used to prevent buffer leak.
ZB_SCHEDULE_GET_ALARM_TIME(func, param, timeout_bi) zb_callback_t func – function to execute.
zb_uint8_t param – callback parameter
zb_time_t timeout_bi – time left for an execution, in beacon intervals.
RET_OK or error code. Check whether an alarm was scheduled and return time left for the callback execution.
Scheduler API calls
Note
Except for the scheduler API, the Zigbee stack API is not thread-safe. This means that calls to the Zigbee API must be invoked through scheduler callbacks or from the same thread that runs the scheduler loop.

The following example demonstrates the usage of Zigbee multitasking with two functions executing each other in an infinite loop. The first function executes immediately. The second function executes after a 10-second delay.

void func1(zb_uint8_t param)
{
ZB_SCHEDULE_CALLBACK(func2, param);
}
void func2(zb_uint8_t param)
{
ZB_SCHEDULE_ALARM (func1, param, 10 * ZB_TIME_ONE_SECOND));
}
void main()
{
func1(1);
ZBOSS_INIT();
while(1)
{
}
}

Zigbee stack memory management subsystem

The Zigbee stack uses static memory allocation for its internal data structures (the memory is not allocated dynamically at runtime). The memory management system provides predictable memory usage and minimizes data copying at runtime when possible. Internal stack data structures are usually invisible to the application.

The main data structure visible for the application is the memory buffer, which transfers packets and parameters between the application and stack layers.

The stack uses a predefined pool of memory buffers of a fixed size. A memory buffer consists of a header and a data buffer. The memory buffer size is enough to hold a single Zigbee packet or an APS fragment. When the APS fragmentation is required on rare occasions, the stack uses memory buffer chaining.

Note
APS fragmentation is an optional feature of ZBOSS.

The application or the stack function can allocate or release a single memory buffer from the pool on demand. Memory buffer allocation is a blocking operation: ZBOSS kernel calls the user’s callback when the buffer becomes available.

The application must use ZB_GET_OUT_BUF_DELAYED() or ZB_GET_IN_BUF_DELAYED() calls to allocate a new buffer. When a buffer becomes available, the delayed buffer allocation makes a callback call through the scheduler.

Logically, the memory buffers are divided into two types: in and out buffers, for incoming and outgoing packets respectively. This separation prevents allocating all the memory buffers to only one type of buffer. For example, if all buffers are only TX buffers and all are in use waiting for an ACK packet, no ACK packet will be received because no RX buffer is available. To prevent this situation, the stack stops allocating TX buffers when half of the buffer pool is allocated to TX buffers. The same rule applies for allocating RX buffers.

Most of the stack functions require some parameters and some data (usually Zigbee packet or its fragment). Both parameters and data are passed to the API in a single memory buffer. Call parameters are stored in the tail of the buffer, while the data section is stored at the head of the buffer. Both the caller and the called functions must be aware of the parameter types.

There are two methods for setting parameters and getting them from the buffer, respectively:

A recommended practice is to use buffer references instead of zb_buf_t* buffer pointers. The buffer reference is a single byte variable that holds the buffer ID.

The stack provides two helpers to get either the buffer by its reference or the reference of a particular buffer, respectively:

The following buffer operations are available in a Zigbee application:

To reserve a space in the memory buffer, use the following methods:

The following table lists the available memory management API macros.

Macro Parameters Return value Description
ZB_BUF_FROM_REF(ref) zb_uint8_t ref – memory buffer reference. zb_buf_t *buf – memory buffer. Get a pointer to the memory buffer using its reference.
ZB_REF_FROM_BUF(buf) zb_buf_t *buf – memory buffer. zb_uint8_t ref – buffer reference. Get a reference (index) to the memory buffer.
ZB_BUF_BEGIN(buf) zb_buf_t *buf – memory buffer. zb_uint8_t *data – data begin. Get a pointer to the first allocated data element in the data buffer.
ZB_BUF_LEN(buf) zb_buf_t *buf – memory buffer. zb_uint8_t length – data length. Get the allocated data size.
ZB_BUF_INITIAL_ALLOC(buf, size, ptr) zb_buf_t *buf – memory buffer.
zb_uint8_t size – size to allocate.
zb_void_t *ptr – (out) pointer to the allocated data buffer.
None. Allocate a buffer in the memory buffer. If possible, the buffer is allocated starting with an offset in the data buffer. The existing buffer content is lost.
ZB_BUF_ALLOC_LEFT(buf, size, ptr) zb_buf_t *buf – memory buffer.
zb_uint8_t size – size to allocate.
zb_void_t *ptr – (out) pointer to the allocated buffer.
None. Extend the currently allocated buffer to the left. Commonly used for allocating a buffer for a packet header.
ZB_BUF_ALLOC_RIGHT(buf, size, ptr) zb_buf_t *buf – memory buffer.
zb_uint8_t size – size to allocate.
zb_void_t *ptr – (out) pointer to the allocated buffer.
None. Extend the currently allocated buffer to the right. Commonly used for allocating a buffer for a packet tail.
ZB_BUF_CUT_LEFT(buf, size, ptr) zb_buf_t *buf – memory buffer.
zb_uint8_t size – size to cut.
zb_void_t *ptr – (out) pointer to new buffer beginning.
None. Cut a buffer beginning. It is normally used for cutting a packet header.
ZB_BUF_CUT_RIGHT(buf, size) zb_buf_t *buf – memory buffer.
zb_uint8_t size – size to cut.
None. Cut a buffer ending. It is usually used for cutting a packet tail.
ZB_GET_BUF_PARAM(buf, type) zb_buf_t *buf – memory buffer.
type – requested parameter type.
type *ptr – pointer to the parameters. Get a pointer to the structured parameters. The parameters are stored at the tail of the data buffer.
ZB_SET_BUF_PARAM_PTR(buf, param, type) zb_buf_t *buf – memory buffer.
zb_void_t *param – pointer to the user data.
type – parameter type.
None.Store the structured parameters. The parameters are stored at the tail of the data buffer.
ZB_BUF_REUSE(buf) zb_buf_t *buf – memory buffer. None. Reuse a buffer, discard data, re-initialize internals of the memory buffer.
ZB_FREE_BUF(buf) zb_buf_t *buf – memory buffer. None. Release a memory buffer.
ZB_GET_IN_BUF() None. zb_buf_t *buf – memory buffer. Get an IN buffer from the buffer pool. If no buffer is available, return NULL.
ZB_GET_OUT_BUF() None. zb_buf_t *buf – memory buffer. Get an OUT buffer from the buffer pool. If no buffer is available, return NULL.
ZB_GET_IN_BUF_DELAYED(callback) zb_callback_t callback – pointer to a callback function. RET_OK or error code. Get an IN buffer from the buffer pool; call the specified callback when the buffer is available.
If the buffer is available, schedule the callback for execution immediately. Otherwise, wait until a buffer is available and execute the callback then.
ZB_GET_OUT_BUF_DELAYED(callback) zb_callback_t callback – pointer to a callback function. RET_OK or error code. Get an OUT buffer from the buffer pool; call the specified callback when the buffer is available.
If the buffer is available, schedule the callback for execution immediately. Otherwise, wait until a buffer is available and execute the callback then.
ZB_GET_OUT_BUF_DELAYED2(callback, user_param) zb_callback2_t callback – pointer to a callback function.
zb_uint16_t user_param – to be passed to callback.
RET_OK or error code Get OUT buffer from the buffer pool. The behavior is the same with ZB_GET_OUT_BUF_DELAYED(), but a user parameter is passed to the callback as the second argument alongside the allocated buffer.
Memory management API calls

Time subsystem

The time subsystem is based on the hardware timer. It uses the special type zb_time_t defined in the stack API. The time value is stored in ticks. Each tick is equal to one "beacon interval" (15.36 ms). The size of the zb_time_t type depends on the platform. The overflow is handled by the Time API.

The time API provides the ability to:

The following table lists the available time API macros.

Macro Parameters Return value Description
ZB_TIMER_GET() None. zb_time_t time Get current timer value in beacon intervals.
ZB_TIME_SUBTRACT(a, b) zb_time_t a – time value.
zb_time_t b – time value to subtract.
zb_time_t time – subtraction result. Subtract time value: a - b.
ZB_TIME_ADD(a, b) zb_time_t a – time value.
zb_time_t b – time value.
zb_ret_t time – addition result. Add time values: a + b.
ZB_TIME_GE(a, b) zb_callback_t func – first time value to compare.
zb_uint8_t param – second time value to compare.
ZB_TRUE if ab, ZB_FALSE otherwise. Compare time value a and value b, check if ab.
ZB_TIME_ONE_SECOND None. zb_time_t time – one second timeout. Constant: one second in beacon intervals.
ZB_MILLISECONDS_TO_BEACON_INTERVAL(ms) zb_uint16_t ms – number of milliseconds to convert. zb_time_t time – timeout in ms. Convert time from milliseconds to beacon intervals.
Time API calls

Debugging

The Zigbee stack provides tracing capabilities for customer support purposes. The stack outputs a hex-encoded binary trace through the Logger module. The binary trace log cannot be decoded with the provided SDK, but it is a very powerful tool for finding stack-level issues by Nordic's support team.

You can enable or disable tracing for a particular subsystem, and define the level of tracing. In addition, the stack is able to trace Zigbee input/output traffic (in/out) on the MAC level.

The trace mask enables or disables trace messages for the whole subsystem:

Macro Value Description
TRACE_SUBSYSTEM_APP 0x0800 Application.
TRACE_SUBSYSTEM_ZCL 0x0100 ZCL subsystem.
TRACE_SUBSYSTEM_ZDO 0x0040 ZDO subsystem.
TRACE_SUBSYSTEM_NWK 0x0008 NWK subsystem.
Trace masks

Tracing is controlled by the trace level (1-4) and the trace mask. The trace level defines the importance of the message: 1 is the most important, 4 is the least important. Setting the trace level to 1 blocks all the messages except those with the highest priority. If an application sets the trace level to 4, all trace messages are allowed to print.

The provided tracing subsystem is disabled by default. Use the following tracing API to manage tracing: enable and disable subsystems, set trace level, etc.

Macro Parameters Return value Description
ZB_SET_TRACE_LEVEL(level) zb_uint8_t level – trace level. None. Set trace level.
ZB_SET_TRACE_MASK(mask) zb_uint8_t mask – trace mask. None. Set trace mask.
ZB_SET_TRACE_ON() None. None. Enable trace.
ZB_SET_TRACE_OFF() None. None. Disable trace.
ZB_SET_TRAF_DUMP_ON() None. None. Enable dump.
ZB_SET_TRAF_DUMP_OFF() None. None. Disable dump.
Trace API calls

Power optimization

By default, the Zigbee examples are power optimized. This means that in the time of inactivity the CPU enters the low power mode. The Sleepy End devices use the signal feature to know when to sleep (see Power saving for ZED), while the other devices enter the low power mode whenever the stack decides it has nothing more to do. The weak function zb_void_t zb_osif_go_idle(zb_void_t) is defined to check if the logger has anything to process. The CPU goes idle if nothing is left to process.

If some other checks are to be performed (or the behavior must be changed in any way), you can redefine this function in the application. Check the example in the Light Bulb device (see Zigbee Light Control example).

Production configuration

Zigbee stack includes a production configuration block feature. This block is useful for per-device customization and is supposed to be written at device production time and never changed after that.

The production configuration block:

The production configuration block has a system part and an optional application-specific part:

The Zigbee stack applies the system part internally, and the application can handle its specific part by itself:

if (status != RET_OK)
{
NRF_LOG_WARNING("Production config is not present or invalid");
}
else
{
zb_uint32_t app_data_length = ZB_BUF_LEN(ZB_BUF_FROM_REF(param)) - sizeof(zb_zdo_app_signal_hdr_t);
if (app_data_length != 0)
{
NRF_LOG_INFO("Application configuration data received; length: %d", app_data_length);
example_application_config_t * ex_cfg = ZB_ZDO_SIGNAL_GET_PARAMS(p_sg_p, example_application_config_t);
process_example_application_config(ex_cfg);
}
NRF_LOG_INFO("Applied Production config");
}
break;

Data that can be placed into the system part of the production configuration block includes:

Moreover, data specific to your application can be placed in the application part of the production configuration block.

You can generate the Production Config using the production config generator. For details please refer to readme.txt in <InstallFolder>\external tools\zigbee_production_config_generator.


Documentation feedback | Developer Zone | Subscribe | Updated