Zigbee Application Framework Callback Interface#
The Zigbee Application Framework callbacks are intended to be used as a means to remove all customer code from the Zigbee Application Framework. Generally, when a callback is called, the Zigbee Application Framework gives the application code a first opportunity at an incoming message or requesting some piece of application data. Within the callback API, some callbacks return a Boolean value indicating that the message has been handled and no further processing will be done. If your application is doing something that conflicts with the Zigbee Application Framework’s handling of a particular message, return TRUE to indicate that the message was handled. This ensures that the Zigbee Application Framework does not interfere with your application’s handling of the message.
Implementing Callbacks#
A callback is a C function that gets invoked by the Application Framework when certain conditions are met. A basic weak implementation of all callbacks is provided by the Application Framework. The application can override each of these weak definitions by simply adding a callback implementation to some application-level source file. For example, you may create my_callbacks.c file by clicking New in Simplicity Studio's Project Explorer focused on a given project, add a callback implementation to this file, and save it. The file automatically becomes a compilation unit in the project and the callback implementation will be compiled and linked during subsequent builds.
Callback Types#
The Zigbee Application Framework utilizes the following callback types:
ZCL command handling callbacks
Stack Callbacks
The following figure illustrates an example of Zigbee Application Framework callback types.
The above figure illustrates a project where:
ZCL Basic Cluster implementation is provided by the SDK, which uses runtime subscription mechanism to subscribe the sl_zigbee_af_basic_cluster_server_command_parse function to the Basic Cluster ZCL commands (server side).
ZCL Thermostat Cluster is not implemented in the GSDK but is implemented in the application. The application uses a runtime subscription mechanism to subscribe myThermostatClusterCommandHandler to the Thermostat Cluster ZCL commands as described in section 5.4.
Application implements a component-specific callback
sl_zigbee_af_basic_cluster_reset_to_factory_defaults_cb
by strongly redefining it.Application implements a global callback
sl_zigbee_af_attribute_read_access_cb
by strongly redefining it.
Global Callbacks#
All global commands fall into this category. The Zigbee Application Framework contains handling code for global ZCL commands. If any global command callback returns TRUE, this indicates that the command has been handled by the application and no further command handling should take place. If the callback returns FALSE, then the Zigbee Application Framework continues to process the command normally.
You can find a full list of Global callbacks at https://docs.silabs.com/zigbee/latest/zigbee-af-api/global-callback.
Global Callback Example#
The pre-command received callback (sl_zigbee_af_pre_command_received_cb(sl_zigbee_af_cluster_command_t)
* cmd) is called after a ZCL command has been received but has not yet been processed by the Zigbee Application Framework’s command handling code. The command is parsed into a useful struct sl_zigbee_af_cluster_command_t
that provides an easy way to access relevant data about the command including its sl_zigbee_aps_frame_t
, message type, source, buffer, length, and any relevant flags for the command. This callback also returns a Boolean value indicating if the command has been handled. If the callback returns TRUE, then it is assumed that the command has been handled by the application and no further action is taken.
ZCL Command Handling Callbacks#
The Zigbee Application Framework provides a runtime mechanism that enables the application to subscribe to ZCL commands related to a certain ZCL cluster. Specifically:
sl_status_t sl_zigbee_subscribe_to_zcl_commands(uint16_t cluster_id, uint16_t manufacturer_id, uint8_t direction, sl_service_function_t service_function)
This API enables the application to subscribe to incoming ZCL commands related to a specific cluster ID, a specific manufacturer ID (if not present, simply pass 0xFFFF), a direction (either ZCL_DIRECTION_CLIENT_TO_SERVER
or ZCL_DIRECTION_SERVER_TO_CLIENT
) and the actual callback to be invoked whenever a matching ZCL command is received. Such a callback must return a value of SL_ZIGBEE_ZCL_STATUS_SUCCESS
if the command was handled, and a value of SL_ZIGBEE_ZCL_STATUS_UNSUP_COMMAND
if the callback did not handle the passed command.
Below is an example of a runtime-subscribed ZCL command callback that handles the “stop” command for the “level control” cluster:
uint32_t my_command_handler(sl_service_opcode_t opcode, sl_service_function_context_t *context) {
assert(opcode == SL_SERVICE_FUNCTION_TYPE_ZCL_COMMAND);
sl_zigbee_af_cluster_command_t *cmd = (sl_zigbee_af_cluster_command_t *)context->data;
switch (cmd->commandId) {
case ZCL_STOP_COMMAND_ID: {
// ZCL command structs and parsing functions are implemented in the
// generated zap-cluster-command-parser.[c,h] files.
sl_zcl_level_control_cluster_stop_command_t cmd_data;
if (zcl_decode_level_control_cluster_stop_command(cmd, &cmd_data) != SL_ZIGBEE_ZCL_STATUS_SUCCESS) {
return SL_ZIGBEE_ZCL_STATUS_UNSUP_COMMAND;
}
// Handle the command here
// Send a default response back to the client
sl_zigbee_af_send_default_response(cmd, SL_ZIGBEE_ZCL_STATUS_SUCCESS);
// handle the command here
return SL_ZIGBEE_ZCL_STATUS_SUCCESS;
}
}
return SL_ZIGBEE_ZCL_STATUS_UNSUP_COMMAND;
}
The application must subscribe to the Level Control cluster commands (server side), by invoking the following, typically at initialization time:
void app_init(void)
{
sl_zigbee_subscribe_to_zcl_commands(ZCL_LEVEL_CONTROL_CLUSTER_ID,0xFFFF,ZCL_DIRECTION_SERVER_TO_CLIENT,my_command_handler);
}
Notice that in the previous example, the application chose to implement a single command and used the ZCL command parsing functions that are generated in the zap-cluster-command-parser.[c,h]. When such a command is received (and handled), the callback returns SL_ZIGBEE_ZCL_STATUS_SUCCESS
while, when other commands belonging to the same cluster are received, the callback returns SL_ZIGBEE_ZCL_STATUS_UNSUP_COMMAND
.
Finally, multiple software modules can subscribe to the same cluster, though every command belonging to a ZCL cluster should be handled by a single software module. If a cluster is not enabled in the ZCL Configurator tool, the application framework responds with a default response with status SL_ZIGBEE_ZCL_STATUS_UNSUPPORTED_CLUSTER
. If the cluster is enabled in the ZCL Configurator tool, but no software module is handling the command, the application framework responds with status SL_ZIGBEE_ZCL_STATUS_UNSUP_COMMAND
.
Component-Specific Callbacks#
Callbacks of this type are calls into the application code from the internal component logic. The full list of component-specific callbacks can be found at Component Specific Callbacks. Additionally, a link to the list of callbacks offered by each component appears in the component’s tile in the Project Configurator. You can implement these callbacks as described in Implementing Callbacks.
For example, the Basic Server Cluster software component exposes the sl_zigbee_af_basic_cluster_reset_to_factory_defaults_cb
callback (as displayed in the Zigbee Cluster Library Common component), which you may choose to implement by simply adding an implementation of this function to one of the C files in the project.
Stack Callbacks#
The Application Framework also provides stack-level callbacks that enable the application to react to stack-specific events. Typically, these stack callbacks match the stack handlers provided by the Zigbee stack libraries that are consumed by the Application Framework. For example:
void sl_zigbee_af_stack_status_cb(sl_status_t status)
void sl_zigbee_af_energy_scan_result_cb(uint8_t channel,int8_t maxRssiValue)
void sl_zigbee_af_network_found_cb(sl_zigbee_zigbee_network_t *networkFound, uint8_t lqi, int8_t rssi)
and so on.
A full list of these stack callbacks can be found at Stack Callbacks.
Callback Flow for Message Processing#
The following figure shows how a message received by the Zigbee Application Framework’s implementation of sl_zigbee_incomming_message_handler
is processed and flows through the framework code and out to the application implemented callbacks.
Once the incoming message is determined to be an incoming global command, it is passed off to the global command handling for processing, as shown in the following figure.
Otherwise, if it is found to be a cluster-specific command, it is passed off to the cluster-specific command processing.
Command Callbacks Implementation Notes#
Command Callback Context#
All command-related callbacks are called from within the context of the sl_zigbee_incomming_message_handler
. This means that Zigbee APIs that are available to the application within that context are available within the command handling callbacks as well. These APIs are listed in the stack API file located at stack/include/message.h. The stack APIs that are available in the command callbacks are listed in the stack message header located at stack/include/message.h and include:
sl_zigbee_send_reply()
sl_zigbee_set_reply_binding()
sl_zigbee_note_senders_binding()
Array Handling in Command Callbacks#
Any Zigbee message that contains an array of arguments is passed as an int8u* pointer to the beginning of the array. This is done even when the Zigbee Application Framework knows that the arguments in the array may be of another type, such as an uint16_t or uint32_t, because of byte alignment issues on the various processors on which the Zigbee Application Framework may run. Developers implementing the callback must parse the array and cast its elements appropriately for their hardware.