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; }