From f05d1f8e3b26055be61dc623180e22958bc6a588 Mon Sep 17 00:00:00 2001 From: rbaron Date: Sat, 3 Dec 2022 18:59:10 +0100 Subject: [PATCH] Zigbee sample works with nRF52840dk + zigbee2mqtt --- code/nrf-connect/samples/zigbee/.gitignore | 2 + .../nrf-connect/samples/zigbee/CMakeLists.txt | 19 ++ .../zigbee/include/zb_range_extender.h | 113 ++++++++ code/nrf-connect/samples/zigbee/prj.conf | 43 +++ code/nrf-connect/samples/zigbee/src/main.c | 245 ++++++++++++++++++ 5 files changed, 422 insertions(+) create mode 100644 code/nrf-connect/samples/zigbee/.gitignore create mode 100644 code/nrf-connect/samples/zigbee/CMakeLists.txt create mode 100644 code/nrf-connect/samples/zigbee/include/zb_range_extender.h create mode 100644 code/nrf-connect/samples/zigbee/prj.conf create mode 100644 code/nrf-connect/samples/zigbee/src/main.c diff --git a/code/nrf-connect/samples/zigbee/.gitignore b/code/nrf-connect/samples/zigbee/.gitignore new file mode 100644 index 0000000..2286ab6 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/.gitignore @@ -0,0 +1,2 @@ +build +build_* diff --git a/code/nrf-connect/samples/zigbee/CMakeLists.txt b/code/nrf-connect/samples/zigbee/CMakeLists.txt new file mode 100644 index 0000000..90d0e20 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/CMakeLists.txt @@ -0,0 +1,19 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project("Zigbee application template") + +# NORDIC SDK APP START +target_sources(app PRIVATE + src/main.c +) + +target_include_directories(app PRIVATE include) +# NORDIC SDK APP END diff --git a/code/nrf-connect/samples/zigbee/include/zb_range_extender.h b/code/nrf-connect/samples/zigbee/include/zb_range_extender.h new file mode 100644 index 0000000..a359452 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/include/zb_range_extender.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef ZB_RANGE_EXTENDER_H +#define ZB_RANGE_EXTENDER_H 1 + +/** + * @defgroup ZB_DEFINE_DEVICE_RANGE_EXTENDER Range Extender + * @{ + * @details + * - @ref ZB_ZCL_IDENTIFY \n + * - @ref ZB_ZCL_BASIC + */ + +/** Range Extender Device ID*/ +#define ZB_RANGE_EXTENDER_DEVICE_ID 0x0008 + +/** Range extender device version */ +#define ZB_DEVICE_VER_RANGE_EXTENDER 0 + +/** @cond internals_doc */ + +/** Range extender IN (server) clusters number */ +#define ZB_RANGE_EXTENDER_IN_CLUSTER_NUM 2 + +/** Range extender OUT (client) clusters number */ +#define ZB_RANGE_EXTENDER_OUT_CLUSTER_NUM 0 + +#define ZB_RANGE_EXTENDER_CLUSTER_NUM \ + (ZB_RANGE_EXTENDER_IN_CLUSTER_NUM + ZB_RANGE_EXTENDER_OUT_CLUSTER_NUM) + +/** Number of attribute for reporting on Range extender device */ +#define ZB_RANGE_EXTENDER_REPORT_ATTR_COUNT 0 + +/** @endcond */ /* internals_doc */ + +/** + * @brief Declare cluster list for Range extender device + * @param cluster_list_name - cluster list variable name + * @param basic_attr_list - attribute list for Basic cluster + * @param identify_attr_list - attribute list for Identify cluster + */ +#define ZB_DECLARE_RANGE_EXTENDER_CLUSTER_LIST( \ + cluster_list_name, \ + basic_attr_list, \ + identify_attr_list) \ +zb_zcl_cluster_desc_t cluster_list_name[] = \ +{ \ + ZB_ZCL_CLUSTER_DESC( \ + ZB_ZCL_CLUSTER_ID_IDENTIFY, \ + ZB_ZCL_ARRAY_SIZE(identify_attr_list, zb_zcl_attr_t), \ + (identify_attr_list), \ + ZB_ZCL_CLUSTER_SERVER_ROLE, \ + ZB_ZCL_MANUF_CODE_INVALID \ + ), \ + ZB_ZCL_CLUSTER_DESC( \ + ZB_ZCL_CLUSTER_ID_BASIC, \ + ZB_ZCL_ARRAY_SIZE(basic_attr_list, zb_zcl_attr_t), \ + (basic_attr_list), \ + ZB_ZCL_CLUSTER_SERVER_ROLE, \ + ZB_ZCL_MANUF_CODE_INVALID \ + ) \ +} + +/** @cond internals_doc */ + +/** + * @brief Declare simple descriptor for Range extender device + * @param ep_name - endpoint variable name + * @param ep_id - endpoint ID + * @param in_clust_num - number of supported input clusters + * @param out_clust_num - number of supported output clusters + */ +#define ZB_ZCL_DECLARE_RANGE_EXTENDER_SIMPLE_DESC(ep_name, ep_id, in_clust_num, out_clust_num) \ + ZB_DECLARE_SIMPLE_DESC(in_clust_num, out_clust_num); \ + ZB_AF_SIMPLE_DESC_TYPE(in_clust_num, out_clust_num) simple_desc_##ep_name = \ + { \ + ep_id, \ + ZB_AF_HA_PROFILE_ID, \ + ZB_RANGE_EXTENDER_DEVICE_ID, \ + ZB_DEVICE_VER_RANGE_EXTENDER, \ + 0, \ + in_clust_num, \ + out_clust_num, \ + { \ + ZB_ZCL_CLUSTER_ID_BASIC, \ + ZB_ZCL_CLUSTER_ID_IDENTIFY \ + } \ + } + +/** @endcond */ /* internals_doc */ + +/** + * @brief Declare endpoint for Range extender device + * @param ep_name - endpoint variable name + * @param ep_id - endpoint ID + * @param cluster_list - endpoint cluster list + */ +#define ZB_DECLARE_RANGE_EXTENDER_EP(ep_name, ep_id, cluster_list) \ + ZB_ZCL_DECLARE_RANGE_EXTENDER_SIMPLE_DESC(ep_name, ep_id, \ + ZB_RANGE_EXTENDER_IN_CLUSTER_NUM, ZB_RANGE_EXTENDER_OUT_CLUSTER_NUM); \ + ZB_AF_DECLARE_ENDPOINT_DESC(ep_name, ep_id, ZB_AF_HA_PROFILE_ID, 0, NULL, \ + ZB_ZCL_ARRAY_SIZE(cluster_list, zb_zcl_cluster_desc_t), cluster_list, \ + (zb_af_simple_desc_1_1_t *)&simple_desc_##ep_name, \ + 0, NULL, /* No reporting ctx */ \ + 0, NULL) /* No CVC ctx */ + +/*! @} */ + +#endif /* ZB_RANGE_EXTENDER_H */ diff --git a/code/nrf-connect/samples/zigbee/prj.conf b/code/nrf-connect/samples/zigbee/prj.conf new file mode 100644 index 0000000..15a1e3f --- /dev/null +++ b/code/nrf-connect/samples/zigbee/prj.conf @@ -0,0 +1,43 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_NCS_SAMPLES_DEFAULTS=y + +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_SERIAL=y +CONFIG_GPIO=y + +# Make sure printk is not printing to the UART console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +CONFIG_HEAP_MEM_POOL_SIZE=2048 +CONFIG_MAIN_THREAD_PRIORITY=7 + +CONFIG_ZIGBEE=y +CONFIG_ZIGBEE_APP_UTILS=y +CONFIG_ZIGBEE_ROLE_ROUTER=y + +# Enable DK LED and Buttons library +CONFIG_DK_LIBRARY=y + +# This example requires more workqueue stack +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +# Enable nRF ECB driver +CONFIG_CRYPTO=y +CONFIG_CRYPTO_NRF_ECB=y +CONFIG_CRYPTO_INIT_PRIORITY=80 + +# Networking +CONFIG_NET_IPV6_MLD=n +CONFIG_NET_IPV6_NBR_CACHE=n +CONFIG_NET_IPV6_RA_RDNSS=n +CONFIG_NET_IP_ADDR_CHECK=n +CONFIG_NET_UDP=n + +# Get Zigbee to scan every channel. +CONFIG_ZIGBEE_CHANNEL_SELECTION_MODE_MULTI=y \ No newline at end of file diff --git a/code/nrf-connect/samples/zigbee/src/main.c b/code/nrf-connect/samples/zigbee/src/main.c new file mode 100644 index 0000000..4669832 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/src/main.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** @file + * + * @brief Zigbee application template. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include "zb_range_extender.h" + + +/* Device endpoint, used to receive ZCL commands. */ +#define APP_TEMPLATE_ENDPOINT 10 + +/* Type of power sources available for the device. + * For possible values see section 3.2.2.2.8 of ZCL specification. + */ +#define TEMPLATE_INIT_BASIC_POWER_SOURCE ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE + +/* LED indicating that device successfully joined Zigbee network. */ +#define ZIGBEE_NETWORK_STATE_LED DK_LED3 + +/* LED used for device identification. */ +#define IDENTIFY_LED DK_LED4 + +/* Button used to enter the Identify mode. */ +#define IDENTIFY_MODE_BUTTON DK_BTN4_MSK + +/* Button to start Factory Reset */ +#define FACTORY_RESET_BUTTON IDENTIFY_MODE_BUTTON + +LOG_MODULE_REGISTER(app, LOG_LEVEL_INF); + +/* Main application customizable context. + * Stores all settings and static values. + */ +struct zb_device_ctx { + zb_zcl_basic_attrs_t basic_attr; + zb_zcl_identify_attrs_t identify_attr; +}; + +/* Zigbee device application context storage. */ +static struct zb_device_ctx dev_ctx; + +ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST( + identify_attr_list, + &dev_ctx.identify_attr.identify_time); + +ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST( + basic_attr_list, + &dev_ctx.basic_attr.zcl_version, + &dev_ctx.basic_attr.power_source); + +ZB_DECLARE_RANGE_EXTENDER_CLUSTER_LIST( + app_template_clusters, + basic_attr_list, + identify_attr_list); + +ZB_DECLARE_RANGE_EXTENDER_EP( + app_template_ep, + APP_TEMPLATE_ENDPOINT, + app_template_clusters); + +ZBOSS_DECLARE_DEVICE_CTX_1_EP( + app_template_ctx, + app_template_ep); + + +/**@brief Function for initializing all clusters attributes. */ +static void app_clusters_attr_init(void) +{ + /* Basic cluster attributes data */ + dev_ctx.basic_attr.zcl_version = ZB_ZCL_VERSION; + dev_ctx.basic_attr.power_source = TEMPLATE_INIT_BASIC_POWER_SOURCE; + + /* Identify cluster attributes data. */ + dev_ctx.identify_attr.identify_time = + ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE; +} + +/**@brief Function to toggle the identify LED + * + * @param bufid Unused parameter, required by ZBOSS scheduler API. + */ +static void toggle_identify_led(zb_bufid_t bufid) +{ + static int blink_status; + + dk_set_led(IDENTIFY_LED, (++blink_status) % 2); + ZB_SCHEDULE_APP_ALARM(toggle_identify_led, bufid, ZB_MILLISECONDS_TO_BEACON_INTERVAL(100)); +} + +/**@brief Function to handle identify notification events on the first endpoint. + * + * @param bufid Unused parameter, required by ZBOSS scheduler API. + */ +static void identify_cb(zb_bufid_t bufid) +{ + zb_ret_t zb_err_code; + + if (bufid) { + /* Schedule a self-scheduling function that will toggle the LED */ + ZB_SCHEDULE_APP_CALLBACK(toggle_identify_led, bufid); + } else { + /* Cancel the toggling function alarm and turn off LED */ + zb_err_code = ZB_SCHEDULE_APP_ALARM_CANCEL(toggle_identify_led, ZB_ALARM_ANY_PARAM); + ZVUNUSED(zb_err_code); + + dk_set_led(IDENTIFY_LED, 0); + } +} + +/**@brief Starts identifying the device. + * + * @param bufid Unused parameter, required by ZBOSS scheduler API. + */ +static void start_identifying(zb_bufid_t bufid) +{ + ZVUNUSED(bufid); + + if (ZB_JOINED()) { + /* Check if endpoint is in identifying mode, + * if not put desired endpoint in identifying mode. + */ + if (dev_ctx.identify_attr.identify_time == + ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE) { + + zb_ret_t zb_err_code = zb_bdb_finding_binding_target( + APP_TEMPLATE_ENDPOINT); + + if (zb_err_code == RET_OK) { + LOG_INF("Enter identify mode"); + } else if (zb_err_code == RET_INVALID_STATE) { + LOG_WRN("RET_INVALID_STATE - Cannot enter identify mode"); + } else { + ZB_ERROR_CHECK(zb_err_code); + } + } else { + LOG_INF("Cancel identify mode"); + zb_bdb_finding_binding_target_cancel(); + } + } else { + LOG_WRN("Device not in a network - cannot enter identify mode"); + } +} + +/**@brief Callback for button events. + * + * @param[in] button_state Bitmask containing buttons state. + * @param[in] has_changed Bitmask containing buttons + * that have changed their state. + */ +static void button_changed(uint32_t button_state, uint32_t has_changed) +{ + if (IDENTIFY_MODE_BUTTON & has_changed) { + if (IDENTIFY_MODE_BUTTON & button_state) { + /* Button changed its state to pressed */ + } else { + /* Button changed its state to released */ + if (was_factory_reset_done()) { + /* The long press was for Factory Reset */ + LOG_DBG("After Factory Reset - ignore button release"); + } else { + /* Button released before Factory Reset */ + + /* Start identification mode */ + ZB_SCHEDULE_APP_CALLBACK(start_identifying, 0); + } + } + } + + check_factory_reset_button(button_state, has_changed); +} + +/**@brief Function for initializing LEDs and Buttons. */ +static void configure_gpio(void) +{ + int err; + + err = dk_buttons_init(button_changed); + if (err) { + LOG_ERR("Cannot init buttons (err: %d)", err); + } + + err = dk_leds_init(); + if (err) { + LOG_ERR("Cannot init LEDs (err: %d)", err); + } +} + +/**@brief Zigbee stack event handler. + * + * @param[in] bufid Reference to the Zigbee stack buffer + * used to pass signal. + */ +void zboss_signal_handler(zb_bufid_t bufid) +{ + /* Update network status LED. */ + zigbee_led_status_update(bufid, ZIGBEE_NETWORK_STATE_LED); + + /* No application-specific behavior is required. + * Call default signal handler. + */ + ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid)); + + /* All callbacks should either reuse or free passed buffers. + * If bufid == 0, the buffer is invalid (not passed). + */ + if (bufid) { + zb_buf_free(bufid); + } +} + +void main(void) +{ + LOG_INF("Starting Zigbee application template example"); + + /* Initialize */ + configure_gpio(); + register_factory_reset_button(FACTORY_RESET_BUTTON); + + /* Register device context (endpoints). */ + ZB_AF_REGISTER_DEVICE_CTX(&app_template_ctx); + + app_clusters_attr_init(); + + /* Register handlers to identify notifications */ + ZB_AF_SET_IDENTIFY_NOTIFICATION_HANDLER(APP_TEMPLATE_ENDPOINT, identify_cb); + + /* Start Zigbee default thread */ + zigbee_enable(); + + LOG_INF("Zigbee application template started"); +}