Hi,
I have a Zephyr ZigBee project based on the Nordic Light Bulb example and a custom ZigBee2MQTT integration. However, I can not trigger an identification on ZigBee2MQTT. After talking with the ZigBee2MQTT guys, I think the issue is in my firmware. Do you have a working reference implementation for NCS 2.8 with ZigBee identification or can you help me figuring out where my problem is / how I can debug it? The main application is showed below:
#include <zephyr/types.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/settings/settings.h>
#include <soc.h>
#include <dk_buttons_and_leds.h>
#include <zboss_api.h>
#include <zb_nrf_platform.h>
#include <zboss_api_addons.h>
#include <zb_mem_config_med.h>
#include <zigbee/zigbee_app_utils.h>
#include <zigbee/zigbee_error_handler.h>
#include "beelight.h"
/* LED indicating that light switch successfully joind Zigbee network. */
#define ZIGBEE_NETWORK_STATE_LED DK_LED3
/* Version of the application software (1 byte). */
#define INIT_BASIC_APP_VERSION 01
/* Version of the implementation of the Zigbee stack (1 byte). */
#define INIT_BASIC_STACK_VERSION 10
/* Version of the hardware of the device (1 byte). */
#define INIT_BASIC_HW_VERSION 10
/* Manufacturer name (32 bytes). */
#define INIT_BASIC_MANUF_NAME "Kampi"
/* Model number assigned by manufacturer (32-bytes long string). */
#define INIT_BASIC_MODEL_ID "BeeLight_v1.0"
/* First 8 bytes specify the date of manufacturer of the device
* in ISO 8601 format (YYYYMMDD). The rest (8 bytes) are manufacturer specific.
*/
#define INIT_BASIC_DATE_CODE "20200329"
/* Describes the physical location of the device (16 bytes).
* May be modified during commissioning process.
*/
#define INIT_BASIC_LOCATION_DESC "Office desk"
/* Button used to enter the Bulb into the Identify mode. */
#define IDENTIFY_MODE_BUTTON DK_BTN4_MSK
/* Button to start Factory Reset. */
#define FACTORY_RESET_BUTTON IDENTIFY_MODE_BUTTON
/* Define 'bat_num' as empty in order to declare default battery set attributes. */
/* According to Table 3-17 of ZCL specification, defining 'bat_num' as 2 or 3 allows */
/* to declare battery set attributes for BATTERY2 and BATTERY3. */
#define bat_num
LOG_MODULE_REGISTER(app, LOG_LEVEL_DBG);
/** @brief Zigbee device application context storage.
*/
static device_ctx_t dev_ctx;
const struct device *const bh1750 = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(bh1750));
/** @brief Declare attribute list for Identify cluster (client).
*/
ZB_ZCL_DECLARE_IDENTIFY_CLIENT_ATTRIB_LIST(identify_client_attr_list);
/** @brief Declare attribute list for Identify cluster (server).
*/
ZB_ZCL_DECLARE_IDENTIFY_SERVER_ATTRIB_LIST(
identify_server_attr_list,
&dev_ctx.identify_attr.identify_time);
/** @brief
*/
ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT(
basic_attr_list,
&dev_ctx.basic_attr.zcl_version,
&dev_ctx.basic_attr.app_version,
&dev_ctx.basic_attr.stack_version,
&dev_ctx.basic_attr.hw_version,
dev_ctx.basic_attr.mf_name,
dev_ctx.basic_attr.model_id,
dev_ctx.basic_attr.date_code,
&dev_ctx.basic_attr.power_source,
dev_ctx.basic_attr.location_id,
&dev_ctx.basic_attr.ph_env,
dev_ctx.basic_attr.sw_ver);
/** @brief Power cluster attributes additions data.
*/
ZB_ZCL_DECLARE_POWER_CONFIG_BATTERY_ATTRIB_LIST_EXT(
power_attr_list,
&dev_ctx.power_attr.voltage,
&dev_ctx.power_attr.size,
&dev_ctx.power_attr.quantity,
&dev_ctx.power_attr.rated_voltage,
&dev_ctx.power_attr.alarm_mask,
&dev_ctx.power_attr.voltage_min_threshold,
&dev_ctx.power_attr.percent_remaining,
&dev_ctx.power_attr.voltage_threshold_1,
&dev_ctx.power_attr.voltage_threshold_2,
&dev_ctx.power_attr.voltage_threshold_3,
&dev_ctx.power_attr.percent_min_threshold,
&dev_ctx.power_attr.percent_threshold_1,
&dev_ctx.power_attr.percent_threshold_2,
&dev_ctx.power_attr.percent_threshold_3,
&dev_ctx.power_attr.alarm_state);
/** @brief Illuminance cluster attributes additions data.
*/
ZB_ZCL_DECLARE_ILLUMINANCE_MEASUREMENT_ATTRIB_LIST(
light_sensor_attr_list,
&dev_ctx.illuminance_attr.measurement_attr,
&dev_ctx.illuminance_attr.min_attr,
&dev_ctx.illuminance_attr.max_attr);
ZB_DECLARE_LIGHT_SENSOR_CLUSTER_LIST(
light_sensor_clusters,
basic_attr_list,
identify_client_attr_list,
identify_server_attr_list,
power_attr_list,
light_sensor_attr_list);
ZB_DECLARE_LIGHT_SENSOR_EP(
light_sensor_ep,
LIGHT_SENSOR_ENDPOINT,
light_sensor_clusters);
ZBOSS_DECLARE_DEVICE_CTX_1_EP(
light_sensor_ctx,
light_sensor_ep);
/** @brief Starts identifying the device.
* @param bufid Unused parameter, required by ZBOSS scheduler API
*/
static void start_identifying(zb_bufid_t bufid)
{
ZVUNUSED(bufid);
if (ZB_JOINED()) {
/* Check if endpoint is in identifying mode, if not, put desired endpoint in identifying mode. */
if (dev_ctx.identify_attr.identify_time == ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE) {
zb_ret_t zb_err_code = zb_bdb_finding_binding_target(LIGHT_SENSOR_ENDPOINT);
if (zb_err_code == RET_OK) {
LOG_INF("Enter identify mode");
} else if (zb_err_code == RET_INVALID_STATE) {
LOG_WRN("RET_INVALID_STATE - Cannot enter identify mode");
} else {
ZB_ERROR_CHECK(zb_err_code);
}
} else {
LOG_INF("Cancel identify mode");
zb_bdb_finding_binding_target_cancel();
}
} else {
LOG_WRN("Device not in a network - cannot enter identify mode");
}
}
/** @brief Callback for button events.
* @param button_state Bitmask containing the state of the buttons
* @param has_changed Bitmask containing buttons that have changed their state
*/
static void button_changed(uint32_t button_state, uint32_t has_changed)
{
if (IDENTIFY_MODE_BUTTON & has_changed) {
if (IDENTIFY_MODE_BUTTON & button_state) {
}
/* Button changed its state to pressed */
else {
/* Button changed its state to released */
if (was_factory_reset_done()) {
/* The long press was for Factory Reset */
LOG_DBG("After Factory Reset - ignore button release");
}
/* Button released before Factory Reset */
else {
/* Start identification mode */
ZB_SCHEDULE_APP_CALLBACK(start_identifying, 0);
}
}
}
check_factory_reset_button(button_state, has_changed);
}
/** @brief Function for initializing LEDs and Buttons.
*/
static void configure_gpio(void)
{
int err;
err = dk_buttons_init(button_changed);
if (err) {
LOG_ERR("Cannot init buttons (err: %d)", err);
}
err = dk_leds_init();
if (err) {
LOG_ERR("Cannot init LEDs (err: %d)", err);
}
}
/** @brief Function to toggle the identify LED.
* @param bufid Unused parameter, required by ZBOSS scheduler API
*/
static void toggle_identify_led(zb_bufid_t bufid)
{
static int blink_status;
// light_bulb_set_brightness(((++blink_status) % 2) ? (255U) : (0U));
ZB_SCHEDULE_APP_ALARM(toggle_identify_led, bufid, ZB_MILLISECONDS_TO_BEACON_INTERVAL(100));
}
/** @brief Function to handle identify notification events.
* @param bufid Unused parameter, required by ZBOSS scheduler API
*/
static void identify_cb(zb_bufid_t bufid)
{
LOG_INF("A");
if (bufid) {
/* Schedule a self-scheduling function that will toggle the LED. */
ZB_SCHEDULE_APP_CALLBACK(toggle_identify_led, bufid);
} else {
zb_ret_t zb_err_code;
/* Cancel the toggling function alarm and restore current Zigbee LED state. */
zb_err_code = ZB_SCHEDULE_APP_ALARM_CANCEL(toggle_identify_led, ZB_ALARM_ANY_PARAM);
ZVUNUSED(zb_err_code);
/*
if (dev_ctx.on_off_attr.on_off) {
light_bulb_set_brightness(dev_ctx.level_control_attr.current_level);
} else {
light_bulb_set_brightness(0U);
}*/
}
}
/** @brief Function for initializing all clusters attributes.
*/
static void clusters_attr_init(void)
{
/* Basic cluster attributes data */
dev_ctx.basic_attr.zcl_version = ZB_ZCL_VERSION;
dev_ctx.basic_attr.app_version = INIT_BASIC_APP_VERSION;
dev_ctx.basic_attr.stack_version = INIT_BASIC_STACK_VERSION;
dev_ctx.basic_attr.hw_version = INIT_BASIC_HW_VERSION;
/* Use ZB_ZCL_SET_STRING_VAL to set strings, because the first byte */
/* should contain string length without trailing zero. */
/* For example "test" string will be encoded as: */
/* [(0x4), 't', 'e', 's', 't'] */
ZB_ZCL_SET_STRING_VAL(
dev_ctx.basic_attr.mf_name,
INIT_BASIC_MANUF_NAME,
ZB_ZCL_STRING_CONST_SIZE(INIT_BASIC_MANUF_NAME)
);
ZB_ZCL_SET_STRING_VAL(
dev_ctx.basic_attr.model_id,
INIT_BASIC_MODEL_ID,
ZB_ZCL_STRING_CONST_SIZE(INIT_BASIC_MODEL_ID)
);
ZB_ZCL_SET_STRING_VAL(
dev_ctx.basic_attr.date_code,
INIT_BASIC_DATE_CODE,
ZB_ZCL_STRING_CONST_SIZE(INIT_BASIC_DATE_CODE)
);
dev_ctx.basic_attr.power_source = ZB_ZCL_BASIC_POWER_SOURCE_BATTERY;
ZB_ZCL_SET_STRING_VAL(
dev_ctx.basic_attr.location_id,
INIT_BASIC_LOCATION_DESC,
ZB_ZCL_STRING_CONST_SIZE(INIT_BASIC_LOCATION_DESC)
);
dev_ctx.basic_attr.ph_env = ZB_ZCL_BASIC_ENV_UNSPECIFIED;
/* Identify cluster attributes data. */
dev_ctx.identify_attr.identify_time = ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE;
/* Initialize the values for the Battery cluster attributes */
dev_ctx.power_attr.size = ZB_ZCL_POWER_CONFIG_BATTERY_SIZE_CR2;
dev_ctx.power_attr.voltage = 25;
dev_ctx.power_attr.percent_remaining = 50;
dev_ctx.power_attr.quantity = 1;
dev_ctx.power_attr.rated_voltage = 30;
ZB_ZCL_SET_ATTRIBUTE(
LIGHT_SENSOR_ENDPOINT,
ZB_ZCL_CLUSTER_ID_POWER_CONFIG,
ZB_ZCL_CLUSTER_SERVER_ROLE,
ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID,
(zb_uint8_t *)&dev_ctx.power_attr.percent_remaining,
ZB_FALSE
);
/* Initialize the values for the Illuminance measurement cluster attributes */
/*
dev_ctx.illuminance_min_attr = 11;
ZB_ZCL_SET_ATTRIBUTE(
LIGHT_SENSOR_ENDPOINT,
ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT,
ZB_ZCL_CLUSTER_SERVER_ROLE,
ZB_ZCL_ATTR_ILLUMINANCE_MEASUREMENT_MIN_MEASURED_VALUE_ID,
(zb_uint8_t *)&dev_ctx.illuminance_min_attr,
ZB_FALSE
);
dev_ctx.illuminance_max_attr = 200;
ZB_ZCL_SET_ATTRIBUTE(
LIGHT_SENSOR_ENDPOINT,
ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT,
ZB_ZCL_CLUSTER_SERVER_ROLE,
ZB_ZCL_ATTR_ILLUMINANCE_MEASUREMENT_MAX_MEASURED_VALUE_ID,
(zb_uint8_t *)&dev_ctx.illuminance_max_attr,
ZB_FALSE
);
*/
}
/** @brief Callback function for handling ZCL commands.
* @param bufid Reference to Zigbee stack buffer used to pass received data
*/
static void zcl_device_cb(zb_bufid_t bufid)
{
zb_uint8_t attr_id;
zb_uint8_t cluster_id;
zb_zcl_device_callback_param_t *device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);
LOG_INF("%s id %hd", __func__, device_cb_param->device_cb_id);
/* Set default response value. */
device_cb_param->status = RET_OK;
switch (device_cb_param->device_cb_id)
{
case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
{
cluster_id = device_cb_param->cb_param.set_attr_value_param.cluster_id;
attr_id = device_cb_param->cb_param.set_attr_value_param.attr_id;
break;
}
default:
{
device_cb_param->status = RET_NOT_IMPLEMENTED;
break;
}
}
LOG_INF("%s status: %hd", __func__, device_cb_param->status);
}
/** @brief Zigbee stack event handler.
* @param bufid Reference to the Zigbee stack buffer used to pass signal
*/
void zboss_signal_handler(zb_bufid_t bufid)
{
/* Update network status LED. */
zigbee_led_status_update(bufid, ZIGBEE_NETWORK_STATE_LED);
/* No application-specific behavior is required. Call default signal handler. */
ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
/* All callbacks should either reuse or free passed buffers. */
/* If bufid == 0, the buffer is invalid (not passed). */
if (bufid) {
zb_buf_free(bufid);
}
}
int main(void)
{
configure_gpio();
register_factory_reset_button(FACTORY_RESET_BUTTON);
/* Register callback for handling ZCL commands. */
ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);
/* Register dimmer switch device context (endpoints). */
ZB_AF_REGISTER_DEVICE_CTX(&light_sensor_ctx);
clusters_attr_init();
/* Register handler to identify notifications. */
ZB_AF_SET_IDENTIFY_NOTIFICATION_HANDLER(LIGHT_SENSOR_ENDPOINT, identify_cb);
/* Start Zigbee default thread. */
zigbee_enable();
LOG_INF("Light sensor application started");
return 0;
}