Greetings.
I am trying to implement a custom cluster with attribute reporting, however, for some reason, i am unable to start reporting of such attributes.
I have started by copying the Template sample and modifying it in the following ways:
- Created the following header files in a new folder (/inc):
zb_zcl_shake_tracker.h
andzb_ha_shaker.h
, the former being my custom cluster and the latter my custom endpoint.
The custom cluster has been defined following the guide for declaring custom cluster and using the Basic cluster as a guideline, whereas the custom endpoint was made by using the Template sample's Range Extender endpoint as a guideline - Created
zb_zcl_shake_tracker.c
in /src as the Basic cluster i used as a guide for the custom cluster had two functions declared (init_server
andinit_client
), in my .c file. These functions are defined but do nothing at all as i wasn't sure whether or not such functions were of any use in this case. - Modified the main.c to add my custom cluster attributes to the device context structure, declare the attribute list, declare the cluster list of my custom endpoint, declare my custom endpoint and custom device context and finally add a infinite loop to toggle one of my custom attributes once every 2 seconds for testing purposes.
- Modified the CMakeList appropriately
Building the application didn't cause any issue and flashing the firmware on a testing board showed that it was working perfectly, with another board running the Shell sample in a coordinator role successfully allowing me to read the attribute via zcl attr read
, using the zdo bind on
command also returned without error, however, using the subscribe on command returned me with an error message: Error: Unable to configure attribute 1 reporting. Status: 1
Trying the same with the On/Off attribute of the On/Off cluster of the Light Bulb sample however returned no such error and the reporting was working appropriately.
At this point i still do not understand what i am doing wrong or how can i fix this, especially as other seem to have had success in implementing their custom clusters with attribute reporting.
Follows the source code of the various files:
main.c
/* * Copyright (c) 2021 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ /** @file * * @brief Zigbee application template. */ #include <zephyr.h> #include <logging/log.h> #include <dk_buttons_and_leds.h> #include <device.h> #include <drivers/sensor.h> #include <zboss_api.h> #include <zigbee/zigbee_error_handler.h> #include <zigbee/zigbee_app_utils.h> #include <zb_nrf_platform.h> #include <zb_zcl_shake_tracker.h> #include <zb_ha_shaker.h> /* Device endpoint, used to receive ZCL commands. */ #define APP_TEMPLATE_ENDPOINT 10 /* Type of power sources available for the device. * For possible values see section 3.2.2.2.8 of ZCL specification. */ #define TEMPLATE_INIT_BASIC_POWER_SOURCE ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE /* LED indicating that device successfully joined Zigbee network. */ #define ZIGBEE_NETWORK_STATE_LED DK_LED3 /* LED used for device identification. */ #define IDENTIFY_LED DK_LED4 /* Button used to enter the Identify mode. */ #define IDENTIFY_MODE_BUTTON DK_BTN4_MSK LOG_MODULE_REGISTER(app, LOG_LEVEL_INF); /* Main application customizable context. * Stores all settings and static values. */ struct zb_device_ctx { zb_zcl_basic_attrs_t basic_attr; zb_zcl_identify_attrs_t identify_attr; zb_zcl_shake_tracker_attrs_t shake_attr; }; /* Zigbee device application context storage. */ static struct zb_device_ctx dev_ctx; ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST( identify_attr_list, &dev_ctx.identify_attr.identify_time); ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST( basic_attr_list, &dev_ctx.basic_attr.zcl_version, &dev_ctx.basic_attr.power_source); ZB_ZCL_DECLARE_SHAKE_TRACKER_ATTRIB_LIST( shake_attribute_list, &dev_ctx.shake_attr.data, &dev_ctx.shake_attr.is_shaked); ZB_HA_DECLARE_SHAKER_CLUSTER_LIST( app_template_clusters, basic_attr_list, identify_attr_list, shake_attribute_list); ZB_HA_DECLARE_SHAKER_EP( app_template_ep, APP_TEMPLATE_ENDPOINT, app_template_clusters); ZB_HA_DECLARE_SHAKER_CTX( app_template_ctx, app_template_ep); /**@brief Function for initializing all clusters attributes. */ static void app_clusters_attr_init(void) { /* Basic cluster attributes data */ dev_ctx.basic_attr.zcl_version = ZB_ZCL_VERSION; dev_ctx.basic_attr.power_source = TEMPLATE_INIT_BASIC_POWER_SOURCE; /* Identify cluster attributes data. */ dev_ctx.identify_attr.identify_time = ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE; /*Shake cluster attributes data*/ ZB_ZCL_SET_STRING_VAL(dev_ctx.shake_attr.data, ZB_ZCL_SHAKE_TRACKER_DATA_DEFAULT_VALUE, strlen(ZB_ZCL_SHAKE_TRACKER_DATA_DEFAULT_VALUE)+1); dev_ctx.shake_attr.is_shaked = ZB_ZCL_SHAKE_TRACKER_IS_SHAKED_DEFAULT_VALUE; } /**@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; dk_set_led(IDENTIFY_LED, (++blink_status) % 2); ZB_SCHEDULE_APP_ALARM(toggle_identify_led, bufid, ZB_MILLISECONDS_TO_BEACON_INTERVAL(100)); } /**@brief Function to handle identify notification events on the first endpoint. * * @param bufid Unused parameter, required by ZBOSS scheduler API. */ static void identify_cb(zb_bufid_t bufid) { zb_ret_t zb_err_code; if (bufid) { /* Schedule a self-scheduling function that will toggle the LED */ ZB_SCHEDULE_APP_CALLBACK(toggle_identify_led, bufid); } else { /* Cancel the toggling function alarm and turn off LED */ zb_err_code = ZB_SCHEDULE_APP_ALARM_CANCEL(toggle_identify_led, ZB_ALARM_ANY_PARAM); ZVUNUSED(zb_err_code); dk_set_led(IDENTIFY_LED, 0); } } /**@breif Starts identifying the device. * * @param bufid Unused parameter, required by ZBOSS scheduler API. */ static void start_identifying(zb_bufid_t bufid) { zb_ret_t zb_err_code; ZVUNUSED(bufid); /* 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) { LOG_INF("Enter identify mode"); zb_err_code = zb_bdb_finding_binding_target( APP_TEMPLATE_ENDPOINT); ZB_ERROR_CHECK(zb_err_code); } else { LOG_INF("Cancel identify mode"); zb_bdb_finding_binding_target_cancel(); } } /**@brief Callback for button events. * * @param[in] button_state Bitmask containing buttons state. * @param[in] has_changed Bitmask containing buttons * that have changed their state. */ static void button_changed(uint32_t button_state, uint32_t has_changed) { /* Calculate bitmask of buttons that are pressed * and have changed their state. */ uint32_t buttons = button_state & has_changed; if (buttons & IDENTIFY_MODE_BUTTON) { ZB_SCHEDULE_APP_CALLBACK(start_identifying, 0); } } /**@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 Zigbee stack event handler. * * @param[in] 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); } } void main(void) { LOG_INF("Starting Zigbee application template example"); /* Initialize */ configure_gpio(); /* Register device context (endpoints). */ ZB_AF_REGISTER_DEVICE_CTX(&app_template_ctx); app_clusters_attr_init(); /* Register handlers to identify notifications */ ZB_AF_SET_IDENTIFY_NOTIFICATION_HANDLER(APP_TEMPLATE_ENDPOINT, identify_cb); /* Start Zigbee default thread */ zigbee_enable(); LOG_INF("Zigbee application template started"); zb_bool_t current_value=dev_ctx.shake_attr.is_shaked; printk("First value for is_shaked is %d \n",current_value); while(1){ k_sleep(K_MSEC(2000)); printk("is_shaked attribute was %d now setting it to",dev_ctx.shake_attr.is_shaked); current_value ^=0x01; ZB_ZCL_SET_ATTRIBUTE(10, ZB_ZCL_CLUSTER_ID_SHAKE_TRACKER,ZB_ZCL_CLUSTER_SERVER_ROLE,ZB_ZCL_ATTR_SHAKE_TRACKER_IS_SHAKED, (zb_uint8_t *)¤t_value,ZB_FALSE); printk(" %d\n", dev_ctx.shake_attr.is_shaked); } }
zb_zcl_shake_tracker.h
#ifndef ZB_ZCL_SHAKE_TRACKER_H #define ZB_ZCL_SHAKE_TRACKER_H 1 #include "zcl/zb_zcl_common.h" #include "zcl/zb_zcl_commands.h" enum zb_zcl_shake_tracker_info_attr_e{ ZB_ZCL_ATTR_SHAKE_TRACKER_DATA =0x0000, ZB_ZCL_ATTR_SHAKE_TRACKER_IS_SHAKED =0x0001 }; enum zb_zcl_is_shaked_is_shaked_e{ ZB_ZCL_IS_SHAKED_IS_NOT_SHAKED = 0, ZB_ZCL_IS_SHAKED_IS_SHAKED = 1 }; #define ZB_ZCL_SHAKE_TRACKER_DATA_DEFAULT_VALUE "Hello"//{'H', 'e', 'l', 'l','o','\0'} #define ZB_ZCL_SHAKE_TRACKER_DATA_SIZE sizeof(ZB_ZCL_SHAKE_TRACKER_DATA_DEFAULT_VALUE) #define ZB_ZCL_SHAKE_TRACKER_IS_SHAKED_DEFAULT_VALUE (ZB_ZCL_IS_SHAKED_IS_NOT_SHAKED) #define ZB_ZCL_CLUSTER_ID_SHAKE_TRACKER 0xABCDU #define ZB_ZCL_DECLARE_SHAKE_TRACKER_ATTRIB_LIST(attr_list, data, is_shaked) \ ZB_ZCL_START_DECLARE_ATTRIB_LIST(attr_list) \ ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_SHAKE_TRACKER_DATA, (data)) \ ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_SHAKE_TRACKER_IS_SHAKED, (is_shaked)) \ ZB_ZCL_FINISH_DECLARE_ATTRIB_LIST #define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_SHAKE_TRACKER_DATA(data_ptr) \ { \ ZB_ZCL_ATTR_SHAKE_TRACKER_DATA, \ ZB_ZCL_ATTR_TYPE_CHAR_STRING, \ ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_REPORTING, \ (void*) data_ptr \ } #define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_SHAKE_TRACKER_IS_SHAKED(data_ptr) \ { \ ZB_ZCL_ATTR_SHAKE_TRACKER_IS_SHAKED, \ ZB_ZCL_ATTR_TYPE_BOOL, \ ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_REPORTING, \ (void*) data_ptr \ } typedef struct zb_zcl_shake_tracker_attrs_s { zb_char_t data [ZB_ZCL_SHAKE_TRACKER_DATA_SIZE]; zb_bool_t is_shaked; } zb_zcl_shake_tracker_attrs_t; #define ZB_ZCL_DECLARE_SHAKE_TRACKER_ATTR_LIST(attr_list, attrs) \ ZB_ZCL_DECLARE_SHAKE_TRACKER_ATTRIB_LIST(attr_list, &attrs.data, &attrs.is_shaked) void zb_zcl_shake_tracker_init_server(void); void zb_zcl_shake_tracker_init_client(void); #define ZB_ZCL_CLUSTER_ID_SHAKE_TRACKER_SERVER_ROLE_INIT zb_zcl_shake_tracker_init_server #define ZB_ZCL_CLUSTER_ID_SHAKE_TRACKER_CLIENT_ROLE_INIT zb_zcl_shake_tracker_init_client #endif
zb_zcl_shake_tracker.c
#include "zboss_api.h" #include "zb_zcl_shake_tracker.h" void zb_zcl_shake_tracker_init_server(){} void zb_zcl_shake_tracker_init_client(){}
zb_ha_shaker.h
#ifndef ZB_HA_SHAKER_H #define ZB_HA_SHAKER_H 1 #define ZB_HA_DEVICE_VER_SHAKER 0 #define ZB_HA_SHAKER_IN_CLUSTER_NUM 2 #define ZB_HA_SHAKER_OUT_CLUSTER_NUM 1 #define ZB_HA_SHAKER_CLUSTER_NUM \ (ZB_HA_SHAKER_IN_CLUSTER_NUM + ZB_HA_SHAKER_OUT_CLUSTER_NUM) #define ZB_HA_SHAKER_REPORT_ATTR_COUNT 1 #define ZB_HA_DECLARE_SHAKER_CLUSTER_LIST( \ cluster_list_name, \ basic_attr_list, \ identify_attr_list, \ shake_attribute_list) \ zb_zcl_cluster_desc_t cluster_list_name[] = \ { \ ZB_ZCL_CLUSTER_DESC( \ ZB_ZCL_CLUSTER_ID_IDENTIFY, \ ZB_ZCL_ARRAY_SIZE(identify_attr_list, zb_zcl_attr_t), \ (identify_attr_list), \ ZB_ZCL_CLUSTER_SERVER_ROLE, \ ZB_ZCL_MANUF_CODE_INVALID \ ), \ ZB_ZCL_CLUSTER_DESC( \ ZB_ZCL_CLUSTER_ID_BASIC, \ ZB_ZCL_ARRAY_SIZE(basic_attr_list, zb_zcl_attr_t), \ (basic_attr_list), \ ZB_ZCL_CLUSTER_SERVER_ROLE, \ ZB_ZCL_MANUF_CODE_INVALID \ ), \ ZB_ZCL_CLUSTER_DESC( \ ZB_ZCL_CLUSTER_ID_SHAKE_TRACKER, \ ZB_ZCL_ARRAY_SIZE(shake_attribute_list, zb_zcl_attr_t), \ (shake_attribute_list), \ ZB_ZCL_CLUSTER_SERVER_ROLE, \ ZB_ZCL_MANUF_CODE_INVALID \ ) \ } \ #define ZB_ZCL_DECLARE_SHAKER_SIMPLE_DESC(ep_name, ep_id, in_clust_num, out_clust_num) \ ZB_DECLARE_SIMPLE_DESC(in_clust_num, out_clust_num); \ ZB_AF_SIMPLE_DESC_TYPE(in_clust_num, out_clust_num) simple_desc_##ep_name = \ { \ ep_id, \ ZB_AF_HA_PROFILE_ID, \ 0xDEAD, \ ZB_HA_DEVICE_VER_SHAKER, \ 0, \ in_clust_num, \ out_clust_num, \ { \ ZB_ZCL_CLUSTER_ID_BASIC, \ ZB_ZCL_CLUSTER_ID_IDENTIFY, \ ZB_ZCL_CLUSTER_ID_SHAKE_TRACKER \ } \ } #define ZB_HA_DECLARE_SHAKER_EP(ep_name, ep_id, cluster_list) \ ZB_ZCL_DECLARE_SHAKER_SIMPLE_DESC(ep_name, ep_id, \ ZB_HA_SHAKER_IN_CLUSTER_NUM, ZB_HA_SHAKER_OUT_CLUSTER_NUM); \ ZB_AF_DECLARE_ENDPOINT_DESC(ep_name, ep_id, ZB_AF_HA_PROFILE_ID, 0, NULL, \ ZB_ZCL_ARRAY_SIZE(cluster_list, zb_zcl_cluster_desc_t), cluster_list, \ (zb_af_simple_desc_1_1_t*)&simple_desc_##ep_name, \ 0, NULL, /* No reporting ctx */ \ 0, NULL) /* No CVC ctx */ #define ZB_HA_DECLARE_SHAKER_CTX(device_ctx, ep_name) \ ZBOSS_DECLARE_DEVICE_CTX_1_EP(device_ctx, ep_name) #endif
Thank you in advance.
Isaia