This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

"zcl_device_cb" of "light-control" filter not OnOff data

Hi Nordic team,
I'm working with nRF52840 PCA10056, base on Thread_Zigbee_v4.1.0, and light_control example.
I modifed, try to add temp_measurement ZCL in all light_bulb / light_switch to receive/trans temperature data - to exact addr.
but "light-bulb" isn't jump to "zcl_device_cb" (just with Temp cmd, OnOff cmd still ok). Maybe declare wrong temp ZCL, or missing anything.

This is my situation:


And below figure, I think "light_switch" have trans out, but not sure "light_bulb" can get:

And this code on "light-switch":

I also saw "pressure_cluster", "multi_sensor" but not clear, i dont known how/where/when use "zb_zcl_add_cluster_handlers".
I read "">infocenter.nordicsemi.com/index.jsp dont have progression.
Please guide me the way to adding 1 cluster (Temp cluster...).

If you need more infor, please note me.

Thank you.
VDM

  • Hi Nordic team

    I attach my code:

    Light-Bulb:

    /*
     Controller:    nRF52840
     Hardware:      nRF52840-DK
     Folder/SDK:	C:\Nordic_Semi\Thread_Zigbee_v4.1.0\examples\zigbee\light_control\light_bulb
     Application:   Zigbee
     IDE:           Segger Embedded Studio
     Complier:      Theo Segger Embedded Studio
     Programer:	Segger J-Link (nRF52840 Starter Kit)
     Company:       OSSTech Co.,Ltd
     Writer:        Vo Duc Minh
     Date:          2021.04.12
    
     File Description:
     * LED1 nhap nhay bao hieu dang chay
     LED2 doi trang thai bao hieu co nut nao duoc nhan
     P0.3 doi trang thai moi lan nhan duoc data
    
     Nhung file da sua:
    
     
                      
     */
    
    #include "sdk_config.h"
    #include "zboss_api.h"
    #include "zboss_api_addons.h"
    #include "zb_mem_config_med.h"
    #include "zb_ha_dimmable_light.h"
    #include "zb_error_handler.h"
    #include "zb_nrf52_internal.h"
    #include "zigbee_helpers.h"
    
    #include "bsp.h"
    #include "boards.h"
    #include "app_pwm.h"
    #include "app_timer.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #include "drv_ws2812.h"
    
    #define MAX_CHILDREN                      10                                    /**< The maximum amount of connected devices. Setting this value to 0 disables association to this device.  */
    #define IEEE_CHANNEL_MASK                 (1l << ZIGBEE_CHANNEL)                /**< Scan only one, predefined channel to find the coordinator. */
    #define HA_DIMMABLE_LIGHT_ENDPOINT        10                                    /**< Device endpoint, used to receive light controlling commands. */
    #define ERASE_PERSISTENT_CONFIG           ZB_FALSE                              /**< Do not erase NVRAM to save the network parameters after device reboot or power-off. */
    #define BULB_PWM_NAME                     PWM1                                  /**< PWM instance used to drive dimmable light bulb. */
    #define BULB_PWM_TIMER                    2                                     /**< Timer number used by PWM. */
    
    /* Basic cluster attributes initial values. */
    #define BULB_INIT_BASIC_APP_VERSION       01                                    /**< Version of the application software (1 byte). */
    #define BULB_INIT_BASIC_STACK_VERSION     10                                    /**< Version of the implementation of the Zigbee stack (1 byte). */
    #define BULB_INIT_BASIC_HW_VERSION        11                                    /**< Version of the hardware of the device (1 byte). */
    #define BULB_INIT_BASIC_MANUF_NAME        "Nordic"                              /**< Manufacturer name (32 bytes). */
    #define BULB_INIT_BASIC_MODEL_ID          "Dimable_Light_v0.1"                  /**< Model number assigned by manufacturer (32-bytes long string). */
    #define BULB_INIT_BASIC_DATE_CODE         "20180416"                            /**< First 8 bytes specify the date of manufacturer of the device in ISO 8601 format (YYYYMMDD). The rest (8 bytes) are manufacturer specific. */
    #define BULB_INIT_BASIC_POWER_SOURCE      ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE   /**< Type of power sources available for the device. For possible values see section 3.2.2.2.8 of ZCL specification. */
    #define BULB_INIT_BASIC_LOCATION_DESC     "Office desk"                         /**< Describes the physical location of the device (16 bytes). May be modified during commisioning process. */
    #define BULB_INIT_BASIC_PH_ENV            ZB_ZCL_BASIC_ENV_UNSPECIFIED          /**< Describes the type of physical environment. For possible values see section 3.2.2.2.10 of ZCL specification. */
    
    #ifdef  BOARD_PCA10059                                                          /**< If it is Dongle */
    #define IDENTIFY_MODE_BSP_EVT             BSP_EVENT_KEY_0                       /**< Button event used to enter the Bulb into the Identify mode. */
    #define ZIGBEE_NETWORK_STATE_LED          BSP_BOARD_LED_0                       /**< LED indicating that light switch successfully joind Zigbee network. */
    #else
    #define IDENTIFY_MODE_BSP_EVT             BSP_EVENT_KEY_3                       /**< Button event used to enter the Bulb into the Identify mode. */
    #define ZIGBEE_NETWORK_STATE_LED          BSP_BOARD_LED_2                       /**< LED indicating that light switch successfully joind Zigbee network. */
    #endif
    #define BULB_LED                          BSP_BOARD_LED_3                       /**< LED immitaing dimmable light bulb. */
    
    #if (APP_BULB_USE_WS2812_LED_CHAIN)
    #define LED_CHAIN_DOUT_PIN                NRF_GPIO_PIN_MAP(1,7)                 /**< GPIO pin used as DOUT (to be connected to DIN pin of the first ws2812 led in chain) */
    #endif
    
    // Minh them vao
    APP_TIMER_DEF(heartBeat_Led);
    #define HEARTBEAT_INTERVAL                                APP_TIMER_TICKS(300)// ms
    #define ZB_HA_DIMMABLE_LIGHT_IN_CLUSTER_NUM_MINH          7                   /*!< Dimmable Light IN (server) clusters number */
    #define ZB_HA_DIMMABLE_LIGHT_OUT_CLUSTER_NUM_MINH         1                   /*!< Dimmable Light OUT (client) clusters number */
    #define ZB_AF_HA_PROFILE_ID_MINH                          0x0104
    #define ZB_HA_DIMMABLE_LIGHT_DEVICE_ID_MINH               0x0101
    #define ZB_HA_DEVICE_VER_DIMMABLE_LIGHT_MINH              1                   /*!< Dimmable light device version */
    #define ZB_ZCL_CLUSTER_REVISION_DEFAULT_MINH              1
    #define ZB_ZCL_ATTR_GLOBAL_CLUSTER_REVISION_ID_MINH       0xFFFD
    #define ZB_ZCL_ATTR_TYPE_U16_MINH                         0x21
    #define ZB_ZCL_ATTR_ACCESS_READ_ONLY_MINH                 0x00
    #define ZB_HA_DIMMABLE_LIGHT_CVC_ATTR_COUNT_MINH          1
    #define ZB_ZCL_ON_OFF_REPORT_ATTR_COUNT_MINH              2	// =1 van chay
    #define ZB_ZCL_LEVEL_CONTROL_REPORT_ATTR_COUNT_MINH       1
    #define ADDR_BULB2                                        0x4EE0
    #define ADDR_SW                                           0x6788
    #define ZB_HA_DIMMABLE_LIGHT_REPORT_ATTR_COUNT_MINH       (ZB_ZCL_ON_OFF_REPORT_ATTR_COUNT_MINH         \
                                                              + ZB_ZCL_LEVEL_CONTROL_REPORT_ATTR_COUNT_MINH \
                                                              + ZB_ZCL_TEMP_MEASUREMENT_REPORT_ATTR_COUNT)
    
    #define ZB_ZCL_DECLARE_HA_DIMMABLE_LIGHT_SIMPLE_DESC_MINH(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_MINH,                                                                  \
        ZB_HA_DIMMABLE_LIGHT_DEVICE_ID_MINH,                                                       \
        ZB_HA_DEVICE_VER_DIMMABLE_LIGHT_MINH,                                                      \
        0,                                                                                         \
        in_clust_num,                                                                              \
        out_clust_num,                                                                             \
        {                                                                                          \
          ZB_ZCL_CLUSTER_ID_BASIC,                                                                 \
          ZB_ZCL_CLUSTER_ID_IDENTIFY,                                                              \
          ZB_ZCL_CLUSTER_ID_SCENES,                                                                \
          ZB_ZCL_CLUSTER_ID_GROUPS,                                                                \
          ZB_ZCL_CLUSTER_ID_ON_OFF,                                                                \
          ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,                                                         \
          ZB_ZCL_CLUSTER_ID_ON_OFF,                                                                \
          ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT                                                       \
        }                                                                                          \
      }
    
    /* Declare endpoint for Dimmable Light device with scenes. */
    #define ZB_HA_DECLARE_LIGHT_EP(ep_name, ep_id, cluster_list)                                \
      ZB_ZCL_DECLARE_HA_DIMMABLE_LIGHT_SIMPLE_DESC_MINH(ep_name, ep_id,                         \
        ZB_HA_DIMMABLE_LIGHT_IN_CLUSTER_NUM_MINH, ZB_HA_DIMMABLE_LIGHT_OUT_CLUSTER_NUM_MINH);   \
      ZBOSS_DEVICE_DECLARE_REPORTING_CTX(reporting_info## device_ctx_name,                      \
                                         ZB_HA_DIMMABLE_LIGHT_REPORT_ATTR_COUNT_MINH);          \
      ZBOSS_DEVICE_DECLARE_LEVEL_CONTROL_CTX(cvc_alarm_info## device_ctx_name,                  \
                                             ZB_HA_DIMMABLE_LIGHT_CVC_ATTR_COUNT_MINH);         \
      ZB_AF_DECLARE_ENDPOINT_DESC(ep_name, ep_id, ZB_AF_HA_PROFILE_ID_MINH,               \
                                  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,       \
                                  ZB_HA_DIMMABLE_LIGHT_REPORT_ATTR_COUNT_MINH,            \
                                  reporting_info## device_ctx_name,                       \
                                  ZB_HA_DIMMABLE_LIGHT_CVC_ATTR_COUNT_MINH,               \
                                  cvc_alarm_info## device_ctx_name)
    
    #define ZB_HA_DECLARE_DIMMABLE_LIGHT_CLUSTER_LIST_MINH(          \
      cluster_list_name,                                             \
      basic_attr_list,                                               \
      identify_attr_list,                                            \
      groups_attr_list,                                              \
      scenes_attr_list,                                              \
      on_off_attr_list,                                              \
      level_control_attr_list,                                       \
      temp_attr_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_SCENES,                                  \
          ZB_ZCL_ARRAY_SIZE(scenes_attr_list, zb_zcl_attr_t),        \
          (scenes_attr_list),                                        \
          ZB_ZCL_CLUSTER_SERVER_ROLE,                                \
          ZB_ZCL_MANUF_CODE_INVALID                                  \
        ),                                                           \
        ZB_ZCL_CLUSTER_DESC(                                         \
          ZB_ZCL_CLUSTER_ID_GROUPS,                                  \
          ZB_ZCL_ARRAY_SIZE(groups_attr_list, zb_zcl_attr_t),        \
          (groups_attr_list),                                        \
          ZB_ZCL_CLUSTER_SERVER_ROLE,                                \
          ZB_ZCL_MANUF_CODE_INVALID                                  \
        ),                                                           \
        ZB_ZCL_CLUSTER_DESC(                                         \
          ZB_ZCL_CLUSTER_ID_ON_OFF,                                  \
          ZB_ZCL_ARRAY_SIZE(on_off_attr_list, zb_zcl_attr_t),        \
          (on_off_attr_list),                                        \
          ZB_ZCL_CLUSTER_SERVER_ROLE,                                \
          ZB_ZCL_MANUF_CODE_INVALID                                  \
        ),                                                           \
        ZB_ZCL_CLUSTER_DESC(                                         \
          ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,                           \
          ZB_ZCL_ARRAY_SIZE(level_control_attr_list, zb_zcl_attr_t), \
          (level_control_attr_list),                                 \
          ZB_ZCL_CLUSTER_SERVER_ROLE,                                \
          ZB_ZCL_MANUF_CODE_INVALID                                  \
        ),                                                           \
        ZB_ZCL_CLUSTER_DESC(                                         \
          ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT,                        \
          ZB_ZCL_ARRAY_SIZE(temp_attr_list, zb_zcl_attr_t),          \
          (temp_attr_list),                                          \
          ZB_ZCL_CLUSTER_SERVER_ROLE,                                \
          ZB_ZCL_MANUF_CODE_INVALID                                  \
        ),                                                           \
        ZB_ZCL_CLUSTER_DESC(                                         \
          ZB_ZCL_CLUSTER_ID_ON_OFF,                                  \
          0,                                                         \
          NULL,                                                      \
          ZB_ZCL_CLUSTER_CLIENT_ROLE,                                \
          ZB_ZCL_MANUF_CODE_INVALID                                  \
        )                                                            \
      }
    
    #define ZB_ZCL_START_DECLARE_ATTRIB_LIST_MINH(attrs_desc_name)                            \
      zb_uint16_t cluster_revision_##attrs_desc_name = ZB_ZCL_CLUSTER_REVISION_DEFAULT_MINH;  \
      zb_zcl_attr_t attrs_desc_name [] = {                                                    \
      {                                                                                       \
        ZB_ZCL_ATTR_GLOBAL_CLUSTER_REVISION_ID_MINH,                                          \
        ZB_ZCL_ATTR_TYPE_U16_MINH,                                                            \
        ZB_ZCL_ATTR_ACCESS_READ_ONLY_MINH,                                                    \
        (zb_voidp_t) &(cluster_revision_##attrs_desc_name)                                    \
      },
    
    #define ZB_ZCL_DECLARE_LEVEL_CONTROL_ATTRIB_LIST_MINH(attr_list, current_level, remaining_time) \
      zb_zcl_level_control_move_status_t move_status_data_ctx## _attr_list;                         \
      ZB_ZCL_START_DECLARE_ATTRIB_LIST_MINH(attr_list)                                              \
      ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID, (current_level))             \
      ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_LEVEL_CONTROL_REMAINING_TIME_ID, (remaining_time))           \
      ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_LEVEL_CONTROL_MOVE_STATUS_ID,                                \
                           (&(move_status_data_ctx## _attr_list)))                                  \
      ZB_ZCL_FINISH_DECLARE_ATTRIB_LIST
    
    #if !defined ZB_ROUTER_ROLE
    #error Define ZB_ROUTER_ROLE to compile light bulb (Router) source code.
    #endif
    
    /* Main application customizable context. Stores all settings and static values. */
    typedef struct
    {
        zb_zcl_basic_attrs_ext_t         basic_attr;
        zb_zcl_identify_attrs_t          identify_attr;
        zb_zcl_scenes_attrs_t            scenes_attr;
        zb_zcl_groups_attrs_t            groups_attr;
        zb_zcl_on_off_attrs_ext_t        on_off_attr;
        zb_zcl_level_control_attrs_t     level_control_attr;
        zb_zcl_temp_measurement_attrs_t  temp_attr;
    } bulb_device_ctx_t;
    APP_PWM_INSTANCE(BULB_PWM_NAME, BULB_PWM_TIMER);
    
    static bulb_device_ctx_t m_dev_ctx;
    ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST(identify_attr_list, &m_dev_ctx.identify_attr.identify_time);
    ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST(groups_attr_list, &m_dev_ctx.groups_attr.name_support);
    ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST(scenes_attr_list,
                                      &m_dev_ctx.scenes_attr.scene_count,
                                      &m_dev_ctx.scenes_attr.current_scene,
                                      &m_dev_ctx.scenes_attr.current_group,
                                      &m_dev_ctx.scenes_attr.scene_valid,
                                      &m_dev_ctx.scenes_attr.name_support);
    ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT(basic_attr_list,
                                         &m_dev_ctx.basic_attr.zcl_version,
                                         &m_dev_ctx.basic_attr.app_version,
                                         &m_dev_ctx.basic_attr.stack_version,
                                         &m_dev_ctx.basic_attr.hw_version,
                                         m_dev_ctx.basic_attr.mf_name,
                                         m_dev_ctx.basic_attr.model_id,
                                         m_dev_ctx.basic_attr.date_code,
                                         &m_dev_ctx.basic_attr.power_source,
                                         m_dev_ctx.basic_attr.location_id,
                                         &m_dev_ctx.basic_attr.ph_env,
                                         m_dev_ctx.basic_attr.sw_ver);
    ZB_ZCL_DECLARE_ON_OFF_ATTRIB_LIST_EXT(on_off_attr_list,
                                          &m_dev_ctx.on_off_attr.on_off,
                                          &m_dev_ctx.on_off_attr.global_scene_ctrl,
                                          &m_dev_ctx.on_off_attr.on_time,
                                          &m_dev_ctx.on_off_attr.off_wait_time);
    ZB_ZCL_DECLARE_LEVEL_CONTROL_ATTRIB_LIST_MINH(level_control_attr_list,
                                             &m_dev_ctx.level_control_attr.current_level,
                                             &m_dev_ctx.level_control_attr.remaining_time);
    ZB_ZCL_DECLARE_TEMP_MEASUREMENT_ATTRIB_LIST(temp_attr_list,
                                              &m_dev_ctx.temp_attr.measure_value,
                                              &m_dev_ctx.temp_attr.min_measure_value,
                                              &m_dev_ctx.temp_attr.max_measure_value,
                                              &m_dev_ctx.temp_attr.tolerance);
    ZB_HA_DECLARE_DIMMABLE_LIGHT_CLUSTER_LIST_MINH(dimmable_light_clusters,
                                              basic_attr_list,
                                              identify_attr_list,
                                              groups_attr_list,
                                              scenes_attr_list,
                                              on_off_attr_list,
                                              level_control_attr_list,
                                              temp_attr_list);
    
    ZB_HA_DECLARE_LIGHT_EP(dimmable_light_ep, HA_DIMMABLE_LIGHT_ENDPOINT, dimmable_light_clusters);
    ZB_HA_DECLARE_DIMMABLE_LIGHT_CTX(dimmable_light_ctx, dimmable_light_ep);
    
    #if (APP_BULB_USE_WS2812_LED_CHAIN)
    /**@brief Timer responsible for triggering periodic led chain refresh */
    APP_TIMER_DEF(m_ws2812_refresh_timer);
    
    /** @brief Requests a led chain refresh */
    static volatile bool m_ws2812_refresh_request;
    #endif
    
    /**@brief Function for initializing the application timer.
     */
    static void timer_init(void)
    {
        uint32_t error_code = app_timer_init();
        APP_ERROR_CHECK(error_code);
    }
    
    /**@brief Function for initializing the nrf log module.
     */
    static void log_init(void)
    {
        ret_code_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    }
    
    /**@brief Sets brightness of on-board LED
     *
     * @param[in] brightness_level Brightness level, allowed values 0 ... 255, 0 - turn off, 255 - full brightness
     */
    static void light_bulb_onboard_set_brightness(zb_uint8_t brightness_level)
    {
        app_pwm_duty_t app_pwm_duty;
    
        /* Scale level value: APP_PWM uses 0-100 scale, but Zigbee level control cluster uses values from 0 up to 255. */
        app_pwm_duty = (brightness_level * 100U) / 255U;
    
        /* Set the duty cycle - keep trying until PWM is ready. */
        while (app_pwm_channel_duty_set(&BULB_PWM_NAME, 0, app_pwm_duty) == NRF_ERROR_BUSY)
        {
        }
    }
    
    #if (APP_BULB_USE_WS2812_LED_CHAIN)
    /**@brief Sets brightness of ws2812 led chain
     *
     * @param[in] brightness_level Brightness level, allowed values 0 ... 255, 0 - turn off, 255 - full brightness
     */
    static void light_bulb_ws2812_chain_set_brightness(zb_uint8_t brightness_level)
    {
        uint32_t color;
    
        /* Decrease brightness just to save your eyes. LEDs can be very bright */
        if (brightness_level >= 2U)
        {
            brightness_level /= 2U;
        }
    
        color = ((uint32_t)brightness_level << 16);  /* Red component   */
        color |= ((uint32_t)brightness_level << 8);  /* Green component */
        color |= (uint32_t)brightness_level;         /* Blue component  */
    
        drv_ws2812_set_pixel_all(color);
    
        /* Main loop will take care of refreshing led chain */
        m_ws2812_refresh_request = true;
    }
    #endif
    
    /**@brief Sets brightness of bulb luminous executive element
     *
     * @param[in] brightness_level Brightness level, allowed values 0 ... 255, 0 - turn off, 255 - full brightness
     */
    static void light_bulb_set_brightness(zb_uint8_t brightness_level)
    {
        light_bulb_onboard_set_brightness(brightness_level);
    #if (APP_BULB_USE_WS2812_LED_CHAIN)
        light_bulb_ws2812_chain_set_brightness(brightness_level);
    #endif
    }
    
    /**@brief Function for setting the light bulb brightness.
      *
      * @param[in]   new_level   Light bulb brightness value.
     */
    static void level_control_set_value(zb_uint16_t new_level)
    {
        NRF_LOG_INFO("Set level value: %i", new_level);
        ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT,                                       
                             ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,            
                             ZB_ZCL_CLUSTER_SERVER_ROLE,                 
                             ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID, 
                             (zb_uint8_t *)&new_level,                                       
                             ZB_FALSE);                                  
        /* According to the table 7.3 of Home Automation Profile Specification v 1.2 rev 29, chapter 7.1.3. */
        if (new_level == 0)
        {
            zb_uint8_t value = ZB_FALSE;
            ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT, 
                                 ZB_ZCL_CLUSTER_ID_ON_OFF,    
                                 ZB_ZCL_CLUSTER_SERVER_ROLE,  
                                 ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
                                 &value,                        
                                 ZB_FALSE);                   
        }
        else
        {
            zb_uint8_t value = ZB_TRUE;
            ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT, 
                                 ZB_ZCL_CLUSTER_ID_ON_OFF,    
                                 ZB_ZCL_CLUSTER_SERVER_ROLE,  
                                 ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
                                 &value,                        
                                 ZB_FALSE);
        }
        light_bulb_set_brightness(new_level);
    }
    
    
    /**@brief Function for turning ON/OFF the light bulb.
     *
     * @param[in]   on   Boolean light bulb state.
     */
    static void on_off_set_value(zb_bool_t on)
    {
        NRF_LOG_INFO("Set ON/OFF value: %i", on);
        ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT, 
                             ZB_ZCL_CLUSTER_ID_ON_OFF,    
                             ZB_ZCL_CLUSTER_SERVER_ROLE,  
                             ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
                             (zb_uint8_t *)&on,                        
                             ZB_FALSE);
        if (on)
        {
            level_control_set_value(m_dev_ctx.level_control_attr.current_level);
        }
        else
        {
            light_bulb_set_brightness(0U);
        }
    }
    
    static zb_uint16_t addr_oss;
    static zb_void_t light_switch_send_on_off(zb_bufid_t bufid, zb_uint16_t addr)
    {
        if (addr==ADDR_SW)
        {
            // Truyen cho Switch
            zb_uint8_t* ptr = ZB_ZCL_START_PACKET_REQ(bufid)
            ZB_ZCL_CONSTRUCT_SPECIFIC_COMMAND_REQ_FRAME_CONTROL(ptr, ZB_ZCL_DISABLE_DEFAULT_RESPONSE)
            ZB_ZCL_CONSTRUCT_COMMAND_HEADER_REQ(ptr, ZB_ZCL_GET_SEQ_NUM(), ZB_ZCL_CMD_ON_OFF_TOGGLE_ID);
            ZB_ZCL_FINISH_PACKET(bufid, ptr)
            ZB_ZCL_SEND_COMMAND_SHORT(bufid, addr, ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
                                      1, HA_DIMMABLE_LIGHT_ENDPOINT,
                                      ZB_AF_HA_PROFILE_ID, ZB_ZCL_CLUSTER_ID_ON_OFF, NULL);
        }
        else if (addr==ADDR_BULB2)
        {
            // truyen cho Bulb khac
            zb_uint8_t* ptr = ZB_ZCL_START_PACKET_REQ(bufid)
            ZB_ZCL_CONSTRUCT_SPECIFIC_COMMAND_REQ_FRAME_CONTROL(ptr, ZB_ZCL_DISABLE_DEFAULT_RESPONSE)
            ZB_ZCL_CONSTRUCT_COMMAND_HEADER_REQ(ptr, ZB_ZCL_GET_SEQ_NUM(), ZB_ZCL_CMD_ON_OFF_TOGGLE_ID);
            ZB_ZCL_FINISH_PACKET(bufid, ptr)
            ZB_ZCL_SEND_COMMAND_SHORT(bufid, addr, ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
                                      10, HA_DIMMABLE_LIGHT_ENDPOINT,
                                      ZB_AF_HA_PROFILE_ID, ZB_ZCL_CLUSTER_ID_ON_OFF, NULL);
        }
    }
    
    /**@brief Callback for button events.
     *
     * @param[in]   evt      Incoming event from the BSP subsystem.
     */
    static void buttons_handler(bsp_event_t evt)
    {
        zb_ret_t zb_err_code;
    
        switch(evt)
        {
            case IDENTIFY_MODE_BSP_EVT:
                /* Check if endpoint is in identifying mode, if not put desired endpoint in identifying mode. */
                if (m_dev_ctx.identify_attr.identify_time == ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE)
                {
                    NRF_LOG_INFO("Bulb put in identifying mode");
                    zb_err_code = zb_bdb_finding_binding_target(HA_DIMMABLE_LIGHT_ENDPOINT);
                    ZB_ERROR_CHECK(zb_err_code);
                }
                else
                {
                    NRF_LOG_INFO("Cancel F&B target procedure");
                    zb_bdb_finding_binding_target_cancel();
                }
                break;
    
            case BSP_EVENT_KEY_0:
                addr_oss = ADDR_SW;
                
                break;
                
            case BSP_EVENT_KEY_1:
                addr_oss = ADDR_BULB2;
                break;
                
            default:
                NRF_LOG_INFO("Unhandled BSP Event received: %d", evt);
                break;
        }
        // Minh them vao. LED2
        zb_buf_get_out_delayed_ext(light_switch_send_on_off, addr_oss, 0);
        if (NRF_P0->OUT & (1<<14))
            NRF_P0->OUTCLR = (1<<14);
        else
            NRF_P0->OUTSET = (1<<14);
    }
    
    
    /**@brief Function for initializing LEDs and a single PWM channel.
     */
    static void leds_buttons_init(void)
    {
        ret_code_t       err_code;
        app_pwm_config_t pwm_cfg = APP_PWM_DEFAULT_CONFIG_1CH(5000L, bsp_board_led_idx_to_pin(BULB_LED));
    
        /* Initialize all LEDs and buttons. */
        err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, buttons_handler);
        APP_ERROR_CHECK(err_code);
        /* By default the bsp_init attaches BSP_KEY_EVENTS_{0-4} to the PUSH events of the corresponding buttons. */
    
        /* Initialize PWM running on timer 1 in order to control dimmable light bulb. */
        err_code = app_pwm_init(&BULB_PWM_NAME, &pwm_cfg, NULL);
        APP_ERROR_CHECK(err_code);
    
        app_pwm_enable(&BULB_PWM_NAME);
    
        while (app_pwm_channel_duty_set(&BULB_PWM_NAME, 0, 99) == NRF_ERROR_BUSY)
        {
        }
    }
    
    /**@brief Function for initializing all clusters attributes.
     */
    static void bulb_clusters_attr_init(void)
    {
        /* Basic cluster attributes data */
        m_dev_ctx.basic_attr.zcl_version   = ZB_ZCL_VERSION;
        m_dev_ctx.basic_attr.app_version   = BULB_INIT_BASIC_APP_VERSION;
        m_dev_ctx.basic_attr.stack_version = BULB_INIT_BASIC_STACK_VERSION;
        m_dev_ctx.basic_attr.hw_version    = BULB_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 wil be encoded as:
         *   [(0x4), 't', 'e', 's', 't']
         */
        ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.mf_name,
                              BULB_INIT_BASIC_MANUF_NAME,
                              ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_MANUF_NAME));
    
        ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.model_id,
                              BULB_INIT_BASIC_MODEL_ID,
                              ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_MODEL_ID));
    
        ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.date_code,
                              BULB_INIT_BASIC_DATE_CODE,
                              ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_DATE_CODE));
    
        m_dev_ctx.basic_attr.power_source = BULB_INIT_BASIC_POWER_SOURCE;
    
        ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.location_id,
                              BULB_INIT_BASIC_LOCATION_DESC,
                              ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_LOCATION_DESC));
    
    
        m_dev_ctx.basic_attr.ph_env = BULB_INIT_BASIC_PH_ENV;
    
        /* Identify cluster attributes data */
        m_dev_ctx.identify_attr.identify_time = ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE;
    
        /* On/Off cluster attributes data */
        m_dev_ctx.on_off_attr.on_off = (zb_bool_t)ZB_ZCL_ON_OFF_IS_ON;
    
        m_dev_ctx.level_control_attr.current_level  = ZB_ZCL_LEVEL_CONTROL_LEVEL_MAX_VALUE;
        m_dev_ctx.level_control_attr.remaining_time = ZB_ZCL_LEVEL_CONTROL_REMAINING_TIME_DEFAULT_VALUE;
        ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT, 
                             ZB_ZCL_CLUSTER_ID_ON_OFF,    
                             ZB_ZCL_CLUSTER_SERVER_ROLE,  
                             ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
                             (zb_uint8_t *)&m_dev_ctx.on_off_attr.on_off,                        
                             ZB_FALSE);                   
        ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT,                                       
                             ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,            
                             ZB_ZCL_CLUSTER_SERVER_ROLE,                 
                             ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID, 
                             (zb_uint8_t *)&m_dev_ctx.level_control_attr.current_level,                                       
                             ZB_FALSE);                                  
    }
    
    /**@brief Function which tries to sleep down the MCU 
     *
     * Function which sleeps the MCU on the non-sleepy End Devices to optimize the power saving.
     * The weak definition inside the OSIF layer provides some minimal working template
     */
    zb_void_t zb_osif_go_idle(zb_void_t)
    {
        //TODO: implement your own logic if needed
        zb_osif_wait_for_event();
    }
    
    typedef struct zb_zcl_device_callback_param_s_oss
    {
      zb_zcl_device_callback_id_t device_cb_id;
      zb_uint8_t endpoint;
      zb_zcl_attr_access_t attr_type;
      zb_ret_t status;
      /** Callback custom data */
      union
      {
        zb_zcl_set_attr_value_param_t  set_attr_value_param;
        zb_zcl_on_off_set_effect_value_param_t  on_off_set_effect_value_param;
        zb_zcl_temp_measurement_attrs_t  temp_value_oss;
    
    #if defined(ZB_ZCL_SUPPORT_CLUSTER_IDENTIFY)
        zb_zcl_identify_effect_value_param_t  identify_effect_value_param;
    #endif
    
       zb_zcl_shade_set_value_param_t  shade_set_value_param;
       zb_zcl_shade_get_value_param_t  shade_get_value_param;
       zb_zcl_level_control_set_value_param_t  level_control_set_value_param;
       zb_zcl_ias_wd_start_warning_value_param_t  start_warning_value_param;
    #if defined ZB_ENABLE_HA
       zb_zcl_ias_zone_enroll_response_value_param_t enroll_response_value_param;
       zb_zcl_thermostat_value_param_t thermostat_value_param;
    #endif /* defined ZB_ENABLE_HA */
       zb_zcl_ias_wd_squawk_value_param_t  squawk_value_param;
    #if defined ZB_ENABLE_HA
    #if defined ZB_HA_ENABLE_OTA_UPGRADE_CLIENT || defined DOXYGEN
       zb_zcl_ota_upgrade_value_param_t  ota_value_param;
    #endif /*defined ZB_HA_ENABLE_OTA_UPGRADE_CLIENT || defined DOXYGEN */
       zb_zcl_poll_control_check_in_cli_param_t checkin_cli_param;
    #if defined ZB_HA_ENABLE_OTA_UPGRADE_SERVER
       zb_zcl_ota_upgrade_srv_query_img_param_t       ota_upgrade_srv_query_img_param;
       zb_zcl_ota_upgrade_srv_upgrade_started_param_t ota_upgrade_srv_upgrade_started_param;
       zb_zcl_ota_upgrade_srv_upgrade_aborted_param_t ota_upgrade_srv_upgrade_aborted_param;
       zb_zcl_ota_upgrade_srv_upgrade_end_param_t     ota_upgrade_srv_upgrade_end_param;
    #endif
    #endif /* defined ZB_ENABLE_HA */
        zb_zcl_device_cmd_generic_param_t gnr;
      }
      cb_param;
    } zb_zcl_device_callback_param_t_oss;
    
    /**@brief Callback function for handling ZCL commands.
     *
     * @param[in]   bufid   Reference to Zigbee stack buffer used to pass received data.
     */
    static zb_void_t zcl_device_cb(zb_bufid_t bufid)
    {
        // Minh them vao
        if (NRF_P0->OUT & (1<<3))
            NRF_P0->OUTCLR = (1<<3);
        else
            NRF_P0->OUTSET = (1<<3);
    
        zb_uint8_t                       cluster_id;
        zb_uint8_t                       attr_id;
        zb_zcl_device_callback_param_t_oss * p_device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t_oss);
    
        /* Set default response value. */
        p_device_cb_param->status = RET_OK;
    
        switch (p_device_cb_param->device_cb_id)
        {
            case ZB_ZCL_LEVEL_CONTROL_SET_VALUE_CB_ID:
                NRF_LOG_INFO("Level control setting to %d", p_device_cb_param->cb_param.level_control_set_value_param.new_value);
                level_control_set_value(p_device_cb_param->cb_param.level_control_set_value_param.new_value);
                break;
    
            case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
                cluster_id = p_device_cb_param->cb_param.set_attr_value_param.cluster_id;
                attr_id    = p_device_cb_param->cb_param.set_attr_value_param.attr_id;
    
                if (cluster_id == ZB_ZCL_CLUSTER_ID_ON_OFF)
                {
                    uint8_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data8;
                    NRF_LOG_INFO("on/off attribute setting to %hd", value);
                    if (attr_id == ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID)
                    {
                        on_off_set_value((zb_bool_t) value);
                    }
                }
                else if (cluster_id == ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL)
                {
                    uint16_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data16;
                    NRF_LOG_INFO("level control attribute setting to %hd", value);
                    if (attr_id == ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID)
                    {
                        level_control_set_value(value);
                    }
                }
                else
                {
                    /* Other clusters can be processed here */
                    if (NRF_P0->OUT & (1<<2))  // P0.2
                        NRF_P0->OUTCLR = (1<<2);
                    else
                        NRF_P0->OUTSET = (1<<2);
                }
                break;
    
            default:
                p_device_cb_param->status = RET_ERROR;
                break;
        }
    }
    
    /**@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. */
        // OnOff, Temp kg vao day. Chi co broadcast vao thoi.
        ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
    
        if (bufid)
        {
            // OnOff kg thay vao day. Chi co broadcast la vao thoi
            zb_buf_free(bufid);
        }
    }
    
    #if (APP_BULB_USE_WS2812_LED_CHAIN)
    static void ws2812_refresh_timer_timeout_handler(void *p_context)
    {
        m_ws2812_refresh_request = true;
    }
    #endif
    
    // Minh them vao
    static void heartBeat_timeout_handler(void * p_context)
    {
        if (NRF_P0->OUT & (1<<13))  // LED1
            NRF_P0->OUTCLR = (1<<13);
        else
            NRF_P0->OUTSET = (1<<13);
    }
    
    /**@brief Function for application main entry.
     */
    int main(void)
    {
        zb_ret_t       zb_err_code;
        zb_ieee_addr_t ieee_addr;
    
        /* Initialize timer, logging system and GPIOs. */
        timer_init();
        log_init();
        leds_buttons_init();
    
        // Minh them vao. Den nhay bao hieu dang chay.
        NRF_P0->PIN_CNF[2] = 1;  // Disconnect input buffer + Output, P0.2
        NRF_P0->OUTSET = (1<<2);
        NRF_P0->PIN_CNF[3] = 1;  // Disconnect input buffer + Output, P0.3
        NRF_P0->OUTSET = (1<<3);
    
        app_timer_create(&heartBeat_Led, APP_TIMER_MODE_REPEATED, heartBeat_timeout_handler);
        app_timer_start(heartBeat_Led, HEARTBEAT_INTERVAL, NULL);
    
    #if (APP_BULB_USE_WS2812_LED_CHAIN)
        ret_code_t ret_code;
        ret_code = drv_ws2812_init(LED_CHAIN_DOUT_PIN);
        APP_ERROR_CHECK(ret_code);
    #endif
    
        /* Set Zigbee stack logging level and traffic dump subsystem. */
        ZB_SET_TRACE_LEVEL(ZIGBEE_TRACE_LEVEL);
        ZB_SET_TRACE_MASK(ZIGBEE_TRACE_MASK);
        ZB_SET_TRAF_DUMP_OFF();
    
        /* Initialize Zigbee stack. */
        ZB_INIT("led_bulb");
    
        /* Set device address to the value read from FICR registers. */
        zb_osif_get_ieee_eui64(ieee_addr);
        zb_set_long_address(ieee_addr);
    
        /* Set static long IEEE address. */
        zb_set_network_router_role(IEEE_CHANNEL_MASK);
        zb_set_max_children(MAX_CHILDREN);
        zigbee_erase_persistent_storage(ERASE_PERSISTENT_CONFIG);
        zb_set_keepalive_timeout(ZB_MILLISECONDS_TO_BEACON_INTERVAL(3000));
    
        /* Initialize application context structure. */
        UNUSED_RETURN_VALUE(ZB_MEMSET(&m_dev_ctx, 0, sizeof(m_dev_ctx)));
    
        /* 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(&dimmable_light_ctx);
    
        bulb_clusters_attr_init();
        level_control_set_value(m_dev_ctx.level_control_attr.current_level);
    
    #if (APP_BULB_USE_WS2812_LED_CHAIN)
        /* Let's have a timer triggering refresh of led state */
        ret_code = app_timer_create(&m_ws2812_refresh_timer, APP_TIMER_MODE_REPEATED,
                ws2812_refresh_timer_timeout_handler);
        APP_ERROR_CHECK(ret_code);
        ret_code = app_timer_start(m_ws2812_refresh_timer, APP_TIMER_TICKS(5000U), NULL);
        APP_ERROR_CHECK(ret_code);
    #endif
    
        /** Start Zigbee Stack. */
        zb_err_code = zboss_start_no_autostart();
        ZB_ERROR_CHECK(zb_err_code);
    
        while(1)
        {
            zboss_main_loop_iteration();
            UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
    
    #if (APP_BULB_USE_WS2812_LED_CHAIN)
            if (m_ws2812_refresh_request)
            {
                if (drv_ws2812_display(NULL, NULL) == NRF_SUCCESS)
                {
                    m_ws2812_refresh_request = false;
                }
            }
    #endif
        }
    }
    

    Light-Switch:

    /*
     Controller:    nRF52840
     Hardware:      nRF52840-DK
     Folder/SDK:	C:\Nordic_Semi\Thread_Zigbee_v4.1.0\examples\zigbee\light_control\light_switch
     Application:   Zigbee
     IDE:           Segger Embedded Studio
     Complier:      Theo Segger Embedded Studio
     Programer:	Segger J-Link (nRF52840 Starter Kit)
     Company:       OSSTech Co.,Ltd
     Writer:        Vo Duc Minh
     Date:          2021.04.12
    
     File Description:
    
    
    
     Nhung file da sua:
    
     
                      
     */
    
    #include "zboss_api.h"
    #include "zb_error_handler.h"
    #include "zigbee_helpers.h"
    
    #include "app_timer.h"
    #include "bsp.h"
    #include "boards.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "zboss_api_addons.h"
    
    #define IEEE_CHANNEL_MASK                   (1l << ZIGBEE_CHANNEL)              /**< Scan only one, predefined channel to find the coordinator. */
    #define LIGHT_SWITCH_ENDPOINT               1                                   /**< Source endpoint used to control light bulb. */
    #define MATCH_DESC_REQ_START_DELAY          (2 * ZB_TIME_ONE_SECOND)            /**< Delay between the light switch startup and light bulb finding procedure. */
    #define MATCH_DESC_REQ_TIMEOUT              (5 * ZB_TIME_ONE_SECOND)            /**< Timeout for finding procedure. */
    #define MATCH_DESC_REQ_ROLE                 ZB_NWK_BROADCAST_RX_ON_WHEN_IDLE    /**< Find only non-sleepy device. */
    #define ERASE_PERSISTENT_CONFIG             ZB_FALSE                            /**< Do not erase NVRAM to save the network parameters after device reboot or power-off. NOTE: If this option is set to ZB_TRUE then do full device erase for all network devices before running other samples. */
    #define ZIGBEE_NETWORK_STATE_LED            BSP_BOARD_LED_2                     /**< LED indicating that light switch successfully joind Zigbee network. */
    #define BULB_FOUND_LED                      BSP_BOARD_LED_3                     /**< LED indicating that light witch found a light bulb to control. */
    #define LIGHT_SWITCH_BUTTON_ON              BSP_BOARD_BUTTON_0                  /**< Button ID used to switch on the light bulb. */
    #define LIGHT_SWITCH_BUTTON_OFF             BSP_BOARD_BUTTON_1                  /**< Button ID used to switch off the light bulb. */
    #define SLEEPY_ON_BUTTON                    BSP_BOARD_BUTTON_2                  /**< Button ID used to determine if we need the sleepy device behaviour (pressed means yes). */
    
    #define LIGHT_SWITCH_DIMM_STEP              15                                  /**< Dim step size - increases/decreses current level (range 0x000 - 0xfe). */
    #define LIGHT_SWITCH_DIMM_TRANSACTION_TIME  2                                   /**< Transition time for a single step operation in 0.1 sec units. 0xFFFF - immediate change. */
    
    #define LIGHT_SWITCH_BUTTON_THRESHOLD       ZB_TIME_ONE_SECOND                      /**< Number of beacon intervals the button should be pressed to dimm the light bulb. */
    #define LIGHT_SWITCH_BUTTON_SHORT_POLL_TMO  ZB_MILLISECONDS_TO_BEACON_INTERVAL(50)  /**< Delay between button state checks used in order to detect button long press. */
    #define LIGHT_SWITCH_BUTTON_LONG_POLL_TMO   ZB_MILLISECONDS_TO_BEACON_INTERVAL(300) /**< Time after which the button state is checked again to detect button hold - the dimm command is sent again. */
    
    #define ADDR_BULB2                                        0x4EE0
    #define ADDR_BULB1                                        0x3823
    
    typedef struct light_switch_bulb_params_s
    {
      zb_uint8_t  endpoint;
      zb_uint16_t short_addr;
    } light_switch_bulb_params_t;
    
    typedef struct light_switch_button_s
    {
      zb_bool_t in_progress;
      zb_time_t timestamp;
    } light_switch_button_t;
    
    typedef struct light_switch_ctx_s
    {
      light_switch_bulb_params_t bulb_params;
      light_switch_button_t      button;
      zb_zcl_on_off_attrs_ext_t  on_off_attr;
    } light_switch_ctx_t;
    
    
    static zb_void_t find_light_bulb_timeout(zb_bufid_t bufid);
    
    static light_switch_ctx_t m_device_ctx;
    static zb_uint8_t         m_attr_zcl_version   = ZB_ZCL_VERSION;
    static zb_uint8_t         m_attr_power_source  = ZB_ZCL_BASIC_POWER_SOURCE_UNKNOWN;
    static zb_uint16_t        m_attr_identify_time = 0;
    
    #define ZB_HA_DECLARE_DIMMER_SWITCH_CLUSTER_LIST_OSS(     \
        cluster_list_name,                                    \
        basic_attr_list,                                      \
        identify_attr_list,                                   \
        on_off_attr_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_ON_OFF,                             \
        ZB_ZCL_ARRAY_SIZE(on_off_attr_list, zb_zcl_attr_t),   \
        (on_off_attr_list),                                   \
        ZB_ZCL_CLUSTER_SERVER_ROLE,                           \
        ZB_ZCL_MANUF_CODE_INVALID                             \
      ),                                                      \
      ZB_ZCL_CLUSTER_DESC(                                    \
        ZB_ZCL_CLUSTER_ID_IDENTIFY,                           \
        0,                                                    \
        NULL,                                                 \
        ZB_ZCL_CLUSTER_CLIENT_ROLE,                           \
        ZB_ZCL_MANUF_CODE_INVALID                             \
      ),                                                      \
      ZB_ZCL_CLUSTER_DESC(                                    \
        ZB_ZCL_CLUSTER_ID_SCENES,                             \
        0,                                                    \
        NULL,                                                 \
        ZB_ZCL_CLUSTER_CLIENT_ROLE,                           \
        ZB_ZCL_MANUF_CODE_INVALID                             \
      ),                                                      \
      ZB_ZCL_CLUSTER_DESC(                                    \
        ZB_ZCL_CLUSTER_ID_GROUPS,                             \
        0,                                                    \
        NULL,                                                 \
        ZB_ZCL_CLUSTER_CLIENT_ROLE,                           \
        ZB_ZCL_MANUF_CODE_INVALID                             \
      ),                                                      \
      ZB_ZCL_CLUSTER_DESC(                                    \
        ZB_ZCL_CLUSTER_ID_ON_OFF,                             \
        0,                                                    \
        NULL,                                                 \
        ZB_ZCL_CLUSTER_CLIENT_ROLE,                           \
        ZB_ZCL_MANUF_CODE_INVALID                             \
      ),                                                      \
      ZB_ZCL_CLUSTER_DESC(                                    \
        ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,                      \
        0,                                                    \
        NULL,                                                 \
        ZB_ZCL_CLUSTER_CLIENT_ROLE,                           \
        ZB_ZCL_MANUF_CODE_INVALID                             \
      ),                                                      \
      ZB_ZCL_CLUSTER_DESC(                                    \
        ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT,                   \
        0,                                                    \
        NULL,                                                 \
        ZB_ZCL_CLUSTER_CLIENT_ROLE,                           \
        ZB_ZCL_MANUF_CODE_INVALID                             \
      )                                                       \
    }
    
    #define ZB_ZCL_DECLARE_DIMMER_SWITCH_SIMPLE_DESC_OSS(                         \
      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,                                                      \
        ZB_HA_DIMMER_SWITCH_DEVICE_ID,                                            \
        ZB_HA_DEVICE_VER_DIMMER_SWITCH,                                           \
        0,                                                                        \
        in_clust_num,                                                             \
        out_clust_num,                                                            \
        {                                                                         \
          ZB_ZCL_CLUSTER_ID_BASIC,                                                \
          ZB_ZCL_CLUSTER_ID_IDENTIFY,                                             \
          ZB_ZCL_CLUSTER_ID_ON_OFF,                                               \
          ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,                                        \
          ZB_ZCL_CLUSTER_ID_SCENES,                                               \
          ZB_ZCL_CLUSTER_ID_GROUPS,                                               \
          ZB_ZCL_CLUSTER_ID_IDENTIFY,                                             \
          ZB_ZCL_CLUSTER_ID_ON_OFF,                                               \
          ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT                                      \
        }                                                                         \
      }
    
    #define ZB_HA_DECLARE_DIMMER_SWITCH_EP_OSS(ep_name, ep_id, cluster_list)                        \
      ZB_ZCL_DECLARE_DIMMER_SWITCH_SIMPLE_DESC_OSS(ep_name, ep_id, 3, 6);                           \
      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 */
    
    ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST(basic_attr_list, &m_attr_zcl_version, &m_attr_power_source);
    ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST(identify_attr_list, &m_attr_identify_time);
    ZB_ZCL_DECLARE_ON_OFF_ATTRIB_LIST_EXT(on_off_attr_list,
                                          &m_device_ctx.on_off_attr.on_off,
                                          &m_device_ctx.on_off_attr.global_scene_ctrl,
                                          &m_device_ctx.on_off_attr.on_time,
                                          &m_device_ctx.on_off_attr.off_wait_time);
    ZB_HA_DECLARE_DIMMER_SWITCH_CLUSTER_LIST_OSS(dimmer_switch_clusters, basic_attr_list, identify_attr_list, on_off_attr_list);
    ZB_HA_DECLARE_DIMMER_SWITCH_EP_OSS(dimmer_switch_ep, LIGHT_SWITCH_ENDPOINT, dimmer_switch_clusters);
    ZB_HA_DECLARE_DIMMER_SWITCH_CTX(dimmer_switch_ctx, dimmer_switch_ep);
    
    // Initializes the timer module. This creates and starts application timers.
    static void timers_init(void)
    {
        ret_code_t err_code;
        err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    }
    
    // Brief Function for initializing the nrf log module.
    static void log_init(void)
    {
        ret_code_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    }
    
    /**@brief Function for sending ON/OFF requests to the light bulb.
     *
     * @param[in]   bufid    Non-zero reference to Zigbee stack buffer that will be used to construct on/off request.
     * @param[in]   on_off   Requested state of the light bulb.
     */
    static zb_uint16_t addr_oss;
    static zb_void_t light_switch_send_on_off(zb_bufid_t bufid, zb_uint16_t addr)
    {
        if (addr==ADDR_BULB1)
        {
            zb_uint8_t* ptr = ZB_ZCL_START_PACKET_REQ(bufid)
            ZB_ZCL_CONSTRUCT_SPECIFIC_COMMAND_REQ_FRAME_CONTROL(ptr, ZB_ZCL_DISABLE_DEFAULT_RESPONSE)
            ZB_ZCL_CONSTRUCT_COMMAND_HEADER_REQ(ptr, ZB_ZCL_GET_SEQ_NUM(), ZB_ZCL_CMD_ON_OFF_TOGGLE_ID);
            ZB_ZCL_FINISH_PACKET(bufid, ptr)
            ZB_ZCL_SEND_COMMAND_SHORT(bufid, addr, ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
                                      m_device_ctx.bulb_params.endpoint,
                                      LIGHT_SWITCH_ENDPOINT,
                                      ZB_AF_HA_PROFILE_ID, ZB_ZCL_CLUSTER_ID_ON_OFF, NULL);
        }
        else if (addr==0xFD29)
        {
            zb_uint8_t* ptr = ZB_ZCL_START_PACKET_REQ(bufid)
            ZB_ZCL_CONSTRUCT_SPECIFIC_COMMAND_REQ_FRAME_CONTROL(ptr, ZB_ZCL_DISABLE_DEFAULT_RESPONSE)
            ZB_ZCL_CONSTRUCT_COMMAND_HEADER_REQ(ptr, ZB_ZCL_GET_SEQ_NUM(), ZB_ZCL_CMD_ON_OFF_TOGGLE_ID);
            ZB_ZCL_FINISH_PACKET(bufid, ptr)
            ZB_ZCL_SEND_COMMAND_SHORT(bufid, addr, ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
                                      LIGHT_SWITCH_ENDPOINT,
                                      LIGHT_SWITCH_ENDPOINT,
                                      ZB_AF_HA_PROFILE_ID, ZB_ZCL_CLUSTER_ID_ON_OFF, NULL);
        }
        else if (addr==0x1111)
        {
            zb_uint16_t addr2 = ADDR_BULB2;
            zb_uint8_t* ptr = ZB_ZCL_START_PACKET_REQ(bufid)
            ZB_ZCL_CONSTRUCT_SPECIFIC_COMMAND_REQ_FRAME_CONTROL(ptr, ZB_ZCL_DISABLE_DEFAULT_RESPONSE)
            ZB_ZCL_CONSTRUCT_COMMAND_HEADER_REQ(ptr, ZB_ZCL_GET_SEQ_NUM(), ZB_ZCL_CMD_ON_OFF_TOGGLE_ID);
            zb_zcl_finish_and_send_packet_new(bufid,
                                              ptr,
                                              (zb_addr_u*)&addr2,
                                              ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
                                              10,
                                              LIGHT_SWITCH_ENDPOINT,
                                              ZB_AF_HA_PROFILE_ID,
                                              ZB_ZCL_CLUSTER_ID_ON_OFF,
                                              0,
                                              ZB_FALSE,
                                              ZB_TRUE,
                                              0);
        }
        else if (addr==ADDR_BULB2)
        {
            zb_uint8_t* ptr = ZB_ZCL_START_PACKET_REQ(bufid)
            ZB_ZCL_CONSTRUCT_SPECIFIC_COMMAND_REQ_FRAME_CONTROL(ptr, ZB_ZCL_DISABLE_DEFAULT_RESPONSE)
            ZB_ZCL_CONSTRUCT_COMMAND_HEADER_REQ(ptr, ZB_ZCL_GET_SEQ_NUM(), ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID);
            ZB_ZCL_FINISH_PACKET(bufid, ptr)
            ZB_ZCL_SEND_COMMAND_SHORT(bufid, addr, ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
                                      11, LIGHT_SWITCH_ENDPOINT,
                                      ZB_AF_HA_PROFILE_ID, ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, NULL);
        }
    }
    
    /**@brief Function for sending step requests to the light bulb.
     *
     * @param[in]   bufid        Non-zero reference to Zigbee stack buffer that will be used to construct step request.
     * @param[in]   is_step_up   Boolean parameter selecting direction of step change.
     */
    static zb_void_t light_switch_send_step(zb_bufid_t bufid, zb_uint16_t is_step_up)
    {
        zb_uint8_t step_dir;
    
        if (is_step_up)
        {
            step_dir = ZB_ZCL_LEVEL_CONTROL_STEP_MODE_UP;
        }
        else
        {
            step_dir = ZB_ZCL_LEVEL_CONTROL_STEP_MODE_DOWN;
        }
    
        NRF_LOG_INFO("Send step level command: %d", is_step_up);
    
        ZB_ZCL_LEVEL_CONTROL_SEND_STEP_REQ(bufid,
                                           m_device_ctx.bulb_params.short_addr,
                                           ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
                                           m_device_ctx.bulb_params.endpoint,
                                           LIGHT_SWITCH_ENDPOINT,
                                           ZB_AF_HA_PROFILE_ID,
                                           ZB_ZCL_DISABLE_DEFAULT_RESPONSE,
                                           NULL,
                                           step_dir,
                                           LIGHT_SWITCH_DIMM_STEP,
                                           LIGHT_SWITCH_DIMM_TRANSACTION_TIME);
    }
    
    /**@brief Callback function receiving finding procedure results.
     *
     * @param[in]   bufid   Reference to Zigbee stack buffer used to pass received data.
     */
    static zb_void_t find_light_bulb_cb(zb_bufid_t bufid)
    {
        zb_zdo_match_desc_resp_t   * p_resp = (zb_zdo_match_desc_resp_t *) zb_buf_begin(bufid);    // Get the beginning of the response
        zb_apsde_data_indication_t * p_ind  = ZB_BUF_GET_PARAM(bufid, zb_apsde_data_indication_t); // Get the pointer to the parameters buffer, which stores APS layer response
        zb_uint8_t                 * p_match_ep;
        zb_ret_t                     zb_err_code;
    
        if ((p_resp->status == ZB_ZDP_STATUS_SUCCESS) && (p_resp->match_len > 0) && (m_device_ctx.bulb_params.short_addr == 0xFFFF))
        {
            /* Match EP list follows right after response header */
            p_match_ep = (zb_uint8_t *)(p_resp + 1);
    
            /* We are searching for exact cluster, so only 1 EP may be found */
            m_device_ctx.bulb_params.endpoint   = *p_match_ep;
            m_device_ctx.bulb_params.short_addr = p_ind->src_addr;
    
            NRF_LOG_INFO("Found bulb addr: %d ep: %d", m_device_ctx.bulb_params.short_addr, m_device_ctx.bulb_params.endpoint);
    
            zb_err_code = ZB_SCHEDULE_APP_ALARM_CANCEL(find_light_bulb_timeout, ZB_ALARM_ANY_PARAM);
            ZB_ERROR_CHECK(zb_err_code);
    
            bsp_board_led_on(BULB_FOUND_LED);
        }
    
        if (bufid)
        {
            zb_buf_free(bufid);
        }
    }
    
    /**@brief Function for sending ON/OFF and Level Control find request.
     *
     * @param[in]   bufid   Non-zero reference to Zigbee stack buffer that will be used to construct find request.
     */
    static zb_void_t find_light_bulb(zb_bufid_t bufid)
    {
        zb_zdo_match_desc_param_t * p_req;
    
        /* Initialize pointers inside buffer and reserve space for zb_zdo_match_desc_param_t request */
        p_req = zb_buf_initial_alloc(bufid, sizeof(zb_zdo_match_desc_param_t) + (1) * sizeof(zb_uint16_t));
    
        p_req->nwk_addr         = MATCH_DESC_REQ_ROLE;              // Send to devices specified by MATCH_DESC_REQ_ROLE
        p_req->addr_of_interest = MATCH_DESC_REQ_ROLE;              // Get responses from devices specified by MATCH_DESC_REQ_ROLE
        p_req->profile_id       = ZB_AF_HA_PROFILE_ID;              // Look for Home Automation profile clusters
    
        ///* We are searching for 2 clusters: On/Off and Level Control Server */
        //// Code nay van chay ok, day la so luong zcl tren remote device. Phai khai bao so luong nay moi duoc.
        //p_req->num_in_clusters  = 3;  // 3 la onoff + level + temp
        //p_req->num_out_clusters = 1;  // 1 la onoff
        //p_req->cluster_list[0]  = ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT;   // ZB_ZCL_CLUSTER_ID_CUSTOM_ATTR cung duoc
    
        /* We are searching for 2 clusters: On/Off and Level Control Server */
        p_req->num_in_clusters  = 3;  // 3 la onoff + level + temp
        p_req->num_out_clusters = 1;  // 1 la onoff
        p_req->cluster_list[0]  = ZB_ZCL_CLUSTER_ID_ON_OFF;
        p_req->cluster_list[1]  = ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL;
        p_req->cluster_list[2]  = ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT;
        p_req->cluster_list[3]  = ZB_ZCL_CLUSTER_ID_ON_OFF;
    
        /*lint -restore */
        m_device_ctx.bulb_params.short_addr = 0xFFFF; // Set 0xFFFF to reset short address in order to parse only one response.
        UNUSED_RETURN_VALUE(zb_zdo_match_desc_req(bufid, find_light_bulb_cb));
    }
    
    /**@brief Finding procedure timeout handler.
     *
     * @param[in]   bufid   Reference to Zigbee stack buffer that will be used to construct find request.
     */
    static zb_void_t find_light_bulb_timeout(zb_bufid_t bufid)
    {
        zb_ret_t zb_err_code;
    
        if (bufid)
        {
            NRF_LOG_INFO("Bulb not found, try again");
            zb_err_code = ZB_SCHEDULE_APP_ALARM(find_light_bulb, bufid, MATCH_DESC_REQ_START_DELAY);
            ZB_ERROR_CHECK(zb_err_code);
            zb_err_code = ZB_SCHEDULE_APP_ALARM(find_light_bulb_timeout, 0, MATCH_DESC_REQ_TIMEOUT);
            ZB_ERROR_CHECK(zb_err_code);
        }
        else
        {
            zb_err_code = zb_buf_get_out_delayed(find_light_bulb_timeout);
            ZB_ERROR_CHECK(zb_err_code);
        }
    }
    
    /**@brief Callback for detecting button press duration.
     *
     * @param[in]   button   BSP Button that was pressed.
     */
    static zb_void_t light_switch_button_handler(zb_uint8_t button)
    {
        zb_time_t current_time;
        zb_bool_t short_expired;
        zb_bool_t on_off;
        zb_ret_t zb_err_code;
    
        current_time = ZB_TIMER_GET();
    
        if (button == LIGHT_SWITCH_BUTTON_ON)
        {
            on_off = ZB_TRUE;
        }
        else
        {
            on_off = ZB_FALSE;
        }
    
        if (ZB_TIME_SUBTRACT(current_time, m_device_ctx.button.timestamp) > LIGHT_SWITCH_BUTTON_THRESHOLD)
        {
            short_expired = ZB_TRUE;
        }
        else
        {
            short_expired = ZB_FALSE;
        }
    
        /* Check if button was released during LIGHT_SWITCH_BUTTON_SHORT_POLL_TMO. */
        if (!bsp_button_is_pressed(button))
        {
            if (!short_expired)
            {
                /* Allocate output buffer and send on/off command. */
                zb_err_code = zb_buf_get_out_delayed_ext(light_switch_send_on_off, addr_oss, 0);
                ZB_ERROR_CHECK(zb_err_code);
            }
    
            /* Button released - wait for accept next event. */
            m_device_ctx.button.in_progress = ZB_FALSE;
        }
        else
        {
            if (short_expired)
            {
                /* The button is still pressed - allocate output buffer and send step command. */
                zb_err_code = zb_buf_get_out_delayed_ext(light_switch_send_step, on_off, 0);
                ZB_ERROR_CHECK(zb_err_code);
                zb_err_code = ZB_SCHEDULE_APP_ALARM(light_switch_button_handler, button, LIGHT_SWITCH_BUTTON_LONG_POLL_TMO);
                if (zb_err_code == RET_OVERFLOW)
                {
                    NRF_LOG_WARNING("Can not schedule another alarm, queue is full.");
                    m_device_ctx.button.in_progress = ZB_FALSE;
                }
                else
                {
                    ZB_ERROR_CHECK(zb_err_code);
                }
            }
            else
            {
                /* Wait another LIGHT_SWITCH_BUTTON_SHORT_POLL_TMO, until LIGHT_SWITCH_BUTTON_THRESHOLD will be reached. */
                zb_err_code = ZB_SCHEDULE_APP_ALARM(light_switch_button_handler, button, LIGHT_SWITCH_BUTTON_SHORT_POLL_TMO);
                if (zb_err_code == RET_OVERFLOW)
                {
                    NRF_LOG_WARNING("Can not schedule another alarm, queue is full.");
                    m_device_ctx.button.in_progress = ZB_FALSE;
                }
                else
                {
                    ZB_ERROR_CHECK(zb_err_code);
                }
            }
        }
    }
    
    /**@brief Callback for button events.
     *
     * @param[in]   evt      Incoming event from the BSP subsystem.
     */
    static void buttons_handler(bsp_event_t evt)
    {
        zb_ret_t zb_err_code;
        zb_uint32_t button;
    
        /* Inform default signal handler about user input at the device. */
        user_input_indicate();
    
        if (m_device_ctx.bulb_params.short_addr == 0xFFFF)
        {
            /* No bulb found yet. */
            return;
        }
    
        switch(evt)
        {
            case BSP_EVENT_KEY_0:
                addr_oss = ADDR_BULB1;
                button = LIGHT_SWITCH_BUTTON_ON;
                break;
    
            case BSP_EVENT_KEY_1:
                addr_oss = ADDR_BULB2;
                button = LIGHT_SWITCH_BUTTON_OFF;
                break;
    
            case BSP_EVENT_KEY_2:
                addr_oss = 0x1111;
                break;
    
            default:
                NRF_LOG_INFO("Unhandled BSP Event received: %d", evt);
                return;
        }
    
        if (!m_device_ctx.button.in_progress)
        {
            m_device_ctx.button.in_progress = ZB_TRUE;
            m_device_ctx.button.timestamp = ZB_TIMER_GET();
    
            zb_err_code = ZB_SCHEDULE_APP_ALARM(light_switch_button_handler, button, LIGHT_SWITCH_BUTTON_SHORT_POLL_TMO);
            if (zb_err_code == RET_OVERFLOW)
            {
                NRF_LOG_WARNING("Can not schedule another alarm, queue is full.");
                m_device_ctx.button.in_progress = ZB_FALSE;
            }
            else
            {
                ZB_ERROR_CHECK(zb_err_code);
            }
        }
    }
    
    /**@brief Function for initializing LEDs and buttons.
     */
    static zb_void_t leds_buttons_init(void)
    {
        ret_code_t error_code;
    
        /* Initialize LEDs and buttons - use BSP to control them. */
        error_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, buttons_handler);
        APP_ERROR_CHECK(error_code);
        /* By default the bsp_init attaches BSP_KEY_EVENTS_{0-4} to the PUSH events of the corresponding buttons. */
    
        bsp_board_leds_off();
    }
    
    /**@brief Function to set the Sleeping Mode according to the SLEEPY_ON_BUTTON state.
    */
    static zb_void_t sleepy_device_setup(void)
    {
        zb_set_rx_on_when_idle(bsp_button_is_pressed(SLEEPY_ON_BUTTON) ? ZB_FALSE : ZB_TRUE);
    
    #if ! defined DISABLE_POWER_CONSUMPTION_OPTIMIZATION
        /* If sleepy behaviour is enabled, power off unused RAM to save maximum energy */
        if (ZB_PIBCACHE_RX_ON_WHEN_IDLE() == ZB_FALSE)
        {
            zigbee_power_down_unused_ram();
        }
    #endif /* ! defined DISABLE_POWER_CONSUMPTION_OPTIMIZATION */
    }
    
    static zb_void_t zcl_device_cb(zb_bufid_t bufid)
    {
        // Minh them vao
        if (NRF_P0->OUT & (1<<3))
            NRF_P0->OUTCLR = (1<<3);
        else
            NRF_P0->OUTSET = (1<<3);
    
        zb_uint8_t                       cluster_id;
        zb_uint8_t                       attr_id;
        zb_zcl_device_callback_param_t * p_device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);
    
        /* Set default response value. */
        p_device_cb_param->status = RET_OK;
    
        switch (p_device_cb_param->device_cb_id)
        {
            case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
                cluster_id = p_device_cb_param->cb_param.set_attr_value_param.cluster_id;
                attr_id    = p_device_cb_param->cb_param.set_attr_value_param.attr_id;
    
                if (cluster_id == ZB_ZCL_CLUSTER_ID_ON_OFF)
                {
                    uint8_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data8;
                    if (attr_id == ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID)
                    {
                        if (NRF_P0->OUT & (1<<13))  // LED1
                            NRF_P0->OUTCLR = (1<<13);
                        else
                            NRF_P0->OUTSET = (1<<13);
                    }
                }
                break;
    
            default:
                p_device_cb_param->status = RET_ERROR;
                break;
        }
    }
    
    /**@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)
    {
        zb_zdo_app_signal_hdr_t      * p_sg_p = NULL;
        zb_zdo_app_signal_type_t       sig    = zb_get_app_signal(bufid, &p_sg_p);
        zb_ret_t                       status = ZB_GET_APP_SIGNAL_STATUS(bufid);
        zb_ret_t                       zb_err_code;
    
        /* Update network status LED */
        zigbee_led_status_update(bufid, ZIGBEE_NETWORK_STATE_LED);
    
        switch(sig)
        {
            case ZB_BDB_SIGNAL_DEVICE_REBOOT:
                /* fall-through */
            case ZB_BDB_SIGNAL_STEERING:
                /* Call default signal handler. */
                ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
                if (status == RET_OK)
                {
                    /* Check the light device address */
                    if (m_device_ctx.bulb_params.short_addr == 0xFFFF)
                    {
                        zb_err_code = ZB_SCHEDULE_APP_ALARM(find_light_bulb, bufid, MATCH_DESC_REQ_START_DELAY);
                        ZB_ERROR_CHECK(zb_err_code);
                        zb_err_code = ZB_SCHEDULE_APP_ALARM(find_light_bulb_timeout, 0, MATCH_DESC_REQ_TIMEOUT);
                        ZB_ERROR_CHECK(zb_err_code);
                        bufid = 0; // Do not free buffer - it will be reused by find_light_bulb callback
                    }
                }
                break;
    
            default:
                /* Call default signal handler. */
                ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
                break;
        }
    
        if (bufid)
        {
            zb_buf_free(bufid);
        }
    }
    
    /**@brief Function for application main entry.
     */
    int main(void)
    {
        zb_ret_t       zb_err_code;
        zb_ieee_addr_t ieee_addr;
    
        /* Initialize timers, loging system and GPIOs. */
        timers_init();
        log_init();
        leds_buttons_init();
    
        // Minh them vao. Den nhay bao hieu dang chay.
        NRF_P0->PIN_CNF[2] = 1;  // Disconnect input buffer + Output, P0.2
        NRF_P0->OUTSET = (1<<2);
        NRF_P0->PIN_CNF[3] = 1;  // Disconnect input buffer + Output, P0.3
        NRF_P0->OUTSET = (1<<3);
    
        /* Set Zigbee stack logging level and traffic dump subsystem. */
        ZB_SET_TRACE_LEVEL(ZIGBEE_TRACE_LEVEL);
        ZB_SET_TRACE_MASK(ZIGBEE_TRACE_MASK);
        ZB_SET_TRAF_DUMP_OFF();
    
        /* Initialize Zigbee stack. */
        ZB_INIT("light_switch");
    
        /* Set device address to the value read from FICR registers. */
        zb_osif_get_ieee_eui64(ieee_addr);
        zb_set_long_address(ieee_addr);
    
        zb_set_network_ed_role(IEEE_CHANNEL_MASK);
        zigbee_erase_persistent_storage(ERASE_PERSISTENT_CONFIG);
    
        zb_set_ed_timeout(ED_AGING_TIMEOUT_64MIN);
        zb_set_keepalive_timeout(ZB_MILLISECONDS_TO_BEACON_INTERVAL(3000));
        sleepy_device_setup();
    
        /* Initialize application context structure. */
        UNUSED_RETURN_VALUE(ZB_MEMSET(&m_device_ctx, 0, sizeof(light_switch_ctx_t)));
    
        /* Set default bulb short_addr. */
        m_device_ctx.bulb_params.short_addr = 0xFFFF;
    
        // Khai bao callback nhan du lieu
        ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);
    
        /* Register dimmer switch device context (endpoints). */
        ZB_AF_REGISTER_DEVICE_CTX(&dimmer_switch_ctx);
    
        /** Start Zigbee Stack. */
        zb_err_code = zboss_start_no_autostart();
        ZB_ERROR_CHECK(zb_err_code);
    
        while(1)
        {
            zboss_main_loop_iteration();
            UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
        }
    }
    

    Thanks

  • Hi,

    In ZB_ZCL_CONSTRUCT_COMMAND_HEADER_REQ you are using ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID as cmd_id. cmd_id is the command identifier, and must be the ID of a command, such as ZB_ZCL_CMD_ON_OFF_OFF_ID. However, ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID is not a command, but the ID of the attribute MeasuredValue. You can therefore not get the temperature measurement using this. If you read about the temperature measurement cluster in the Zigbee Cluster Library specification (chapter 4.4 in revision 6), you can see that the cluster does not have any commands, only attributes. You can get MeasuredValue by either using the read attributes command or report attributes command. Since you want to get the temperature measurement after a button press and not receive it periodically, read attributes command would be the best choice.

    You can read detailed information about the read attributes command in the ZCL spec (chapter 2.5.1 in revision 6). There you can see when this command is generated, effect on receipts, as well as information regarding the read attribute response command. For more information about how to implement this in our SDK, you can check out Read attributes request and response sending and parsing where you can find macros and more detailed descriptions. In order to receive MeasuredValue from the light bulb, the light switch will have to send a read attributes request commend, which can be formed and sent as below:

    ZB_ZCL_GENERAL_INIT_READ_ATTR_REQ(zcl_cmd_buf, cmd_ptr, ZB_ZCL_ENABLE_DEFAULT_RESPONSE);
    ZB_ZCL_GENERAL_ADD_ID_READ_ATTR_REQ(cmd_ptr, ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID);
    ZB_ZCL_GENERAL_SEND_READ_ATTR_REQ(zcl_cmd_buf, cmd_ptr, DUT_ADDR, DUT_ADDR_MODE, DUT_ENDPOINT,
                                     TH_ENDPOINT, ZB_AF_HA_PROFILE_ID,
                                     ZB_ZCL_CLUSTER_ID_BINARY_INPUT, NULL);

    When the light bulb receives the read attributes request it will generate a read attributes response as a response to this. The read attributes response can be parsed as:

    ZB_ZCL_GENERAL_GET_NEXT_READ_ATTR_RES(buf, read_attr_resp);

    The documentation for all of these macros can be found in the link I added above. If you want to see an example where this is used, you can check out the function readattr_send() on line 324 in /components/zigbee/cli/zigbee_cli_cmd_attr.c.

    Best regards,

    Marte

Related