Hi,
I'm trying to modify the light bulb example and turn it into a light switch (not a light switch that controls another bulb in the network), but I'm quite new on the Zigbee API and on the nRF52 development.
My main goal is to toggle a relay (in this example emulated by LED2_G).
The code right now looks like this:
/**
* Copyright (c) 2018 - 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_light_bulb main.c
* @{
* @ingroup zigbee_examples
* @brief Dimmable light sample (HA profile)
*/
#include "sdk_config.h"
#include "zboss_api.h"
#include "zboss_api_addons.h"
#include "zb_error_handler.h"
#include "zb_nrf52_internal.h"
#include "zigbee_helpers.h"
#include "bsp.h"
#include "boards.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "nrf_drv_gpiote.h"
#define MAX_CHILDREN 10 /**< The maximum amount of connected devices. Setting this value to 0 disables association to this device. */
#define IEEE_CHANNEL_MASK (1l << ZIGBEE_CHANNEL) /**< Scan only one, predefined channel to find the coordinator. */
#define HA_SWITCH_ENDPOINT 10 /**< Device endpoint, used to receive light controlling commands. */
#define ERASE_PERSISTENT_CONFIG ZB_FALSE /**< Do not erase NVRAM to save the network parameters after device reboot or power-off. */
/* Basic cluster attributes initial values. */
#define BULB_INIT_BASIC_APP_VERSION 01 /**< Version of the application software (1 byte). */
#define BULB_INIT_BASIC_STACK_VERSION 10 /**< Version of the implementation of the Zigbee stack (1 byte). */
#define BULB_INIT_BASIC_HW_VERSION 11 /**< Version of the hardware of the device (1 byte). */
#define BULB_INIT_BASIC_MANUF_NAME "Test" /**< Manufacturer name (32 bytes). */
#define BULB_INIT_BASIC_MODEL_ID "Switchv0" /**< Model number assigned by manufacturer (32-bytes long string). */
#define BULB_INIT_BASIC_DATE_CODE "20180416" /**< First 8 bytes specify the date of manufacturer of the device in ISO 8601 format (YYYYMMDD). The rest (8 bytes) are manufacturer specific. */
#define BULB_INIT_BASIC_POWER_SOURCE ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE /**< Type of power sources available for the device. For possible values see section 3.2.2.2.8 of ZCL specification. */
#define BULB_INIT_BASIC_LOCATION_DESC "Office desk" /**< Describes the physical location of the device (16 bytes). May be modified during commisioning process. */
#define BULB_INIT_BASIC_PH_ENV ZB_ZCL_BASIC_ENV_UNSPECIFIED /**< Describes the type of physical environment. For possible values see section 3.2.2.2.10 of ZCL specification. */
#ifdef BOARD_PCA10059 /**< If it is Dongle */
#define IDENTIFY_MODE_BSP_EVT BSP_EVENT_KEY_0 /**< Button event used to enter the Bulb into the Identify mode. */
#define ZIGBEE_NETWORK_STATE_LED BSP_BOARD_LED_0 /**< LED indicating that light switch successfully joind Zigbee network. */
#else
#define IDENTIFY_MODE_BSP_EVT BSP_EVENT_KEY_3 /**< Button event used to enter the Bulb into the Identify mode. */
#define ZIGBEE_NETWORK_STATE_LED BSP_BOARD_LED_2 /**< LED indicating that light switch successfully joind Zigbee network. */
#endif
#if !defined ZB_ROUTER_ROLE
#error Define ZB_ROUTER_ROLE to compile light bulb (Router) source code.
#endif
#define LED2_G NRF_GPIO_PIN_MAP(1,9)
/* Main application customizable context. Stores all settings and static values. */
typedef struct
{
zb_zcl_basic_attrs_ext_t basic_attr;
zb_zcl_identify_attrs_t identify_attr;
zb_zcl_on_off_attrs_ext_t on_off_attr;
} switch_device_ctx_t;
static switch_device_ctx_t m_dev_ctx;
/******************* Declare attributes ************************/
/* Switch config cluster attributes data */
zb_uint8_t attr_switch_type = ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_TYPE_TOGGLE;
zb_uint8_t attr_switch_actions = ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_ACTIONS_DEFAULT_VALUE;
ZB_ZCL_DECLARE_ON_OFF_SWITCH_CONFIGURATION_ATTRIB_LIST(switch_cfg_attr_list, &attr_switch_type, &attr_switch_actions);
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_IDENTIFY_ATTRIB_LIST(identify_attr_list, &m_dev_ctx.identify_attr.identify_time);
/********************* Declare device **************************/
ZB_HA_DECLARE_ON_OFF_SWITCH_CLUSTER_LIST(on_off_switch_clusters, switch_cfg_attr_list, basic_attr_list, identify_attr_list);
ZB_HA_DECLARE_ON_OFF_SWITCH_EP(on_off_switch_ep, HA_SWITCH_ENDPOINT, on_off_switch_clusters);
ZB_HA_DECLARE_ON_OFF_SWITCH_CTX(on_off_switch_ctx, on_off_switch_ep);
/**@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();
}
static void gpio_init(void)
{
ret_code_t err_code;
err_code = nrf_drv_gpiote_init();
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(false);
err_code = nrf_drv_gpiote_out_init(LED2_G, &out_config);
APP_ERROR_CHECK(err_code);
}
/**@brief Function for turning ON/OFF the light bulb.
*
* @param[in] on Boolean light bulb state.
*/
static void on_off_set_value(zb_bool_t on)
{
NRF_LOG_INFO("Set ON/OFF value: %i", on);
ZB_ZCL_SET_ATTRIBUTE(HA_SWITCH_ENDPOINT,
ZB_ZCL_CLUSTER_ID_ON_OFF,
ZB_ZCL_CLUSTER_SERVER_ROLE,
ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
(zb_uint8_t *)&on,
ZB_FALSE);
if (on)
{
nrf_drv_gpiote_out_set(LED2_G);
}
else
{
nrf_drv_gpiote_out_clear(LED2_G);
}
}
/**@brief Callback for button events.
*
* @param[in] evt Incoming event from the BSP subsystem.
*/
static void buttons_handler(bsp_event_t evt)
{
zb_ret_t zb_err_code;
switch(evt)
{
case IDENTIFY_MODE_BSP_EVT:
/* Check if endpoint is in identifying mode, if not put desired endpoint in identifying mode. */
if (m_dev_ctx.identify_attr.identify_time == ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE)
{
NRF_LOG_INFO("Bulb put in identifying mode");
zb_err_code = zb_bdb_finding_binding_target(HA_SWITCH_ENDPOINT);
ZB_ERROR_CHECK(zb_err_code);
}
else
{
NRF_LOG_INFO("Cancel F&B target procedure");
zb_bdb_finding_binding_target_cancel();
}
break;
default:
NRF_LOG_INFO("Unhandled BSP Event received: %d", evt);
break;
}
}
/**@brief Function for initializing LEDs and a single PWM channel.
*/
static void buttons_init(void)
{
ret_code_t err_code;
/* Initialize all and buttons. */
err_code = bsp_init(BSP_INIT_BUTTONS, buttons_handler);
APP_ERROR_CHECK(err_code);
/* By default the bsp_init attaches BSP_KEY_EVENTS_{0-4} to the PUSH events of the corresponding buttons. */
}
/**@brief Function for initializing all clusters attributes.
*/
static void switch_clusters_attr_init(void)
{
/* Basic cluster attributes data */
m_dev_ctx.basic_attr.zcl_version = ZB_ZCL_VERSION;
m_dev_ctx.basic_attr.app_version = BULB_INIT_BASIC_APP_VERSION;
m_dev_ctx.basic_attr.stack_version = BULB_INIT_BASIC_STACK_VERSION;
m_dev_ctx.basic_attr.hw_version = BULB_INIT_BASIC_HW_VERSION;
/* Use ZB_ZCL_SET_STRING_VAL to set strings, because the first byte should
* contain string length without trailing zero.
*
* For example "test" string wil be encoded as:
* [(0x4), 't', 'e', 's', 't']
*/
ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.mf_name,
BULB_INIT_BASIC_MANUF_NAME,
ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_MANUF_NAME));
ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.model_id,
BULB_INIT_BASIC_MODEL_ID,
ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_MODEL_ID));
ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.date_code,
BULB_INIT_BASIC_DATE_CODE,
ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_DATE_CODE));
m_dev_ctx.basic_attr.power_source = BULB_INIT_BASIC_POWER_SOURCE;
ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.location_id,
BULB_INIT_BASIC_LOCATION_DESC,
ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_LOCATION_DESC));
m_dev_ctx.basic_attr.ph_env = BULB_INIT_BASIC_PH_ENV;
/* Identify cluster attributes data */
m_dev_ctx.identify_attr.identify_time = ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE;
/* On/Off cluster attributes data */
m_dev_ctx.on_off_attr.on_off = (zb_bool_t)ZB_ZCL_ON_OFF_IS_ON;
ZB_ZCL_SET_ATTRIBUTE(HA_SWITCH_ENDPOINT,
ZB_ZCL_CLUSTER_ID_ON_OFF,
ZB_ZCL_CLUSTER_SERVER_ROLE,
ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
(zb_uint8_t *)&m_dev_ctx.on_off_attr.on_off,
ZB_FALSE);
}
/**@brief Function which tries to sleep down the MCU
*
* Function which sleeps the MCU on the non-sleepy End Devices to optimize the power saving.
* The weak definition inside the OSIF layer provides some minimal working template
*/
zb_void_t zb_osif_go_idle(zb_void_t)
{
//TODO: implement your own logic if needed
zb_osif_wait_for_event();
}
/**@brief Callback function for handling ZCL commands.
*
* @param[in] bufid Reference to Zigbee stack buffer used to pass received data.
*/
static zb_void_t zcl_device_cb(zb_bufid_t bufid)
{
zb_uint8_t cluster_id;
zb_uint8_t attr_id;
zb_zcl_device_callback_param_t * p_device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);
NRF_LOG_INFO("zcl_device_cb id %hd", p_device_cb_param->device_cb_id);
/* Set default response value. */
p_device_cb_param->status = RET_OK;
switch (p_device_cb_param->device_cb_id)
{
case ZB_ZCL_LEVEL_CONTROL_SET_VALUE_CB_ID:
NRF_LOG_INFO("Level control setting to %d", p_device_cb_param->cb_param.level_control_set_value_param.new_value);
//level_control_set_value(p_device_cb_param->cb_param.level_control_set_value_param.new_value);
break;
case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
cluster_id = p_device_cb_param->cb_param.set_attr_value_param.cluster_id;
attr_id = p_device_cb_param->cb_param.set_attr_value_param.attr_id;
if (cluster_id == ZB_ZCL_CLUSTER_ID_ON_OFF)
{
uint8_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data8;
NRF_LOG_INFO("on/off attribute setting to %hd", value);
if (attr_id == ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID)
{
on_off_set_value((zb_bool_t) value);
}
}
else
{
/* Other clusters can be processed here */
NRF_LOG_INFO("Unhandled cluster attribute id: %d", cluster_id);
}
break;
default:
p_device_cb_param->status = RET_ERROR;
break;
}
NRF_LOG_INFO("zcl_device_cb status: %hd", p_device_cb_param->status);
}
/**@brief Zigbee stack event handler.
*
* @param[in] bufid Reference to the Zigbee stack buffer used to pass signal.
*/
void zboss_signal_handler(zb_bufid_t bufid)
{
/* Update network status LED */
zigbee_led_status_update(bufid, ZIGBEE_NETWORK_STATE_LED);
/* No application-specific behavior is required. Call default signal handler. */
ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
if (bufid)
{
zb_buf_free(bufid);
}
}
/**@brief Function for application main entry.
*/
int main(void)
{
zb_ret_t zb_err_code;
zb_ieee_addr_t ieee_addr;
/* Initialize timer, logging system and GPIOs. */
log_init();
buttons_init();
gpio_init();
/* Set Zigbee stack logging level and traffic dump subsystem. */
ZB_SET_TRACE_LEVEL(ZIGBEE_TRACE_LEVEL);
ZB_SET_TRACE_MASK(ZIGBEE_TRACE_MASK);
ZB_SET_TRAF_DUMP_OFF();
/* Initialize Zigbee stack. */
ZB_INIT("led_bulb");
/* Set device address to the value read from FICR registers. */
zb_osif_get_ieee_eui64(ieee_addr);
zb_set_long_address(ieee_addr);
/* Set static long IEEE address. */
zb_set_network_router_role(IEEE_CHANNEL_MASK);
zb_set_max_children(MAX_CHILDREN);
zigbee_erase_persistent_storage(ERASE_PERSISTENT_CONFIG);
zb_set_keepalive_timeout(ZB_MILLISECONDS_TO_BEACON_INTERVAL(3000));
/* Initialize application context structure. */
UNUSED_RETURN_VALUE(ZB_MEMSET(&m_dev_ctx, 0, sizeof(m_dev_ctx)));
/* Register callback for handling ZCL commands. */
ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);
/* Register dimmer switch device context (endpoints). */
ZB_AF_REGISTER_DEVICE_CTX(&on_off_switch_ctx);
switch_clusters_attr_init();
/** Start Zigbee Stack. */
zb_err_code = zboss_start_no_autostart();
ZB_ERROR_CHECK(zb_err_code);
while(1)
{
zboss_main_loop_iteration();
UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
}
}
/**
* @}
*/
Any idea on why this isn't working? Did I miss something on the endpoint declaration?
Thanks!