Zigbee Network steering was not successful (status: -1)

Hi,

I'm trying to implement zigbee into our application. For testing purposes I start with the example code, and I use the multiprotocol template as a starting point.

I also referred to https://developer.nordicsemi.com/nRF_Connect_SDK/doc/zboss/3.5.0.0/group__ha__window__covering.html as sample code.

When I implement all this, it succesfully compiles. However when I try to let it connect to the coordinator, I get these debug messages:

<info> app: Zigbee signal received (signal: 7, status: -1)
<info> app: Network steering was not successful (status: -1)

I don't know what I'm doing wrong, I checked a lot of things. I must say the samples for zigbee are quite limited compared to BLE.

Can someone please take a look what I should change or how I can debug this properly so that I know what I need to change?

Below the code:

/**
 * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
/** @file
 *
 * @defgroup zigbee_examples_multiprotocol_template main.c
 * @{
 * @ingroup zigbee_examples
 * @brief Template project main file for dynamic BLE and Zigbee.
 *
 * This file contains a template for creating a new multiprotocol BLE + Zigbee application.
 * In terms of BLE, it has the code necessary to wake up from button, advertise,
 * get a connection, restart advertising on disconnect and if no new connection was created
 * go back to system-off mode.
 * In terms of Zigbee, it has the code implementing the Range Extender Device - a
 * simple router which is just relaying packets.
 * It can easily be used as a starting point for creating a new application, the comments identified
 * with 'YOUR_JOB' indicates where and how you can customize.
 *
 */

#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#include "zboss_api.h"
#include "zb_mem_config_med.h"
#include "zb_error_handler.h"
#include "zigbee_helpers.h"
#include "zboss_api_addons.h"
#include "nordic_common.h"
#include "nrf.h"
#include "app_error.h"
#include "app_scheduler.h"
#include "ble.h"
#include "ble_hci.h"
#include "ble_srv_common.h"
#include "ble_advdata.h"
#include "ble_advertising.h"
#include "ble_conn_params.h"
#include "nrf_assert.h"
#include "nrf_sdh.h"
#include "nrf_sdh_soc.h"
#include "nrf_sdh_ble.h"
#include "app_timer.h"
#include "fds.h"
#include "peer_manager.h"
#include "peer_manager_handler.h"
#include "bsp_btn_ble.h"
#include "ble_conn_state.h"
#include "nrf_ble_gatt.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#define APP_FEATURE_NOT_SUPPORTED       BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2    /**< Reply when unsupported features are requested. */

#define DEVICE_NAME                     "Nordic_Template"                       /**< Name of device. Will be included in the advertising data. */
#define MANUFACTURER_NAME               "NordicSemiconductor"                   /**< Manufacturer. Will be passed to Device Information Service. */
#define APP_ADV_INTERVAL                320                                     /**< The advertising interval (in units of 0.625 ms. This value corresponds to 200 ms). */
#define SLEEPY_ON_BUTTON                    BSP_BOARD_BUTTON_2                  /**< Button ID used to determine if we need the sleepy device behaviour (pressed means yes). */
#define APP_ADV_DURATION                18000                                   /**< The advertising duration (180 seconds) in units of 10 milliseconds. */

#define APP_BLE_OBSERVER_PRIO           1                                       /**< Application's BLE observer priority. You shouldn't need to modify this value. */
#define APP_BLE_CONN_CFG_TAG            1                                       /**< A tag identifying the SoftDevice BLE configuration. */

#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(100, UNIT_1_25_MS)        /**< Minimum acceptable connection interval (0.1 seconds). */
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(200, UNIT_1_25_MS)        /**< Maximum acceptable connection interval (0.2 second). */
#define SLAVE_LATENCY                   0                                       /**< Slave latency. */
#define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)         /**< Connection supervisory timeout (4 seconds). */

#define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                   /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                  /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT    3                                       /**< Number of attempts before giving up the connection parameter negotiation. */

#define SEC_PARAM_BOND                  1                                       /**< Perform bonding. */
#define SEC_PARAM_MITM                  0                                       /**< Man In The Middle protection not required. */
#define SEC_PARAM_LESC                  0                                       /**< LE Secure Connections not enabled. */
#define SEC_PARAM_KEYPRESS              0                                       /**< Keypress notifications not enabled. */
#define SEC_PARAM_IO_CAPABILITIES       BLE_GAP_IO_CAPS_NONE                    /**< No I/O capabilities. */
#define SEC_PARAM_OOB                   0                                       /**< Out Of Band data not available. */
#define SEC_PARAM_MIN_KEY_SIZE          7                                       /**< Minimum encryption key size. */
#define SEC_PARAM_MAX_KEY_SIZE          16                                      /**< Maximum encryption key size. */

#define DEAD_BEEF                       0xDEADBEEF                              /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */

#define SCHED_QUEUE_SIZE                32                                      /**< Maximum number of events in the scheduler queue. */
#define SCHED_EVENT_DATA_SIZE           APP_TIMER_SCHED_EVENT_DATA_SIZE         /**< Maximum app_scheduler event size. */

#define MAX_CHILDREN                    10                                      /**< The maximum amount of connected devices. Setting this value to 0 disables association to this device.  */
#define RANGE_EXTENDER_ENDPOINT         7                                       /**< Endpoint for the Zigbee Range Extender 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 IEEE_CHANNEL_MASK               (1l << ZIGBEE_CHANNEL)                  /**< Scan only one, predefined channel to find the coordinator. */
#define ZIGBEE_NETWORK_STATE_LED        BSP_BOARD_LED_2                         /**< LED indicating that light switch successfully joind Zigbee network. */
#define LIGHT_SWITCH_ENDPOINT           1                                   /**< Source endpoint used to control light bulb. */
#define ENDPOINT_C          1
#define ZB_OUTPUT_ENDPOINT 1

NRF_BLE_GATT_DEF(m_gatt);                                                       /**< GATT module instance. */
BLE_ADVERTISING_DEF(m_advertising);                                             /**< Advertising module instance. */



zb_uint8_t zcl_specific_cluster_cmd_handler(zb_uint8_t param)
{
  zb_zcl_parsed_hdr_t cmd_info;
  zb_uint8_t lqi = ZB_MAC_LQI_UNDEFINED;
  zb_int8_t rssi = ZB_MAC_RSSI_UNDEFINED;
 
  TRACE_MSG(TRACE_APP1, "> zcl_specific_cluster_cmd_handler", (FMT__0));
 
  ZB_ZCL_COPY_PARSED_HEADER(param, &cmd_info);
 
  uint16_t g_dst_addr = ZB_ZCL_PARSED_HDR_SHORT_DATA(&cmd_info).source.u.short_addr;
 
  ZB_ZCL_DEBUG_DUMP_HEADER(&cmd_info);
  TRACE_MSG(TRACE_APP3, "payload size: %i", (FMT__D, zb_buf_len(param)));
 
  zb_zdo_get_diag_data(g_dst_addr, &lqi, &rssi);
  TRACE_MSG(TRACE_APP3, "lqi %hd rssi %d", (FMT__H_H, lqi, rssi));
 
  if (cmd_info.cmd_direction == ZB_ZCL_FRAME_DIRECTION_TO_CLI)
  {
    TRACE_MSG(
        TRACE_ERROR,
        "Unsupported \"from server\" command direction",
        (FMT__0));
  }
 
  TRACE_MSG(TRACE_APP1, "< zcl_specific_cluster_cmd_handler", (FMT__0));
  return ZB_FALSE;
}


void test_device_interface_cb(zb_uint8_t param)
{
 
  zb_zcl_device_callback_param_t *device_cb_param =
    ZB_BUF_GET_PARAM(param, zb_zcl_device_callback_param_t);
 
  TRACE_MSG(TRACE_APP1, "> test_device_interface_cb param %hd id %hd", (FMT__H_H,
      param, device_cb_param->device_cb_id));
 
  device_cb_param->status = RET_OK;
 
  switch (device_cb_param->device_cb_id)
  {
    case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
      if (device_cb_param->cb_param.set_attr_value_param.cluster_id == ZB_ZCL_CLUSTER_ID_ON_OFF &&
          device_cb_param->cb_param.set_attr_value_param.attr_id == ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID)
      {
        if (device_cb_param->cb_param.set_attr_value_param.values.data8)
        {
          TRACE_MSG(TRACE_APP1, "set ON", (FMT__0));
#ifdef ZB_USE_BUTTONS
          zb_osif_led_on(0);
#endif
        }
        else
        {
          TRACE_MSG(TRACE_APP1, "set OFF", (FMT__0));
#ifdef ZB_USE_BUTTONS
          zb_osif_led_off(0);
#endif
        }
      }
      break;
    case ZB_ZCL_WINDOW_COVERING_UP_OPEN_CB_ID:
    {
      zb_uint8_t lift_percentage_val = 0x00;
      zb_uint8_t tilt_percentage_val = 0x00;
 
      ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                   ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                   ZB_ZCL_CLUSTER_SERVER_ROLE,
                                   ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID,
                                   &lift_percentage_val,
                                   ZB_FALSE));
 
      ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                   ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                   ZB_ZCL_CLUSTER_SERVER_ROLE,
                                   ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID,
                                   &tilt_percentage_val,
                                   ZB_FALSE));
      break;
    }
    case ZB_ZCL_WINDOW_COVERING_DOWN_CLOSE_CB_ID:
    {
      zb_uint8_t lift_percentage_val = 0x64;
      zb_uint8_t tilt_percentage_val = 0x64;
 
      ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                   ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                   ZB_ZCL_CLUSTER_SERVER_ROLE,
                                   ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID,
                                   &lift_percentage_val,
                                   ZB_FALSE));
 
      ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                   ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                   ZB_ZCL_CLUSTER_SERVER_ROLE,
                                   ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID,
                                   &tilt_percentage_val,
                                   ZB_FALSE));
      break;
    }
    case ZB_ZCL_WINDOW_COVERING_STOP_CB_ID:
      break;
    case ZB_ZCL_WINDOW_COVERING_GO_TO_LIFT_PERCENTAGE_CB_ID:
    {
      const zb_zcl_go_to_lift_percentage_req_t *lift_percentage = ZB_ZCL_DEVICE_CMD_PARAM_IN_GET(param, zb_zcl_go_to_lift_percentage_req_t);
      zb_zcl_attr_t *attr_desc = zb_zcl_get_attr_desc_a(ENDPOINT_C,
          ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID);
      if (attr_desc)
      {
        ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                     ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                     ZB_ZCL_CLUSTER_SERVER_ROLE,
                                     ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID,
                                     (zb_uint8_t *)&(lift_percentage->percentage_lift_value),
                                     ZB_FALSE));
      }
      break;
    }
    case ZB_ZCL_WINDOW_COVERING_GO_TO_TILT_PERCENTAGE_CB_ID:
    {
      const zb_zcl_go_to_tilt_percentage_req_t *tilt_percentage = ZB_ZCL_DEVICE_CMD_PARAM_IN_GET(param, zb_zcl_go_to_tilt_percentage_req_t);
      zb_zcl_attr_t *attr_desc = zb_zcl_get_attr_desc_a(ENDPOINT_C,
            ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID);
      if (attr_desc)
      {
        ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                     ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                     ZB_ZCL_CLUSTER_SERVER_ROLE,
                                     ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID,
                                     (zb_uint8_t *)&(tilt_percentage->percentage_tilt_value),
                                     ZB_FALSE));
      }
      break;
    }
    /* >>>> Scenes */
    //case ZB_ZCL_SCENES_STORE_SCENE_CB_ID:
    //{
    //  const zb_zcl_scenes_store_scene_req_t *store_scene_req = ZB_ZCL_DEVICE_CMD_PARAM_IN_GET(param, zb_zcl_scenes_store_scene_req_t);
    //  zb_uint8_t idx = 0xFF;
    //  zb_uint8_t *store_scene_status = ZB_ZCL_DEVICE_CMD_PARAM_OUT_GET(param, zb_uint8_t);
    //  const zb_zcl_parsed_hdr_t *in_cmd_info = ZB_ZCL_DEVICE_CMD_PARAM_CMD_INFO(param);
 
    //  TRACE_MSG(TRACE_APP1, "ZB_ZCL_SCENES_STORE_SCENE_CB_ID: group_id 0x%x scene_id 0x%hd", (FMT__D_H, store_scene_req->group_id, store_scene_req->scene_id));
 
    //  if (!zb_aps_is_endpoint_in_group(
    //             store_scene_req->group_id,
    //             ZB_ZCL_PARSED_HDR_SHORT_DATA(in_cmd_info).dst_endpoint))
    //  {
    //    *store_scene_status = ZB_ZCL_STATUS_INVALID_FIELD;
    //  }
    //  else
    //  {
    //    idx = test_device_scenes_get_entry(store_scene_req->group_id, store_scene_req->scene_id);
 
    //    if (idx != 0xFF)
    //    {
    //      if (scenes_table[idx].common.group_id != ZB_ZCL_SCENES_FREE_SCENE_TABLE_RECORD)
    //      {
    //        /* Update existing entry with current On/Off state */
    //        device_cb_param->status = RET_ALREADY_EXISTS;
    //        scenes_table[idx].lift_percentage_state = g_attr_window_covering_current_position_lift_percentage;
    //        scenes_table[idx].tilt_percentage_state = g_attr_window_covering_current_position_tilt_percentage;
    //      }
    //      else
    //      {
    //        /* Create new entry with empty name and 0 transition time */
    //        scenes_table[idx].common.group_id = store_scene_req->group_id;
    //        scenes_table[idx].common.scene_id = store_scene_req->scene_id;
    //        scenes_table[idx].common.transition_time = 0;
    //        scenes_table[idx].lift_percentage_state = g_attr_window_covering_current_position_lift_percentage;
    //        scenes_table[idx].tilt_percentage_state = g_attr_window_covering_current_position_tilt_percentage;
    //      }
 
    //      *store_scene_status = ZB_ZCL_STATUS_SUCCESS;
    //    }
    //    else
    //    {
    //      *store_scene_status = ZB_ZCL_STATUS_INSUFF_SPACE;
    //    }
    //  }
    //}
    //break;
    //case ZB_ZCL_SCENES_RECALL_SCENE_CB_ID:
    //{
    //  const zb_zcl_scenes_recall_scene_req_t *recall_scene_req = ZB_ZCL_DEVICE_CMD_PARAM_IN_GET(param, zb_zcl_scenes_recall_scene_req_t);
    //  zb_uint8_t idx = 0xFF;
    //  zb_uint8_t *recall_scene_status = ZB_ZCL_DEVICE_CMD_PARAM_OUT_GET(param, zb_uint8_t);
 
    //  TRACE_MSG(TRACE_APP1, "ZB_ZCL_SCENES_RECALL_SCENE_CB_ID: group_id 0x%x scene_id 0x%hd", (FMT__D_H, recall_scene_req->group_id, recall_scene_req->scene_id));
 
    //  idx = test_device_scenes_get_entry(recall_scene_req->group_id, recall_scene_req->scene_id);
 
    //  if (idx != 0xFF &&
    //      scenes_table[idx].common.group_id != ZB_ZCL_SCENES_FREE_SCENE_TABLE_RECORD)
    //  {
    //    /* Recall this entry */
    //    ZB_ZCL_SET_ATTRIBUTE(
    //      ENDPOINT_C,
    //      ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
    //      ZB_ZCL_CLUSTER_SERVER_ROLE,
    //      ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID,
    //      &scenes_table[idx].lift_percentage_state,
    //      ZB_FALSE);
    //    ZB_ZCL_SET_ATTRIBUTE(
    //      ENDPOINT_C,
    //      ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
    //      ZB_ZCL_CLUSTER_SERVER_ROLE,
    //      ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID,
    //      &scenes_table[idx].tilt_percentage_state,
    //      ZB_FALSE);
    //    *recall_scene_status = ZB_ZCL_STATUS_SUCCESS;
    //  }
    //  else
    //  {
    //    *recall_scene_status = ZB_ZCL_STATUS_NOT_FOUND;
    //  }
    //}
    //break;
    //case ZB_ZCL_SCENES_INTERNAL_REMOVE_ALL_SCENES_ALL_ENDPOINTS_ALL_GROUPS_CB_ID:
    //{
    //  test_device_scenes_table_init();
    //}
    //break;
      /* <<<< Scenes */
    default:
      device_cb_param->status = RET_OK;
      break;
  }
 
  TRACE_MSG(TRACE_APP1, "< test_device_interface_cb %hd", (FMT__H, device_cb_param->status));
}


  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;

} on_off_switch_ctx_t;

  on_off_switch_ctx_t m_dev_ctx;


zb_uint16_t g_attr_identify_identify_time = ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE;
/* Groups cluster attributes data */
zb_uint8_t g_attr_groups_name_support = 0;
/* Scenes cluster attributes data */
zb_uint8_t g_attr_scenes_scene_count = ZB_ZCL_SCENES_SCENE_COUNT_DEFAULT_VALUE;
zb_uint8_t g_attr_scenes_current_scene = ZB_ZCL_SCENES_CURRENT_SCENE_DEFAULT_VALUE;
zb_uint16_t g_attr_scenes_current_group = ZB_ZCL_SCENES_CURRENT_GROUP_DEFAULT_VALUE;
zb_uint8_t g_attr_scenes_scene_valid = ZB_ZCL_SCENES_SCENE_VALID_DEFAULT_VALUE;
zb_uint8_t g_attr_scenes_name_support = 0;
/* Window Covering cluster attributes data */
zb_uint8_t g_attr_window_covering_window_covering_type = ZB_ZCL_WINDOW_COVERING_WINDOW_COVERING_TYPE_DEFAULT_VALUE;
zb_uint8_t g_attr_window_covering_config_status = ZB_ZCL_WINDOW_COVERING_CONFIG_STATUS_DEFAULT_VALUE;
zb_uint8_t g_attr_window_covering_current_position_lift_percentage = ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_DEFAULT_VALUE;
zb_uint8_t g_attr_window_covering_current_position_tilt_percentage = 0x32;//ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_DEFAULT_VALUE;
zb_uint16_t g_attr_window_covering_installed_open_limit_lift = ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_DEFAULT_VALUE;
zb_uint16_t g_attr_window_covering_installed_closed_limit_lift = ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_DEFAULT_VALUE;
zb_uint16_t g_attr_window_covering_installed_open_limit_tilt = ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_DEFAULT_VALUE;
zb_uint16_t g_attr_window_covering_installed_closed_limit_tilt = ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_DEFAULT_VALUE;
zb_uint8_t g_attr_window_covering_mode = ZB_ZCL_WINDOW_COVERING_MODE_DEFAULT_VALUE;


  /* Basic cluster attributes data */
  zb_uint8_t g_attr_basic_zcl_version = ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE;
  zb_uint8_t g_attr_basic_application_version = ZB_ZCL_BASIC_APPLICATION_VERSION_DEFAULT_VALUE;
  zb_uint8_t g_attr_basic_stack_version = ZB_ZCL_BASIC_STACK_VERSION_DEFAULT_VALUE;
  zb_uint8_t g_attr_basic_hw_version = ZB_ZCL_BASIC_HW_VERSION_DEFAULT_VALUE;
  zb_char_t g_attr_basic_manufacturer_name[] = ZB_ZCL_BASIC_MANUFACTURER_NAME_DEFAULT_VALUE;
  zb_char_t g_attr_basic_model_identifier[] = ZB_ZCL_BASIC_MODEL_IDENTIFIER_DEFAULT_VALUE;
  zb_char_t g_attr_basic_date_code[] = ZB_ZCL_BASIC_DATE_CODE_DEFAULT_VALUE;
  zb_uint8_t g_attr_basic_power_source = ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE;
  zb_char_t g_attr_basic_location_description[] = ZB_ZCL_BASIC_LOCATION_DESCRIPTION_DEFAULT_VALUE;
  zb_uint8_t g_attr_basic_physical_environment = ZB_ZCL_BASIC_PHYSICAL_ENVIRONMENT_DEFAULT_VALUE;
  zb_char_t g_attr_sw_build_id[] = {0};

static void on_off_switch_clusters_attr_init(void) {



    m_dev_ctx.basic_attr.zcl_version   = ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE;
    m_dev_ctx.basic_attr.app_version   = ZB_ZCL_BASIC_APPLICATION_VERSION_DEFAULT_VALUE;
    m_dev_ctx.basic_attr.stack_version = ZB_ZCL_BASIC_STACK_VERSION_DEFAULT_VALUE;
    m_dev_ctx.basic_attr.hw_version    = ZB_ZCL_BASIC_HW_VERSION_DEFAULT_VALUE;
   
ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT(basic_attr_list, &g_attr_basic_zcl_version, &g_attr_basic_application_version, &g_attr_basic_stack_version, &g_attr_basic_hw_version, &g_attr_basic_manufacturer_name, &g_attr_basic_model_identifier, &g_attr_basic_date_code, &g_attr_basic_power_source, &g_attr_basic_location_description, &g_attr_basic_physical_environment, &g_attr_sw_build_id);

/* Identify cluster attributes data */

ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST(identify_attr_list, &g_attr_identify_identify_time);
ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST(groups_attr_list, &g_attr_groups_name_support);
ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST(scenes_attr_list, &g_attr_scenes_scene_count, &g_attr_scenes_current_scene, &g_attr_scenes_current_group, &g_attr_scenes_scene_valid, &g_attr_scenes_name_support);
ZB_ZCL_DECLARE_WINDOW_COVERING_CLUSTER_ATTRIB_LIST(window_covering_attr_list, &g_attr_window_covering_window_covering_type, &g_attr_window_covering_config_status, &g_attr_window_covering_current_position_lift_percentage, &g_attr_window_covering_current_position_tilt_percentage, &g_attr_window_covering_installed_open_limit_lift, &g_attr_window_covering_installed_closed_limit_lift, &g_attr_window_covering_installed_open_limit_tilt, &g_attr_window_covering_installed_closed_limit_tilt, &g_attr_window_covering_mode);
/********************* Declare device **************************/
ZB_HA_DECLARE_WINDOW_COVERING_CLUSTER_LIST(window_covering_clusters, basic_attr_list, identify_attr_list, groups_attr_list, scenes_attr_list, window_covering_attr_list);
ZB_HA_DECLARE_WINDOW_COVERING_EP(window_covering_ep, ENDPOINT_C, window_covering_clusters);
ZB_HA_DECLARE_WINDOW_COVERING_CTX(device_ctx, window_covering_ep);

  /* Register device list */
  ZB_AF_REGISTER_DEVICE_CTX(&device_ctx);
  ZB_AF_SET_ENDPOINT_HANDLER(ZB_OUTPUT_ENDPOINT, zcl_specific_cluster_cmd_handler);
  ZB_ZCL_REGISTER_DEVICE_CB(test_device_interface_cb);


}

static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */

/* YOUR_JOB: Declare all services structure your application is using
 *  BLE_XYZ_DEF(m_xyz);
 */

// YOUR_JOB: Use UUIDs for service(s) used in your application.
static ble_uuid_t m_adv_uuids[] = /**< Universally unique service identifiers. */
    {
        {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}};

static void advertising_start(bool erase_bonds);


/**@brief Callback function for asserts in the SoftDevice.
 *
 * @details This function will be called in case of an assert in the SoftDevice.
 *
 * @warning This handler is an example only and does not fit a final product. You need to analyze
 *          how your product is supposed to react in case of Assert.
 * @warning On assert from the SoftDevice, the system can only recover on reset.
 *
 * @param[in] line_num   Line number of the failing ASSERT call.
 * @param[in] file_name  File name of the failing ASSERT call.
 */
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
{
    app_error_handler(DEAD_BEEF, line_num, p_file_name);
}


/**@brief Function for handling Peer Manager events.
 *
 * @param[in] p_evt  Peer Manager event.
 */
static void pm_evt_handler(pm_evt_t const * p_evt)
{
    pm_handler_on_pm_evt(p_evt);
    pm_handler_flash_clean(p_evt);

    switch (p_evt->evt_id)
    {
        case PM_EVT_PEERS_DELETE_SUCCEEDED:
            advertising_start(false);
            break;

        default:
            break;
    }
}


/**@brief Function for the Timer initialization.
 *
 * @details Initializes the timer module. This creates and starts application timers.
 */
static void timers_init(void)
{
    // Initialize timer module.
    ret_code_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);

    // Create timers.

    /* YOUR_JOB: Create any timers to be used by the application.
                 Below is an example of how to create a timer.
                 For every new timer needed, increase the value of the macro APP_TIMER_MAX_TIMERS by
                 one.
       ret_code_t err_code;
       err_code = app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, timer_timeout_handler);
       APP_ERROR_CHECK(err_code); */
}


/**@brief Function for the GAP initialization.
 *
 * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
 *          device including the device name, appearance, and the preferred connection parameters.
 */
static void gap_params_init(void)
{
    ret_code_t              err_code;
    ble_gap_conn_params_t   gap_conn_params;
    ble_gap_conn_sec_mode_t sec_mode;

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);

    err_code = sd_ble_gap_device_name_set(&sec_mode,
                                          (const uint8_t *)DEVICE_NAME,
                                          strlen(DEVICE_NAME));
    APP_ERROR_CHECK(err_code);

    /* YOUR_JOB: Use an appearance value matching the application's use case.
       err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_);
       APP_ERROR_CHECK(err_code); */

    memset(&gap_conn_params, 0, sizeof(gap_conn_params));

    gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
    gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
    gap_conn_params.slave_latency     = SLAVE_LATENCY;
    gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;

    err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for initializing the GATT module.
 */
static void gatt_init(void)
{
    ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for handling the YYY Service events.
 * YOUR_JOB implement a service handler function depending on the event the service you are using can generate
 *
 * @details This function will be called for all YY Service events which are passed to
 *          the application.
 *
 * @param[in]   p_yy_service   YY Service structure.
 * @param[in]   p_evt          Event received from the YY Service.
 *
 *
static void on_yys_evt(ble_yy_service_t     * p_yy_service,
                       ble_yy_service_evt_t * p_evt)
{
    switch (p_evt->evt_type)
    {
        case BLE_YY_NAME_EVT_WRITE:
            APPL_LOG("[APPL]: charact written with value %s. ", p_evt->params.char_xx.value.p_str);
            break;

        default:
            // No implementation needed.
            break;
    }
}
*/

/**@brief Function for initializing services that will be used by the application.
 */
static void services_init(void)
{
    /* YOUR_JOB: Add code to initialize the services used by the application.
       ret_code_t                         err_code;
       ble_xxs_init_t                     xxs_init;
       ble_yys_init_t                     yys_init;

       // Initialize XXX Service.
       memset(&xxs_init, 0, sizeof(xxs_init));

       xxs_init.evt_handler                = NULL;
       xxs_init.is_xxx_notify_supported    = true;
       xxs_init.ble_xx_initial_value.level = 100;

       err_code = ble_bas_init(&m_xxs, &xxs_init);
       APP_ERROR_CHECK(err_code);

       // Initialize YYY Service.
       memset(&yys_init, 0, sizeof(yys_init));
       yys_init.evt_handler                  = on_yys_evt;
       yys_init.ble_yy_initial_value.counter = 0;

       err_code = ble_yy_service_init(&yys_init, &yy_init);
       APP_ERROR_CHECK(err_code);
     */
}


/**@brief Function for handling the Connection Parameters Module.
 *
 * @details This function will be called for all events in the Connection Parameters Module which
 *          are passed to the application.
 *          @note All this function does is to disconnect. This could have been done by simply
 *                setting the disconnect_on_fail config parameter, but instead we use the event
 *                handler mechanism to demonstrate its use.
 *
 * @param[in] p_evt  Event received from the Connection Parameters Module.
 */
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
{
    ret_code_t err_code;

    if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
    {
        err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
        APP_ERROR_CHECK(err_code);
    }
}


/**@brief Function for handling a Connection Parameters error.
 *
 * @param[in] nrf_error  Error code containing information about what went wrong.
 */
static void conn_params_error_handler(uint32_t nrf_error)
{
    APP_ERROR_HANDLER(nrf_error);
}


/**@brief Function for initializing the Connection Parameters module.
 */
static void conn_params_init(void)
{
    ret_code_t             err_code;
    ble_conn_params_init_t cp_init;

    memset(&cp_init, 0, sizeof(cp_init));

    cp_init.p_conn_params                  = NULL;
    cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
    cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
    cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
    cp_init.start_on_notify_cccd_handle    = BLE_GATT_HANDLE_INVALID;
    cp_init.disconnect_on_fail             = false;
    cp_init.evt_handler                    = on_conn_params_evt;
    cp_init.error_handler                  = conn_params_error_handler;

    err_code = ble_conn_params_init(&cp_init);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for starting timers.
 */
static void application_timers_start(void)
{
    /* YOUR_JOB: Start your timers. below is an example of how to start a timer.
       ret_code_t err_code;
       err_code = app_timer_start(m_app_timer_id, TIMER_INTERVAL, NULL);
       APP_ERROR_CHECK(err_code); */
}


/**@brief Function for handling advertising events.
 *
 * @details This function will be called for advertising events which are passed to the application.
 *
 * @param[in] ble_adv_evt  Advertising event.
 */
static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
    ret_code_t err_code;

    switch (ble_adv_evt)
    {
        case BLE_ADV_EVT_FAST:
            NRF_LOG_INFO("Fast advertising.");
            err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_ADV_EVT_IDLE:
            err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
            APP_ERROR_CHECK(err_code);
            break;

        default:
            break;
    }
}


/**@brief Function for handling BLE events.
 *
 * @param[in]   p_ble_evt   Bluetooth stack event.
 * @param[in]   p_context   Unused.
 */
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
    ret_code_t err_code = NRF_SUCCESS;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_DISCONNECTED:
            NRF_LOG_INFO("Disconnected.");
            break;

        case BLE_GAP_EVT_CONNECTED:
            NRF_LOG_INFO("Connected.");
            err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
            APP_ERROR_CHECK(err_code);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            break;

        case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
        {
            NRF_LOG_DEBUG("PHY update request.");
            ble_gap_phys_t const phys =
            {
                .rx_phys = BLE_GAP_PHY_AUTO,
                .tx_phys = BLE_GAP_PHY_AUTO,
            };
            err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
            APP_ERROR_CHECK(err_code);
        } break;

        case BLE_GATTC_EVT_TIMEOUT:
            // Disconnect on GATT Client timeout event.
            NRF_LOG_DEBUG("GATT Client Timeout.");
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_GATTS_EVT_TIMEOUT:
            // Disconnect on GATT Server timeout event.
            NRF_LOG_DEBUG("GATT Server Timeout.");
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_EVT_USER_MEM_REQUEST:
            err_code = sd_ble_user_mem_reply(p_ble_evt->evt.gattc_evt.conn_handle, NULL);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
        {
            ble_gatts_evt_rw_authorize_request_t  req;
            ble_gatts_rw_authorize_reply_params_t auth_reply;

            req = p_ble_evt->evt.gatts_evt.params.authorize_request;

            if (req.type != BLE_GATTS_AUTHORIZE_TYPE_INVALID)
            {
                if ((req.request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ)     ||
                    (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) ||
                    (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL))
                {
                    if (req.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
                    {
                        auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
                    }
                    else
                    {
                        auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
                    }
                    auth_reply.params.write.gatt_status = APP_FEATURE_NOT_SUPPORTED;
                    err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle,
                                                               &auth_reply);
                    APP_ERROR_CHECK(err_code);
                }
            }
        } break; // BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST

        default:
            // No implementation needed.
            break;
    }
}

/**@brief Function for initializing the BLE stack.
 *
 * @details Initializes the SoftDevice and the BLE event interrupt.
 */
static void ble_stack_init(void)
{
    ret_code_t err_code;

    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);

    // Configure the BLE stack using the default settings.
    // Fetch the start address of the application RAM.
    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
    APP_ERROR_CHECK(err_code);

    // Enable BLE stack.
    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);

    // Register a handler for BLE events.
    NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}

/**@brief Function for the Peer Manager initialization.
 */
static void peer_manager_init(void)
{
    ble_gap_sec_params_t sec_param;
    ret_code_t           err_code;

    err_code = pm_init();
    APP_ERROR_CHECK(err_code);

    memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));

    // Security parameters to be used for all security procedures.
    sec_param.bond           = SEC_PARAM_BOND;
    sec_param.mitm           = SEC_PARAM_MITM;
    sec_param.lesc           = SEC_PARAM_LESC;
    sec_param.keypress       = SEC_PARAM_KEYPRESS;
    sec_param.io_caps        = SEC_PARAM_IO_CAPABILITIES;
    sec_param.oob            = SEC_PARAM_OOB;
    sec_param.min_key_size   = SEC_PARAM_MIN_KEY_SIZE;
    sec_param.max_key_size   = SEC_PARAM_MAX_KEY_SIZE;
    sec_param.kdist_own.enc  = 1;
    sec_param.kdist_own.id   = 1;
    sec_param.kdist_peer.enc = 1;
    sec_param.kdist_peer.id  = 1;

    err_code = pm_sec_params_set(&sec_param);
    APP_ERROR_CHECK(err_code);

    err_code = pm_register(pm_evt_handler);
    APP_ERROR_CHECK(err_code);
}


/**@brief Clear bond information from persistent storage.
 */
static void delete_bonds(void)
{
    ret_code_t err_code;

    NRF_LOG_INFO("Erase bonds!");

    err_code = pm_peers_delete();
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for handling events from the BSP module.
 *
 * @param[in]   event   Event generated when button is pressed.
 */
static void bsp_event_handler(bsp_event_t event)
{
    ret_code_t err_code;

    switch (event)
    {
        case BSP_EVENT_DISCONNECT:
            err_code = sd_ble_gap_disconnect(m_conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            if (err_code != NRF_ERROR_INVALID_STATE)
            {
                APP_ERROR_CHECK(err_code);
            }
            break; // BSP_EVENT_DISCONNECT

        case BSP_EVENT_WHITELIST_OFF:
            if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
            {
                err_code = ble_advertising_restart_without_whitelist(&m_advertising);
                if (err_code != NRF_ERROR_INVALID_STATE)
                {
                    APP_ERROR_CHECK(err_code);
                }
            }
            break; // BSP_EVENT_KEY_0

        default:
            break;
    }
}


/**@brief Function for initializing the Advertising functionality.
 */
static void advertising_init(void)
{
    ret_code_t             err_code;
    ble_advertising_init_t init;

    memset(&init, 0, sizeof(init));

    init.advdata.name_type               = BLE_ADVDATA_FULL_NAME;
    init.advdata.include_appearance      = true;
    init.advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    init.advdata.uuids_complete.p_uuids  = m_adv_uuids;

    init.config.ble_adv_fast_enabled  = true;
    init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
    init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;

    init.evt_handler = on_adv_evt;

    err_code = ble_advertising_init(&m_advertising, &init);
    APP_ERROR_CHECK(err_code);

    ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}


/**@brief Function for initializing buttons and leds.
 *
 * @param[out] p_erase_bonds  Will be true if the clear bonding button was pressed to wake the application up.
 */
static void buttons_leds_init(bool * p_erase_bonds)
{
    ret_code_t err_code;
    bsp_event_t startup_event;

    err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler);
    APP_ERROR_CHECK(err_code);

    err_code = bsp_btn_ble_init(NULL, &startup_event);
    APP_ERROR_CHECK(err_code);

    *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
}


/**@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 starting advertising.
 */
static void advertising_start(bool erase_bonds)
{
    if (erase_bonds == true)
    {
        delete_bonds();
        // Advertising is started by PM_EVT_PEERS_DELETED_SUCEEDED evetnt
    }
    else
    {
        ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);

        APP_ERROR_CHECK(err_code);
    }
}

/**@brief Function for initializing scheduler module.
 */
static void scheduler_init(void)
{
    APP_SCHED_INIT(SCHED_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
}

///**@brief Function for initializing the Zigbee ZCL attributes
// */
//static void test_device_attr_init(void)
//{
//    m_dev_ctx.zcl_version   = ZB_ZCL_VERSION;
//    m_dev_ctx.power_source  = ZB_ZCL_BASIC_POWER_SOURCE_UNKNOWN;
//    m_dev_ctx.identify_time = 0;
//}

/**@brief Zigbee ZCL Callback handler
 */
static zb_void_t zcl_device_cb(zb_bufid_t bufid)
{
    zb_zcl_device_callback_param_t * p_device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);

    p_device_cb_param->status = RET_OK;
    switch (p_device_cb_param->device_cb_id)
    {
        case ZB_ZCL_IDENTIFY_EFFECT_CB_ID:
        {
            zb_zcl_identify_effect_value_param_t * p_identify_value = &(p_device_cb_param->cb_param.identify_effect_value_param);

            switch (p_identify_value->effect_id)
            {
                case ZB_ZCL_IDENTIFY_EFFECT_ID_BLINK:
                    /* Blink. */
                    NRF_LOG_INFO("Blink-blink ;-)");
                    break;

                default:
                    break;
            }
            /* No need to free the buffer - stack handles that if needed */
        }
            break;

        /* YOUR_JOB: Handle ZCL events relevant to your application. */
        default:
            break;
    }
}


/**@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 */
}


/**@brief Function for initializing the Zigbee Stack
 */
static void zigbee_init(void)
{

    zb_ieee_addr_t ieee_addr;

    /* 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("multiprotocol_template");

    /* 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_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();
    on_off_switch_clusters_attr_init();
    /* Initialize application context structure. */
    //

    /* Register dimmer switch device context (endpoints). */


    /* Register callback for handling ZCL commands. */
    //ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);

    //test_device_attr_init();
}

/**@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, NULL);
    zb_ret_t                 status = ZB_GET_APP_SIGNAL_STATUS(bufid);
    zb_ret_t                       zb_err_code;

    /* YOUR_JOB: Handle stack signals relevant to your application. */
    NRF_LOG_INFO("Zigbee signal received (signal: %u, status: %d)", sig, status);

    /* Update network status LED */
    zigbee_led_status_update(bufid, ZIGBEE_NETWORK_STATE_LED);

    //switch (sig)
    //{
    //    default:
    //        /* Call default signal handler. */
    //        ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
    //        break;
    //}

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

/***************************************************************************************************
 * @section Main
 **************************************************************************************************/

/**@brief Function for application main entry.
 */
int main(void)
{
    zb_ret_t zb_err_code;
    bool erase_bonds;

    // Initialize.
    log_init();
    scheduler_init();
    timers_init();
    buttons_leds_init(&erase_bonds);
    ble_stack_init();
    gap_params_init();
    gatt_init();
    advertising_init();
    services_init();
    conn_params_init();
    peer_manager_init();

    // Start execution.
    NRF_LOG_INFO("BLE Zigbee template example started.");
    application_timers_start();

    zigbee_init();

    /** Start Zigbee Stack. */
    zb_err_code = zboss_start_no_autostart();
    ZB_ERROR_CHECK(zb_err_code);

    advertising_start(erase_bonds);

    // Enter main loop.
    while (true)
    {
        zboss_main_loop_iteration();
        UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
        app_sched_execute();
    }
}


/**
 * @}
 */

Parents Reply Children
  • Hi,

    I have managed to solve parts of the issue. As I suspected, it was related to the Zigbee device configuration. I am not sure exactly what the issue was, if it was something with how the attributes and clusters were declared and handled, due to incorrect default values, or that things were done in incorrect order, but I changed all of this to be more inline with how we are doing it in our examples. The device does no longer stop working during joining. It is able to join the network, and both Zigbee and BLE works. However, there seems to be an issue with registering the endpoint and/or device context, as the endpoint is not active on the device. I have so far been unable to find the cause of this, as I cannot see any obvious issues, but I will keep looking into it. I have attached my main.c here, so you can also test with it to see if Zigbee and BLE are working on your side, and if you are able to find the issue with the endpoint.

    /**
     * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    /** @file
     *
     * @defgroup zigbee_examples_multiprotocol_template main.c
     * @{
     * @ingroup zigbee_examples
     * @brief Template project main file for dynamic BLE and Zigbee.
     *
     * This file contains a template for creating a new multiprotocol BLE + Zigbee application.
     * In terms of BLE, it has the code necessary to wake up from button, advertise,
     * get a connection, restart advertising on disconnect and if no new connection was created
     * go back to system-off mode.
     * In terms of Zigbee, it has the code implementing the Range Extender Device - a
     * simple router which is just relaying packets.
     * It can easily be used as a starting point for creating a new application, the comments identified
     * with 'YOUR_JOB' indicates where and how you can customize.
     *
     */
    
    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    
    #include "zboss_api.h"
    #include "zb_mem_config_med.h"
    #include "zb_error_handler.h"
    #include "zigbee_helpers.h"
    #include "zboss_api_addons.h"
    #include "nordic_common.h"
    #include "nrf.h"
    #include "app_error.h"
    #include "app_scheduler.h"
    #include "ble.h"
    #include "ble_hci.h"
    #include "ble_srv_common.h"
    #include "ble_advdata.h"
    #include "ble_advertising.h"
    #include "ble_conn_params.h"
    #include "nrf_assert.h"
    #include "nrf_sdh.h"
    #include "nrf_sdh_soc.h"
    #include "nrf_sdh_ble.h"
    #include "app_timer.h"
    #include "fds.h"
    #include "peer_manager.h"
    #include "peer_manager_handler.h"
    #include "bsp_btn_ble.h"
    #include "ble_conn_state.h"
    #include "nrf_ble_gatt.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #define APP_FEATURE_NOT_SUPPORTED       BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2    /**< Reply when unsupported features are requested. */
    
    #define DEVICE_NAME                     "Nordic_Template"                       /**< Name of device. Will be included in the advertising data. */
    #define MANUFACTURER_NAME               "NordicSemiconductor"                   /**< Manufacturer. Will be passed to Device Information Service. */
    #define DATE_CODE         "20180921"   
    #define LOCATION_DESCRIPTION     "Office desk" 
    #define APP_ADV_INTERVAL                320                                     /**< The advertising interval (in units of 0.625 ms. This value corresponds to 200 ms). */
    #define SLEEPY_ON_BUTTON                    BSP_BOARD_BUTTON_2                  /**< Button ID used to determine if we need the sleepy device behaviour (pressed means yes). */
    #define APP_ADV_DURATION                18000                                   /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
    
    #define APP_BLE_OBSERVER_PRIO           1                                       /**< Application's BLE observer priority. You shouldn't need to modify this value. */
    #define APP_BLE_CONN_CFG_TAG            1                                       /**< A tag identifying the SoftDevice BLE configuration. */
    
    #define MIN_CONN_INTERVAL               MSEC_TO_UNITS(100, UNIT_1_25_MS)        /**< Minimum acceptable connection interval (0.1 seconds). */
    #define MAX_CONN_INTERVAL               MSEC_TO_UNITS(200, UNIT_1_25_MS)        /**< Maximum acceptable connection interval (0.2 second). */
    #define SLAVE_LATENCY                   0                                       /**< Slave latency. */
    #define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)         /**< Connection supervisory timeout (4 seconds). */
    
    #define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                   /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
    #define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                  /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
    #define MAX_CONN_PARAMS_UPDATE_COUNT    3                                       /**< Number of attempts before giving up the connection parameter negotiation. */
    
    #define SEC_PARAM_BOND                  1                                       /**< Perform bonding. */
    #define SEC_PARAM_MITM                  0                                       /**< Man In The Middle protection not required. */
    #define SEC_PARAM_LESC                  0                                       /**< LE Secure Connections not enabled. */
    #define SEC_PARAM_KEYPRESS              0                                       /**< Keypress notifications not enabled. */
    #define SEC_PARAM_IO_CAPABILITIES       BLE_GAP_IO_CAPS_NONE                    /**< No I/O capabilities. */
    #define SEC_PARAM_OOB                   0                                       /**< Out Of Band data not available. */
    #define SEC_PARAM_MIN_KEY_SIZE          7                                       /**< Minimum encryption key size. */
    #define SEC_PARAM_MAX_KEY_SIZE          16                                      /**< Maximum encryption key size. */
    
    #define DEAD_BEEF                       0xDEADBEEF                              /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */
    
    #define SCHED_QUEUE_SIZE                32                                      /**< Maximum number of events in the scheduler queue. */
    #define SCHED_EVENT_DATA_SIZE           APP_TIMER_SCHED_EVENT_DATA_SIZE         /**< Maximum app_scheduler event size. */
    
    #define MAX_CHILDREN                    10                                      /**< The maximum amount of connected devices. Setting this value to 0 disables association to this device.  */
    #define RANGE_EXTENDER_ENDPOINT         7                                       /**< Endpoint for the Zigbee Range Extender 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 IEEE_CHANNEL_MASK               (1l << ZIGBEE_CHANNEL)                  /**< Scan only one, predefined channel to find the coordinator. */
    #define ZIGBEE_NETWORK_STATE_LED        BSP_BOARD_LED_2                         /**< LED indicating that light switch successfully joind Zigbee network. */
    #define LIGHT_SWITCH_ENDPOINT           1                                   /**< Source endpoint used to control light bulb. */
    #define ENDPOINT_C          1
    #define ZB_OUTPUT_ENDPOINT 1
    
    NRF_BLE_GATT_DEF(m_gatt);                                                       /**< GATT module instance. */
    BLE_ADVERTISING_DEF(m_advertising);                                             /**< Advertising module instance. */
    
    typedef struct
    {
        zb_uint8_t  window_covering_type;
        zb_uint8_t  config_status;
        zb_uint8_t  current_position_lift_percentage;
        zb_uint8_t  current_position_tilt_percentage;
        zb_uint16_t installed_open_limit_lift;
        zb_uint16_t installed_closed_limit_lift;
        zb_uint16_t installed_open_limit_tilt;
        zb_uint16_t installed_closed_limit_tilt;
        zb_uint8_t  mode;
    } zb_zcl_window_covering_attrs_t;
    
    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_window_covering_attrs_t   window_covering_attr;
    } window_cowering_ctx_t;
    
    window_cowering_ctx_t m_dev_ctx;
    
    ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST(identify_attr_list, 
                                        &m_dev_ctx.identify_attr.identify_time);
    
    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_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_WINDOW_COVERING_CLUSTER_ATTRIB_LIST(window_covering_attr_list,
                                                       &m_dev_ctx.window_covering_attr.window_covering_type,
                                                       &m_dev_ctx.window_covering_attr.config_status,
                                                       &m_dev_ctx.window_covering_attr.current_position_lift_percentage,
                                                       &m_dev_ctx.window_covering_attr.current_position_tilt_percentage,
                                                       &m_dev_ctx.window_covering_attr.installed_open_limit_lift,
                                                       &m_dev_ctx.window_covering_attr.installed_closed_limit_lift,
                                                       &m_dev_ctx.window_covering_attr.installed_open_limit_tilt,
                                                       &m_dev_ctx.window_covering_attr.installed_closed_limit_tilt,
                                                       &m_dev_ctx.window_covering_attr.mode);
    
    ZB_HA_DECLARE_WINDOW_COVERING_CLUSTER_LIST(window_covering_cluster,
                                               basic_attr_list,
                                               identify_attr_list,
                                               groups_attr_list,
                                               scenes_attr_list,
                                               window_covering_attr_list);
    
    ZB_HA_DECLARE_WINDOW_COVERING_EP(window_covering_ep,
                                     ENDPOINT_C,
                                     window_covering_cluster);
    
    ZB_HA_DECLARE_WINDOW_COVERING_CTX(device_ctx, window_covering_ep);
    
    zb_uint8_t zcl_specific_cluster_cmd_handler(zb_uint8_t param)
    {
      zb_zcl_parsed_hdr_t cmd_info;
      zb_uint8_t lqi = ZB_MAC_LQI_UNDEFINED;
      zb_int8_t rssi = ZB_MAC_RSSI_UNDEFINED;
     
      TRACE_MSG(TRACE_APP1, "> zcl_specific_cluster_cmd_handler", (FMT__0));
     
      ZB_ZCL_COPY_PARSED_HEADER(param, &cmd_info);
     
      uint16_t g_dst_addr = ZB_ZCL_PARSED_HDR_SHORT_DATA(&cmd_info).source.u.short_addr;
     
      ZB_ZCL_DEBUG_DUMP_HEADER(&cmd_info);
      TRACE_MSG(TRACE_APP3, "payload size: %i", (FMT__D, zb_buf_len(param)));
     
      zb_zdo_get_diag_data(g_dst_addr, &lqi, &rssi);
      TRACE_MSG(TRACE_APP3, "lqi %hd rssi %d", (FMT__H_H, lqi, rssi));
     
      if (cmd_info.cmd_direction == ZB_ZCL_FRAME_DIRECTION_TO_CLI)
      {
        TRACE_MSG(
            TRACE_ERROR,
            "Unsupported \"from server\" command direction",
            (FMT__0));
      }
     
      TRACE_MSG(TRACE_APP1, "< zcl_specific_cluster_cmd_handler", (FMT__0));
      return ZB_FALSE;
    }
    
    
    void test_device_interface_cb(zb_uint8_t param)
    {
     
      zb_zcl_device_callback_param_t *device_cb_param =
        ZB_BUF_GET_PARAM(param, zb_zcl_device_callback_param_t);
     
      TRACE_MSG(TRACE_APP1, "> test_device_interface_cb param %hd id %hd", (FMT__H_H,
          param, device_cb_param->device_cb_id));
     
      device_cb_param->status = RET_OK;
     
      switch (device_cb_param->device_cb_id)
      {
        case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
          if (device_cb_param->cb_param.set_attr_value_param.cluster_id == ZB_ZCL_CLUSTER_ID_ON_OFF &&
              device_cb_param->cb_param.set_attr_value_param.attr_id == ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID)
          {
            if (device_cb_param->cb_param.set_attr_value_param.values.data8)
            {
              TRACE_MSG(TRACE_APP1, "set ON", (FMT__0));
    #ifdef ZB_USE_BUTTONS
              zb_osif_led_on(0);
    #endif
            }
            else
            {
              TRACE_MSG(TRACE_APP1, "set OFF", (FMT__0));
    #ifdef ZB_USE_BUTTONS
              zb_osif_led_off(0);
    #endif
            }
          }
          break;
        case ZB_ZCL_WINDOW_COVERING_UP_OPEN_CB_ID:
        {
          zb_uint8_t lift_percentage_val = 0x00;
          zb_uint8_t tilt_percentage_val = 0x00;
     
          ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                       ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                       ZB_ZCL_CLUSTER_SERVER_ROLE,
                                       ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID,
                                       &lift_percentage_val,
                                       ZB_FALSE));
     
          ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                       ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                       ZB_ZCL_CLUSTER_SERVER_ROLE,
                                       ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID,
                                       &tilt_percentage_val,
                                       ZB_FALSE));
          break;
        }
        case ZB_ZCL_WINDOW_COVERING_DOWN_CLOSE_CB_ID:
        {
          zb_uint8_t lift_percentage_val = 0x64;
          zb_uint8_t tilt_percentage_val = 0x64;
     
          ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                       ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                       ZB_ZCL_CLUSTER_SERVER_ROLE,
                                       ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID,
                                       &lift_percentage_val,
                                       ZB_FALSE));
     
          ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                       ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                       ZB_ZCL_CLUSTER_SERVER_ROLE,
                                       ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID,
                                       &tilt_percentage_val,
                                       ZB_FALSE));
          break;
        }
        case ZB_ZCL_WINDOW_COVERING_STOP_CB_ID:
          break;
        case ZB_ZCL_WINDOW_COVERING_GO_TO_LIFT_PERCENTAGE_CB_ID:
        {
          const zb_zcl_go_to_lift_percentage_req_t *lift_percentage = ZB_ZCL_DEVICE_CMD_PARAM_IN_GET(param, zb_zcl_go_to_lift_percentage_req_t);
          zb_zcl_attr_t *attr_desc = zb_zcl_get_attr_desc_a(ENDPOINT_C,
              ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID);
          if (attr_desc)
          {
            ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                         ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                         ZB_ZCL_CLUSTER_SERVER_ROLE,
                                         ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID,
                                         (zb_uint8_t *)&(lift_percentage->percentage_lift_value),
                                         ZB_FALSE));
          }
          break;
        }
        case ZB_ZCL_WINDOW_COVERING_GO_TO_TILT_PERCENTAGE_CB_ID:
        {
          const zb_zcl_go_to_tilt_percentage_req_t *tilt_percentage = ZB_ZCL_DEVICE_CMD_PARAM_IN_GET(param, zb_zcl_go_to_tilt_percentage_req_t);
          zb_zcl_attr_t *attr_desc = zb_zcl_get_attr_desc_a(ENDPOINT_C,
                ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID);
          if (attr_desc)
          {
            ZVUNUSED(zb_zcl_set_attr_val(ENDPOINT_C,
                                         ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
                                         ZB_ZCL_CLUSTER_SERVER_ROLE,
                                         ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID,
                                         (zb_uint8_t *)&(tilt_percentage->percentage_tilt_value),
                                         ZB_FALSE));
          }
          break;
        }
        /* >>>> Scenes */
        //case ZB_ZCL_SCENES_STORE_SCENE_CB_ID:
        //{
        //  const zb_zcl_scenes_store_scene_req_t *store_scene_req = ZB_ZCL_DEVICE_CMD_PARAM_IN_GET(param, zb_zcl_scenes_store_scene_req_t);
        //  zb_uint8_t idx = 0xFF;
        //  zb_uint8_t *store_scene_status = ZB_ZCL_DEVICE_CMD_PARAM_OUT_GET(param, zb_uint8_t);
        //  const zb_zcl_parsed_hdr_t *in_cmd_info = ZB_ZCL_DEVICE_CMD_PARAM_CMD_INFO(param);
     
        //  TRACE_MSG(TRACE_APP1, "ZB_ZCL_SCENES_STORE_SCENE_CB_ID: group_id 0x%x scene_id 0x%hd", (FMT__D_H, store_scene_req->group_id, store_scene_req->scene_id));
     
        //  if (!zb_aps_is_endpoint_in_group(
        //             store_scene_req->group_id,
        //             ZB_ZCL_PARSED_HDR_SHORT_DATA(in_cmd_info).dst_endpoint))
        //  {
        //    *store_scene_status = ZB_ZCL_STATUS_INVALID_FIELD;
        //  }
        //  else
        //  {
        //    idx = test_device_scenes_get_entry(store_scene_req->group_id, store_scene_req->scene_id);
     
        //    if (idx != 0xFF)
        //    {
        //      if (scenes_table[idx].common.group_id != ZB_ZCL_SCENES_FREE_SCENE_TABLE_RECORD)
        //      {
        //        /* Update existing entry with current On/Off state */
        //        device_cb_param->status = RET_ALREADY_EXISTS;
        //        scenes_table[idx].lift_percentage_state = g_attr_window_covering_current_position_lift_percentage;
        //        scenes_table[idx].tilt_percentage_state = g_attr_window_covering_current_position_tilt_percentage;
        //      }
        //      else
        //      {
        //        /* Create new entry with empty name and 0 transition time */
        //        scenes_table[idx].common.group_id = store_scene_req->group_id;
        //        scenes_table[idx].common.scene_id = store_scene_req->scene_id;
        //        scenes_table[idx].common.transition_time = 0;
        //        scenes_table[idx].lift_percentage_state = g_attr_window_covering_current_position_lift_percentage;
        //        scenes_table[idx].tilt_percentage_state = g_attr_window_covering_current_position_tilt_percentage;
        //      }
     
        //      *store_scene_status = ZB_ZCL_STATUS_SUCCESS;
        //    }
        //    else
        //    {
        //      *store_scene_status = ZB_ZCL_STATUS_INSUFF_SPACE;
        //    }
        //  }
        //}
        //break;
        //case ZB_ZCL_SCENES_RECALL_SCENE_CB_ID:
        //{
        //  const zb_zcl_scenes_recall_scene_req_t *recall_scene_req = ZB_ZCL_DEVICE_CMD_PARAM_IN_GET(param, zb_zcl_scenes_recall_scene_req_t);
        //  zb_uint8_t idx = 0xFF;
        //  zb_uint8_t *recall_scene_status = ZB_ZCL_DEVICE_CMD_PARAM_OUT_GET(param, zb_uint8_t);
     
        //  TRACE_MSG(TRACE_APP1, "ZB_ZCL_SCENES_RECALL_SCENE_CB_ID: group_id 0x%x scene_id 0x%hd", (FMT__D_H, recall_scene_req->group_id, recall_scene_req->scene_id));
     
        //  idx = test_device_scenes_get_entry(recall_scene_req->group_id, recall_scene_req->scene_id);
     
        //  if (idx != 0xFF &&
        //      scenes_table[idx].common.group_id != ZB_ZCL_SCENES_FREE_SCENE_TABLE_RECORD)
        //  {
        //    /* Recall this entry */
        //    ZB_ZCL_SET_ATTRIBUTE(
        //      ENDPOINT_C,
        //      ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
        //      ZB_ZCL_CLUSTER_SERVER_ROLE,
        //      ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID,
        //      &scenes_table[idx].lift_percentage_state,
        //      ZB_FALSE);
        //    ZB_ZCL_SET_ATTRIBUTE(
        //      ENDPOINT_C,
        //      ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,
        //      ZB_ZCL_CLUSTER_SERVER_ROLE,
        //      ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID,
        //      &scenes_table[idx].tilt_percentage_state,
        //      ZB_FALSE);
        //    *recall_scene_status = ZB_ZCL_STATUS_SUCCESS;
        //  }
        //  else
        //  {
        //    *recall_scene_status = ZB_ZCL_STATUS_NOT_FOUND;
        //  }
        //}
        //break;
        //case ZB_ZCL_SCENES_INTERNAL_REMOVE_ALL_SCENES_ALL_ENDPOINTS_ALL_GROUPS_CB_ID:
        //{
        //  test_device_scenes_table_init();
        //}
        //break;
          /* <<<< Scenes */
        default:
          device_cb_param->status = RET_OK;
          break;
      }
     
      TRACE_MSG(TRACE_APP1, "< test_device_interface_cb %hd", (FMT__H, device_cb_param->status));
    }
    
    
    
    static void on_off_switch_clusters_attr_init(void) {
        /* Basic cluster attributes data */
        m_dev_ctx.basic_attr.zcl_version   = ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE;
        m_dev_ctx.basic_attr.app_version   = ZB_ZCL_BASIC_APPLICATION_VERSION_DEFAULT_VALUE;
        m_dev_ctx.basic_attr.stack_version = ZB_ZCL_BASIC_STACK_VERSION_DEFAULT_VALUE;
        m_dev_ctx.basic_attr.hw_version    = ZB_ZCL_BASIC_HW_VERSION_DEFAULT_VALUE;
    
        ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.mf_name,
                              MANUFACTURER_NAME,
                              ZB_ZCL_STRING_CONST_SIZE(MANUFACTURER_NAME));
    
        ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.model_id,
                              DEVICE_NAME,
                              ZB_ZCL_STRING_CONST_SIZE(DEVICE_NAME));
    
        ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.date_code,
                              DATE_CODE,
                              ZB_ZCL_STRING_CONST_SIZE(DATE_CODE));
    
        m_dev_ctx.basic_attr.power_source = ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE;
    
        ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.location_id,
                              LOCATION_DESCRIPTION,
                              ZB_ZCL_STRING_CONST_SIZE(LOCATION_DESCRIPTION));
    
        m_dev_ctx.basic_attr.ph_env = ZB_ZCL_BASIC_PHYSICAL_ENVIRONMENT_DEFAULT_VALUE;
    
    
        /* Identify cluster attributes data */
        m_dev_ctx.identify_attr.identify_time = ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE;
    
        /* Groups cluster attributes data */
        //m_dev_ctx.groups_attr.name_support = 0;
    
        /* Scenes cluster attributes data */
    
        /* Window Covering cluster attributes data */
        m_dev_ctx.window_covering_attr.window_covering_type = ZB_ZCL_WINDOW_COVERING_WINDOW_COVERING_TYPE_DEFAULT_VALUE;
        m_dev_ctx.window_covering_attr.config_status = ZB_ZCL_WINDOW_COVERING_CONFIG_STATUS_DEFAULT_VALUE;
        m_dev_ctx.window_covering_attr.current_position_lift_percentage = ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_DEFAULT_VALUE;
        m_dev_ctx.window_covering_attr.current_position_tilt_percentage = ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_DEFAULT_VALUE;
        m_dev_ctx.window_covering_attr.installed_open_limit_lift = ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_DEFAULT_VALUE;
        m_dev_ctx.window_covering_attr.installed_closed_limit_lift = ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_DEFAULT_VALUE;
        m_dev_ctx.window_covering_attr.installed_open_limit_tilt = ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_DEFAULT_VALUE;
        m_dev_ctx.window_covering_attr.installed_closed_limit_tilt = ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_DEFAULT_VALUE;
        m_dev_ctx.window_covering_attr.mode = ZB_ZCL_WINDOW_COVERING_MODE_DEFAULT_VALUE;
    
    }
    
    static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */
    
    /* YOUR_JOB: Declare all services structure your application is using
     *  BLE_XYZ_DEF(m_xyz);
     */
    
    // YOUR_JOB: Use UUIDs for service(s) used in your application.
    static ble_uuid_t m_adv_uuids[] = /**< Universally unique service identifiers. */
        {
            {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}};
    
    static void advertising_start(bool erase_bonds);
    
    
    /**@brief Callback function for asserts in the SoftDevice.
     *
     * @details This function will be called in case of an assert in the SoftDevice.
     *
     * @warning This handler is an example only and does not fit a final product. You need to analyze
     *          how your product is supposed to react in case of Assert.
     * @warning On assert from the SoftDevice, the system can only recover on reset.
     *
     * @param[in] line_num   Line number of the failing ASSERT call.
     * @param[in] file_name  File name of the failing ASSERT call.
     */
    void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
    {
        app_error_handler(DEAD_BEEF, line_num, p_file_name);
    }
    
    
    /**@brief Function for handling Peer Manager events.
     *
     * @param[in] p_evt  Peer Manager event.
     */
    static void pm_evt_handler(pm_evt_t const * p_evt)
    {
        pm_handler_on_pm_evt(p_evt);
        pm_handler_flash_clean(p_evt);
    
        switch (p_evt->evt_id)
        {
            case PM_EVT_PEERS_DELETE_SUCCEEDED:
                advertising_start(false);
                break;
    
            default:
                break;
        }
    }
    
    
    /**@brief Function for the Timer initialization.
     *
     * @details Initializes the timer module. This creates and starts application timers.
     */
    static void timers_init(void)
    {
        // Initialize timer module.
        ret_code_t err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    
        // Create timers.
    
        /* YOUR_JOB: Create any timers to be used by the application.
                     Below is an example of how to create a timer.
                     For every new timer needed, increase the value of the macro APP_TIMER_MAX_TIMERS by
                     one.
           ret_code_t err_code;
           err_code = app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, timer_timeout_handler);
           APP_ERROR_CHECK(err_code); */
    }
    
    
    /**@brief Function for the GAP initialization.
     *
     * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
     *          device including the device name, appearance, and the preferred connection parameters.
     */
    static void gap_params_init(void)
    {
        ret_code_t              err_code;
        ble_gap_conn_params_t   gap_conn_params;
        ble_gap_conn_sec_mode_t sec_mode;
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
    
        err_code = sd_ble_gap_device_name_set(&sec_mode,
                                              (const uint8_t *)DEVICE_NAME,
                                              strlen(DEVICE_NAME));
        APP_ERROR_CHECK(err_code);
    
        /* YOUR_JOB: Use an appearance value matching the application's use case.
           err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_);
           APP_ERROR_CHECK(err_code); */
    
        memset(&gap_conn_params, 0, sizeof(gap_conn_params));
    
        gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
        gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
        gap_conn_params.slave_latency     = SLAVE_LATENCY;
        gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;
    
        err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for initializing the GATT module.
     */
    static void gatt_init(void)
    {
        ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for handling the YYY Service events.
     * YOUR_JOB implement a service handler function depending on the event the service you are using can generate
     *
     * @details This function will be called for all YY Service events which are passed to
     *          the application.
     *
     * @param[in]   p_yy_service   YY Service structure.
     * @param[in]   p_evt          Event received from the YY Service.
     *
     *
    static void on_yys_evt(ble_yy_service_t     * p_yy_service,
                           ble_yy_service_evt_t * p_evt)
    {
        switch (p_evt->evt_type)
        {
            case BLE_YY_NAME_EVT_WRITE:
                APPL_LOG("[APPL]: charact written with value %s. ", p_evt->params.char_xx.value.p_str);
                break;
    
            default:
                // No implementation needed.
                break;
        }
    }
    */
    
    /**@brief Function for initializing services that will be used by the application.
     */
    static void services_init(void)
    {
        /* YOUR_JOB: Add code to initialize the services used by the application.
           ret_code_t                         err_code;
           ble_xxs_init_t                     xxs_init;
           ble_yys_init_t                     yys_init;
    
           // Initialize XXX Service.
           memset(&xxs_init, 0, sizeof(xxs_init));
    
           xxs_init.evt_handler                = NULL;
           xxs_init.is_xxx_notify_supported    = true;
           xxs_init.ble_xx_initial_value.level = 100;
    
           err_code = ble_bas_init(&m_xxs, &xxs_init);
           APP_ERROR_CHECK(err_code);
    
           // Initialize YYY Service.
           memset(&yys_init, 0, sizeof(yys_init));
           yys_init.evt_handler                  = on_yys_evt;
           yys_init.ble_yy_initial_value.counter = 0;
    
           err_code = ble_yy_service_init(&yys_init, &yy_init);
           APP_ERROR_CHECK(err_code);
         */
    }
    
    
    /**@brief Function for handling the Connection Parameters Module.
     *
     * @details This function will be called for all events in the Connection Parameters Module which
     *          are passed to the application.
     *          @note All this function does is to disconnect. This could have been done by simply
     *                setting the disconnect_on_fail config parameter, but instead we use the event
     *                handler mechanism to demonstrate its use.
     *
     * @param[in] p_evt  Event received from the Connection Parameters Module.
     */
    static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
    {
        ret_code_t err_code;
    
        if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
        {
            err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
            APP_ERROR_CHECK(err_code);
        }
    }
    
    
    /**@brief Function for handling a Connection Parameters error.
     *
     * @param[in] nrf_error  Error code containing information about what went wrong.
     */
    static void conn_params_error_handler(uint32_t nrf_error)
    {
        APP_ERROR_HANDLER(nrf_error);
    }
    
    
    /**@brief Function for initializing the Connection Parameters module.
     */
    static void conn_params_init(void)
    {
        ret_code_t             err_code;
        ble_conn_params_init_t cp_init;
    
        memset(&cp_init, 0, sizeof(cp_init));
    
        cp_init.p_conn_params                  = NULL;
        cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
        cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
        cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
        cp_init.start_on_notify_cccd_handle    = BLE_GATT_HANDLE_INVALID;
        cp_init.disconnect_on_fail             = false;
        cp_init.evt_handler                    = on_conn_params_evt;
        cp_init.error_handler                  = conn_params_error_handler;
    
        err_code = ble_conn_params_init(&cp_init);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for starting timers.
     */
    static void application_timers_start(void)
    {
        /* YOUR_JOB: Start your timers. below is an example of how to start a timer.
           ret_code_t err_code;
           err_code = app_timer_start(m_app_timer_id, TIMER_INTERVAL, NULL);
           APP_ERROR_CHECK(err_code); */
    }
    
    
    /**@brief Function for handling advertising events.
     *
     * @details This function will be called for advertising events which are passed to the application.
     *
     * @param[in] ble_adv_evt  Advertising event.
     */
    static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
    {
        ret_code_t err_code;
    
        switch (ble_adv_evt)
        {
            case BLE_ADV_EVT_FAST:
                NRF_LOG_INFO("Fast advertising.");
                err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_ADV_EVT_IDLE:
                err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
                APP_ERROR_CHECK(err_code);
                break;
    
            default:
                break;
        }
    }
    
    
    /**@brief Function for handling BLE events.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     * @param[in]   p_context   Unused.
     */
    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        ret_code_t err_code = NRF_SUCCESS;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Disconnected.");
                break;
    
            case BLE_GAP_EVT_CONNECTED:
                NRF_LOG_INFO("Connected.");
                err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
                APP_ERROR_CHECK(err_code);
                m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
                break;
    
            case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
            {
                NRF_LOG_DEBUG("PHY update request.");
                ble_gap_phys_t const phys =
                {
                    .rx_phys = BLE_GAP_PHY_AUTO,
                    .tx_phys = BLE_GAP_PHY_AUTO,
                };
                err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
                APP_ERROR_CHECK(err_code);
            } break;
    
            case BLE_GATTC_EVT_TIMEOUT:
                // Disconnect on GATT Client timeout event.
                NRF_LOG_DEBUG("GATT Client Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_TIMEOUT:
                // Disconnect on GATT Server timeout event.
                NRF_LOG_DEBUG("GATT Server Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_EVT_USER_MEM_REQUEST:
                err_code = sd_ble_user_mem_reply(p_ble_evt->evt.gattc_evt.conn_handle, NULL);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
            {
                ble_gatts_evt_rw_authorize_request_t  req;
                ble_gatts_rw_authorize_reply_params_t auth_reply;
    
                req = p_ble_evt->evt.gatts_evt.params.authorize_request;
    
                if (req.type != BLE_GATTS_AUTHORIZE_TYPE_INVALID)
                {
                    if ((req.request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ)     ||
                        (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) ||
                        (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL))
                    {
                        if (req.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
                        {
                            auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
                        }
                        else
                        {
                            auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
                        }
                        auth_reply.params.write.gatt_status = APP_FEATURE_NOT_SUPPORTED;
                        err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle,
                                                                   &auth_reply);
                        APP_ERROR_CHECK(err_code);
                    }
                }
            } break; // BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST
    
            default:
                // No implementation needed.
                break;
        }
    }
    
    /**@brief Function for initializing the BLE stack.
     *
     * @details Initializes the SoftDevice and the BLE event interrupt.
     */
    static void ble_stack_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_sdh_enable_request();
        APP_ERROR_CHECK(err_code);
    
        // Configure the BLE stack using the default settings.
        // Fetch the start address of the application RAM.
        uint32_t ram_start = 0;
        err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Enable BLE stack.
        err_code = nrf_sdh_ble_enable(&ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Register a handler for BLE events.
        NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
    }
    
    /**@brief Function for the Peer Manager initialization.
     */
    static void peer_manager_init(void)
    {
        ble_gap_sec_params_t sec_param;
        ret_code_t           err_code;
    
        err_code = pm_init();
        APP_ERROR_CHECK(err_code);
    
        memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));
    
        // Security parameters to be used for all security procedures.
        sec_param.bond           = SEC_PARAM_BOND;
        sec_param.mitm           = SEC_PARAM_MITM;
        sec_param.lesc           = SEC_PARAM_LESC;
        sec_param.keypress       = SEC_PARAM_KEYPRESS;
        sec_param.io_caps        = SEC_PARAM_IO_CAPABILITIES;
        sec_param.oob            = SEC_PARAM_OOB;
        sec_param.min_key_size   = SEC_PARAM_MIN_KEY_SIZE;
        sec_param.max_key_size   = SEC_PARAM_MAX_KEY_SIZE;
        sec_param.kdist_own.enc  = 1;
        sec_param.kdist_own.id   = 1;
        sec_param.kdist_peer.enc = 1;
        sec_param.kdist_peer.id  = 1;
    
        err_code = pm_sec_params_set(&sec_param);
        APP_ERROR_CHECK(err_code);
    
        err_code = pm_register(pm_evt_handler);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Clear bond information from persistent storage.
     */
    static void delete_bonds(void)
    {
        ret_code_t err_code;
    
        NRF_LOG_INFO("Erase bonds!");
    
        err_code = pm_peers_delete();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for handling events from the BSP module.
     *
     * @param[in]   event   Event generated when button is pressed.
     */
    static void bsp_event_handler(bsp_event_t event)
    {
        ret_code_t err_code;
    
        switch (event)
        {
            case BSP_EVENT_DISCONNECT:
                err_code = sd_ble_gap_disconnect(m_conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                if (err_code != NRF_ERROR_INVALID_STATE)
                {
                    APP_ERROR_CHECK(err_code);
                }
                break; // BSP_EVENT_DISCONNECT
    
            case BSP_EVENT_WHITELIST_OFF:
                if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
                {
                    err_code = ble_advertising_restart_without_whitelist(&m_advertising);
                    if (err_code != NRF_ERROR_INVALID_STATE)
                    {
                        APP_ERROR_CHECK(err_code);
                    }
                }
                break; // BSP_EVENT_KEY_0
    
            default:
                break;
        }
    }
    
    
    /**@brief Function for initializing the Advertising functionality.
     */
    static void advertising_init(void)
    {
        ret_code_t             err_code;
        ble_advertising_init_t init;
    
        memset(&init, 0, sizeof(init));
    
        init.advdata.name_type               = BLE_ADVDATA_FULL_NAME;
        init.advdata.include_appearance      = true;
        init.advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
        init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
        init.advdata.uuids_complete.p_uuids  = m_adv_uuids;
    
        init.config.ble_adv_fast_enabled  = true;
        init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
        init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
    
        init.evt_handler = on_adv_evt;
    
        err_code = ble_advertising_init(&m_advertising, &init);
        APP_ERROR_CHECK(err_code);
    
        ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
    }
    
    
    /**@brief Function for initializing buttons and leds.
     *
     * @param[out] p_erase_bonds  Will be true if the clear bonding button was pressed to wake the application up.
     */
    static void buttons_leds_init(bool * p_erase_bonds)
    {
        ret_code_t err_code;
        bsp_event_t startup_event;
    
        err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler);
        APP_ERROR_CHECK(err_code);
    
        err_code = bsp_btn_ble_init(NULL, &startup_event);
        APP_ERROR_CHECK(err_code);
    
        *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
    }
    
    
    /**@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 starting advertising.
     */
    static void advertising_start(bool erase_bonds)
    {
        if (erase_bonds == true)
        {
            delete_bonds();
            // Advertising is started by PM_EVT_PEERS_DELETED_SUCEEDED evetnt
        }
        else
        {
            ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
    
            APP_ERROR_CHECK(err_code);
        }
    }
    
    /**@brief Function for initializing scheduler module.
     */
    static void scheduler_init(void)
    {
        APP_SCHED_INIT(SCHED_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
    }
    
    ///**@brief Function for initializing the Zigbee ZCL attributes
    // */
    //static void test_device_attr_init(void)
    //{
    //    m_dev_ctx.zcl_version   = ZB_ZCL_VERSION;
    //    m_dev_ctx.power_source  = ZB_ZCL_BASIC_POWER_SOURCE_UNKNOWN;
    //    m_dev_ctx.identify_time = 0;
    //}
    
    /**@brief Zigbee ZCL Callback handler
     */
    static zb_void_t zcl_device_cb(zb_bufid_t bufid)
    {
        zb_zcl_device_callback_param_t * p_device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);
    
        p_device_cb_param->status = RET_OK;
        switch (p_device_cb_param->device_cb_id)
        {
            case ZB_ZCL_IDENTIFY_EFFECT_CB_ID:
            {
                zb_zcl_identify_effect_value_param_t * p_identify_value = &(p_device_cb_param->cb_param.identify_effect_value_param);
    
                switch (p_identify_value->effect_id)
                {
                    case ZB_ZCL_IDENTIFY_EFFECT_ID_BLINK:
                        /* Blink. */
                        NRF_LOG_INFO("Blink-blink ;-)");
                        break;
    
                    default:
                        break;
                }
                /* No need to free the buffer - stack handles that if needed */
            }
                break;
    
            /* YOUR_JOB: Handle ZCL events relevant to your application. */
            default:
                break;
        }
    }
    
    
    /**@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 */
    }
    
    
    /**@brief Function for initializing the Zigbee Stack
     */
    static void zigbee_init(void)
    {
    
        zb_ieee_addr_t ieee_addr;
    
        /* 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("multiprotocol_template");
    
        /* 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_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();
    
        UNUSED_RETURN_VALUE(ZB_MEMSET(&m_dev_ctx, 0, sizeof(m_dev_ctx)));
    
        ZB_AF_REGISTER_DEVICE_CTX(&device_ctx);
    
        ZB_ZCL_REGISTER_DEVICE_CB(test_device_interface_cb);
    
        ZB_AF_SET_ENDPOINT_HANDLER(ZB_OUTPUT_ENDPOINT, zcl_specific_cluster_cmd_handler);
    
        on_off_switch_clusters_attr_init();
        /* Initialize application context structure. */
        //
    
        /* Register dimmer switch device context (endpoints). */
    
    
        /* Register callback for handling ZCL commands. */
        //ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);
    
        //test_device_attr_init();
    }
    
    /**@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, NULL);
        zb_ret_t                 status = ZB_GET_APP_SIGNAL_STATUS(bufid);
        zb_ret_t                       zb_err_code;
    
        /* YOUR_JOB: Handle stack signals relevant to your application. */
        NRF_LOG_INFO("Zigbee signal received (signal: %u, status: %d)", sig, status);
    
        /* Update network status LED */
        zigbee_led_status_update(bufid, ZIGBEE_NETWORK_STATE_LED);
    
        //switch (sig)
        //{
        //    default:
        //        /* Call default signal handler. */
        //        ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
        //        break;
        //}
    
        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);
        }
    }
    
    /***************************************************************************************************
     * @section Main
     **************************************************************************************************/
    
    /**@brief Function for application main entry.
     */
    int main(void)
    {
        zb_ret_t zb_err_code;
        bool erase_bonds;
    
        // Initialize.
        log_init();
        scheduler_init();
        timers_init();
        buttons_leds_init(&erase_bonds);
        ble_stack_init();
        gap_params_init();
        gatt_init();
        advertising_init();
        services_init();
        conn_params_init();
        peer_manager_init();
    
        // Start execution.
        NRF_LOG_INFO("BLE Zigbee template example started.");
        application_timers_start();
    
        zigbee_init();
    
        /** Start Zigbee Stack. */
        zb_err_code = zboss_start_no_autostart();
        ZB_ERROR_CHECK(zb_err_code);
    
        advertising_start(erase_bonds);
    
        // Enter main loop.
        while (true)
        {
            zboss_main_loop_iteration();
            UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
            app_sched_execute();
        }
    }
    
    
    /**
     * @}
     */

    Best regards,

    Marte

  • Hi Marte,

    Thank you very much, it works now. I can successfully join a zigbee network now.

    I think it's indeed because of the misconfiguration, I configured an on_off switch but it should of course be window covering. However I couldn't find anything about this, and I see now you made your own struct with the correct values. Where did you find this? Or you got it from the Zigbee specification?

    Where do you see the endpoint failure? In the logging I see only this:

    <info> app: Zigbee stack initialized
    <info> zboss:  00 00 00 00 DE AD 0E 02|........
    <info> zboss:  01 00 0F 00 2B 08 07 08|....+...
    <info> zboss:  01 00 00 00 DE AD 1E 02|........
    <info> zboss:  01 00 10 00 A7 F8 13 01|........
    <info> zboss:  02 00 00 00 01 00 00 00|........
    <info> zboss:  01 00 00 00 01 00 00 00|........
    <info> zboss:  00 00 00 00 DE AD 1A 02|........
    <info> zboss:  28 00 11 00 2B 08 79 06|(...+.y.
    <info> zboss:  00 00 00 00 A3 62 00 00|.....b..
    <info> zboss:  0F 00 00 00 01 00 00 00|........
    <info> zboss:  DE AD 12 02 28 00 12 00|....(...
    <info> zboss:  2B 08 7A 06 16 5F 08 FF|+.z.._..
    <info> zboss:  FF 2E 21 00 DE AD 12 02|..!.....
    <info> zboss:  29 00 13 00 A7 F8 CA 01|).......
    <info> zboss:  06 00 00 00 00 00 00 00|........
    <info> zboss:  DE AD 16 02 29 00 14 00|....)...
    <info> zboss:  2B 08 43 04 04 00 00 00|+.C.....
    <info> zboss:  00 00 00 00 06 00 00 00|........
    <info> app: Zigbee signal received (signal: 6, status: 0)
    <info> app: Joined network successfully on reboot signal (Extended PAN ID: 00212effff085f16, PAN ID: 0xB722)
    

  • Hi,

    Gueston said:
    and I see now you made your own struct with the correct values. Where did you find this?

    Are you thinking of zb_zcl_window_covering_attrs_t and the data types of the attributes there? You can find it in the Zigbee cluster library specification, in the Window Covering attribute set under the Window Covering cluster chapter. You can also find it in zb_zcl_window_covering.h, for example for installed_open_limit_tilt you see it is ZB_ZCL_ATTR_TYPE_U16:

    #define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_ID(data_ptr) \
    {                                                                                                 \
      ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_ID,                                       \
      ZB_ZCL_ATTR_TYPE_U16,                                                                           \
      ZB_ZCL_ATTR_ACCESS_READ_ONLY,                                                                   \
      (zb_voidp_t) data_ptr                                                                           \
    }

    If you instead mean window_cowering_ctx_t then this is also something you can find several places. The specification is one of them. Another is our documentation for the HA device: Window Covering:

    Window Covering device has 5 clusters (see spec 7.6.3):

    But you can also get it from looking at ZB_HA_DECLARE_WINDOW_COVERING_CLUSTER_LIST and which clusters to add there.

    Gueston said:
    Where do you see the endpoint failure? In the logging I see only this:

    I saw it when testing with another device. I used the Zigbee: Shell example from nRF Connect SDK, but Zigbee CLI Agent from nRF5 SDK can also be used, since you are using that and not nRF Connect SDK. With this you can send commands other devices (see Zigbee shell commands for a full list of commands in nRF Connect SDK and Zigbee CLI Reference for the commands in nRF5 SDK). I tried to send a simple descriptor request (zdo simple_desc_req) to the window covering device but I got an error that the endpoint does not exist. So I tried to send an active endpoint request (zdo active_ep), but the endpoint for the window covering device (endpoint 1) was not part of the response.

    Best regards,

    Marte

Related