Enabled debug stuff, and added got working to a basic level. Need to add different logic for different switch actions

master
noah metz 2024-02-16 00:31:20 -07:00
parent 01c037e4cd
commit fd8fc112cf
5 changed files with 459 additions and 25 deletions

@ -4,3 +4,5 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(jamie)
target_sources(app PRIVATE src/main.c)
target_include_directories(app PRIVATE include)

@ -9,6 +9,35 @@
zephyr,entropy = &rng;
ncs,zigbee-timer = &timer2;
};
leds {
network_led: network_led {
gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
label = "Network Activity LED";
};
};
buttons {
sw_0: sw0 {
gpios = <&gpio1 10 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
label = "Switch 1 Input";
};
sw_1: sw1 {
gpios = <&gpio1 13 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
label = "Switch 2 Input";
};
sw_2: sw2 {
gpios = <&gpio1 15 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
label = "Switch 3 Input";
};
sw_3: sw3 {
gpios = <&gpio0 2 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
label = "Switch 4 Input";
};
};
};
&timer2 {

@ -0,0 +1,76 @@
#ifndef SWITCH_H
#define SWITCH_H
#define ZB_DECLARE_INFO_EP(ep_name, ep_id, dev_ctx) \
\
ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST( \
ep_name ## _identify_attr_list, \
&dev_ctx.identify_attr.identify_time); \
\
ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT( \
ep_name ## _basic_attr_list, \
&dev_ctx.basic_attr.zcl_version, \
&dev_ctx.basic_attr.app_version, \
&dev_ctx.basic_attr.stack_version, \
&dev_ctx.basic_attr.hw_version, \
dev_ctx.basic_attr.mf_name, \
dev_ctx.basic_attr.model_id, \
dev_ctx.basic_attr.date_code, \
&dev_ctx.basic_attr.power_source, \
dev_ctx.basic_attr.location_id, \
&dev_ctx.basic_attr.ph_env, \
dev_ctx.basic_attr.sw_ver); \
\
zb_zcl_cluster_desc_t ep_name ## _clusters[] = { \
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_IDENTIFY, ZB_ZCL_ARRAY_SIZE(ep_name ## _identify_attr_list, zb_zcl_attr_t), (ep_name ## _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(ep_name ## _basic_attr_list, zb_zcl_attr_t), (ep_name ## _basic_attr_list), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
}; \
\
ZB_AF_SIMPLE_DESC_TYPE(2, 0) ep_name ## _desc = { \
ep_id, \
ZB_AF_HA_PROFILE_ID, \
0x0008, \
0, \
0, \
2, \
0, \
{ \
ZB_ZCL_CLUSTER_ID_BASIC, \
ZB_ZCL_CLUSTER_ID_IDENTIFY, \
} \
}; \
\
ZB_AF_DECLARE_ENDPOINT_DESC(ep_name, ep_id, ZB_AF_HA_PROFILE_ID, 0, NULL, ZB_ZCL_ARRAY_SIZE(ep_name ## _clusters, zb_zcl_cluster_desc_t), ep_name ## _clusters, (zb_af_simple_desc_1_1_t *)&ep_name ## _desc, 0, NULL, 0, NULL)
#define ZB_DECLARE_SW_EP(ep_name, ep_id, state, type, action) \
\
ZB_ZCL_DECLARE_ON_OFF_ATTRIB_LIST(ep_name ## _attribs, &state.on_off); \
ZB_ZCL_DECLARE_ON_OFF_CLIENT_ATTRIB_LIST(ep_name ## _client_attribs); \
ZB_ZCL_DECLARE_ON_OFF_SWITCH_CONFIGURATION_ATTRIB_LIST(ep_name ## _config_attribs, &type, &action); \
\
zb_zcl_cluster_desc_t ep_name ## _clusters[] = { \
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_ON_OFF, ZB_ZCL_ARRAY_SIZE(ep_name ## _attribs, zb_zcl_attr_t), (ep_name ## _attribs), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_ON_OFF, ZB_ZCL_ARRAY_SIZE(ep_name ## _client_attribs, zb_zcl_attr_t), (ep_name ## _client_attribs), ZB_ZCL_CLUSTER_CLIENT_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_ON_OFF_SWITCH_CONFIG, ZB_ZCL_ARRAY_SIZE(ep_name ## _config_attribs, zb_zcl_attr_t), (ep_name ## _config_attribs), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
}; \
\
ZB_AF_SIMPLE_DESC_TYPE(2, 1) ep_name ## _desc = { \
ep_id, \
ZB_AF_HA_PROFILE_ID, \
0x0000, \
0, \
0, \
2, \
1, \
{ \
ZB_ZCL_CLUSTER_ID_ON_OFF, \
ZB_ZCL_CLUSTER_ID_ON_OFF, \
ZB_ZCL_CLUSTER_ID_ON_OFF_SWITCH_CONFIG, \
} \
}; \
\
ZBOSS_DEVICE_DECLARE_REPORTING_CTX(ep_name ## _rep_ctx, 1); \
ZB_AF_DECLARE_ENDPOINT_DESC(ep_name, ep_id, ZB_AF_HA_PROFILE_ID, 0, NULL, ZB_ZCL_ARRAY_SIZE(ep_name ## _clusters, zb_zcl_cluster_desc_t), ep_name ## _clusters, (zb_af_simple_desc_1_1_t *)&ep_name ## _desc, 1, ep_name ## _rep_ctx, 0, NULL)
#endif // SWITCH_H

@ -1,9 +1,16 @@
#
# Copyright (c) 2022 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
# LOG configuration
CONFIG_LOG=y
CONFIG_LOG_MODE_DEFERRED=y
CONFIG_LOG_BUFFER_SIZE=4096
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=1024
CONFIG_LOG_PROCESS_THREAD_STARTUP_DELAY_MS=1000
# Configure serial
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_SERIAL=y
@ -16,9 +23,13 @@ CONFIG_HEAP_MEM_POOL_SIZE=2048
CONFIG_MAIN_THREAD_PRIORITY=7
CONFIG_ZIGBEE=y
CONFIG_ZIGBEE_CHANNEL=11
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
@ -31,24 +42,30 @@ CONFIG_CRYPTO_INIT_PRIORITY=80
CONFIG_NET_IPV6=n
CONFIG_NET_IP_ADDR_CHECK=n
# Zigbee shell
CONFIG_ZIGBEE_SHELL=y
CONFIG_ZIGBEE_SHELL_DEBUG_CMD=y
CONFIG_ZIGBEE_SHELL_ENDPOINT=64
# Increase RX serial ring buffer
CONFIG_SHELL_BACKEND_SERIAL_RX_RING_BUFFER_SIZE=128
# USB subsystem configuration
CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_DEVICE_PRODUCT="Zigbee Shell"
CONFIG_USB_CDC_ACM=y
CONFIG_UART_LINE_CTRL=y
# Initialize USB device as soon as possible as the USB CDC ACM is used
# as backend for both Shell and Logging subsystems.
CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=y
# Enable this to duplicate Zephyr logs to default Logger serial backend,
# usually connected to on-board JLink device.
# CONFIG_LOG_BACKEND_UART=y
CONFIG_BOARD_HAS_NRF5_BOOTLOADER=n
CONFIG_DEBUG_OPTIMIZATIONS=y
CONFIG_DEBUG_THREAD_INFO=y
CONFIG_RESET_ON_FATAL_ERROR=n
CONFIG_LOG_BACKEND_SWO=y
CONFIG_ZIGBEE_SHELL_ENDPOINT=10
CONFIG_ZIGBEE_SHELL=y
CONFIG_ZIGBEE_SHELL_DEBUG_CMD=y
CONFIG_DEBUG=y
CONFIG_ASSERT=y
CONFIG_ZBOSS_HALT_ON_ASSERT=y
CONFIG_ZIGBEE_LOGGER_EP=n
CONFIG_ZIGBEE_CHANNEL_SELECTION_MODE_MULTI=y

@ -1,31 +1,341 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/gpio.h>
#include <zboss_api.h>
#include <zboss_api_addons.h>
#include <zboss_api_zcl_addons.h>
#include <zigbee/zigbee_error_handler.h>
#include <zigbee/zigbee_app_utils.h>
#include <zb_nrf_platform.h>
#include "switch.h"
LOG_MODULE_REGISTER(app, LOG_LEVEL_INF);
#include <zboss_api.h>
void zboss_signal_handler(zb_uint8_t param) {
#define INFO_ENDPOINT CONFIG_ZIGBEE_SHELL_ENDPOINT
#define SW0_EP 1
#define SW1_EP 2
#define SW2_EP 3
#define SW3_EP 4
const uint8_t sw_ep[4] = {SW0_EP, SW1_EP, SW2_EP, SW3_EP};
/* Main application customizable context.
* Stores all settings and static values.
*/
struct app_static {
zb_zcl_basic_attrs_ext_t basic_attr;
zb_zcl_identify_attrs_t identify_attr;
enum zb_zcl_on_off_switch_configuration_switch_type_e types[4];
};
struct app_ram {
zb_zcl_on_off_attrs_t states[4];
};
struct app_nvram {
enum zb_zcl_on_off_switch_configuration_switch_actions_e actions[4];
};
/* Zigbee device application context storage. */
static struct app_static app_static = {
.basic_attr = {
.zcl_version = ZB_ZCL_VERSION,
.power_source = ZB_ZCL_BASIC_POWER_SOURCE_MAINS_SINGLE_PHASE,
.ph_env = ZB_ZCL_BASIC_ENV_UNSPECIFIED,
.app_version = 1,
.stack_version = 10,
.hw_version = 1,
.mf_name = {0},
.model_id = {0},
.date_code = {0},
.location_id = {0},
},
.identify_attr = {
.identify_time = ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE,
},
.types = {
ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_TYPE_TOGGLE,
ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_TYPE_TOGGLE,
ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_TYPE_TOGGLE,
ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_TYPE_TOGGLE,
},
};
static struct app_ram app_ram = {
.states = {
0,
0,
0,
0,
},
};
static struct app_nvram app_nvram = {
.actions = {
ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_ACTIONS_TYPE1,
ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_ACTIONS_TYPE1,
ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_ACTIONS_TYPE1,
ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_ACTIONS_TYPE1,
},
};
ZB_DECLARE_SIMPLE_DESC(2, 0);
ZB_DECLARE_SIMPLE_DESC(2, 1);
ZB_DECLARE_INFO_EP(info_ep, INFO_ENDPOINT, app_static);
ZB_DECLARE_SW_EP(sw0_ep, SW0_EP, app_ram.states[0], app_static.types[0], app_nvram.actions[0]);
ZB_DECLARE_SW_EP(sw1_ep, SW1_EP, app_ram.states[1], app_static.types[1], app_nvram.actions[1]);
ZB_DECLARE_SW_EP(sw2_ep, SW2_EP, app_ram.states[2], app_static.types[2], app_nvram.actions[2]);
ZB_DECLARE_SW_EP(sw3_ep, SW3_EP, app_ram.states[3], app_static.types[3], app_nvram.actions[3]);
ZBOSS_DECLARE_DEVICE_CTX_EP_VA(
zigbee_ctx,
&info_ep,
&sw0_ep,
&sw1_ep,
&sw2_ep,
&sw3_ep);
static const struct gpio_dt_spec network_led = GPIO_DT_SPEC_GET(DT_NODELABEL(network_led), gpios);
static const struct gpio_dt_spec sw[4] = {
GPIO_DT_SPEC_GET(DT_NODELABEL(sw_0), gpios),
GPIO_DT_SPEC_GET(DT_NODELABEL(sw_1), gpios),
GPIO_DT_SPEC_GET(DT_NODELABEL(sw_2), gpios),
GPIO_DT_SPEC_GET(DT_NODELABEL(sw_3), gpios),
};
static struct gpio_callback sw_cb[4] = {0};
#define SW_DEBOUNCE_MS 500
static uint64_t sw_last[4] = {0, 0, 0, 0};
/**@brief Function for initializing all clusters attributes. */
static void zigbee_init_clusters(void)
{
char manufacturer[] = "MetzNet";
ZB_ZCL_SET_STRING_VAL(app_static.basic_attr.mf_name, manufacturer, ZB_ZCL_STRING_CONST_SIZE(manufacturer));
char model[] = "jamie";
ZB_ZCL_SET_STRING_VAL(app_static.basic_attr.model_id, model, ZB_ZCL_STRING_CONST_SIZE(model));
char date[] = "jamie";
ZB_ZCL_SET_STRING_VAL(app_static.basic_attr.date_code, date, ZB_ZCL_STRING_CONST_SIZE(date));
char location[] = "jamie";
ZB_ZCL_SET_STRING_VAL(app_static.basic_attr.date_code, location, ZB_ZCL_STRING_CONST_SIZE(location));
}
typedef struct queue_item_s {
uint8_t sw;
int64_t time;
} queue_item_t;
struct k_queue switch_queue = {0};
void sw_handler(size_t i) {
queue_item_t* item = k_malloc(sizeof(queue_item_t));
item->sw = i;
item->time = k_uptime_get();
k_queue_alloc_append(&switch_queue, item);
}
void sw0_handler(const struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pins) {
sw_handler(0);
}
void sw1_handler(const struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pins) {
sw_handler(1);
}
void sw2_handler(const struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pins) {
sw_handler(2);
}
void sw3_handler(const struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pins) {
sw_handler(3);
}
gpio_callback_handler_t sw_handlers[4] = {
sw0_handler,
sw1_handler,
sw2_handler,
sw3_handler,
};
static void configure_gpio(void)
{
int err;
err = gpio_pin_configure_dt(&network_led, GPIO_OUTPUT);
if(err) {
}
k_queue_init(&switch_queue);
for(int i = 0; i < 4; i++) {
err = gpio_pin_configure_dt(&(sw[i]), GPIO_INPUT);
err = gpio_pin_interrupt_configure_dt(&(sw[i]), GPIO_INT_EDGE_BOTH);
gpio_init_callback(&(sw_cb[i]), sw_handlers[i], BIT(sw[i].pin));
err = gpio_add_callback(sw[i].port, &(sw_cb[i]));
sw_last[i] = k_uptime_get();
}
}
/**@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)
{
gpio_pin_toggle_dt(&network_led);
zb_zdo_app_signal_hdr_t *sig_handler = NULL;
zb_zdo_app_signal_type_t sig = zb_get_app_signal(bufid, &sig_handler);
switch (sig) {
default:
/* Call default signal handler. */
ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
break;
}
if (bufid) {
zb_buf_free(bufid);
}
}
static void identify_cb(zb_bufid_t bufid) {
(void)bufid;
}
static void set_sw(zb_uint8_t param) {
uint8_t sw = (param & 0xF0) >> 4;
uint8_t val = (param & 0x0F);
ZB_ZCL_SET_ATTRIBUTE(
sw_ep[sw],
ZB_ZCL_CLUSTER_ID_ON_OFF,
ZB_ZCL_CLUSTER_SERVER_ROLE,
ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
(zb_uint8_t *)&val,
ZB_FALSE);
LOG_INF("Set sw %d to %d", sw, val);
}
zb_ret_t app_nvram_write(zb_uint8_t page, zb_uint32_t pos) {
LOG_INF("NVRAM_WRITE");
return zb_osif_nvram_write(page, pos, (uint8_t*)(&app_nvram), sizeof(struct app_nvram));
}
void app_nvram_read(zb_uint8_t page, zb_uint32_t pos, zb_uint16_t payload_length) {
LOG_INF("NVRAM_READ");
zb_uint16_t read_len = payload_length;
if(payload_length > sizeof(struct app_nvram)) {
read_len = sizeof(struct app_nvram);
}
zb_osif_nvram_read(page, pos, (uint8_t*)(&app_nvram), payload_length);
}
zb_uint16_t app_nvram_size(void){
return (zb_uint16_t)(sizeof(struct app_nvram));
}
/**@brief Callback function for handling ZCL commands.
*
* @param[in] bufid Reference to Zigbee stack buffer
* used to pass received data.
*/
static void zcl_device_cb(zb_bufid_t bufid) {
zb_uint8_t cluster_id;
zb_uint8_t attr_id;
zb_zcl_device_callback_param_t *device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);
/* Set default response value. */
device_cb_param->status = RET_OK;
switch(device_cb_param->device_cb_id){
case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
cluster_id = device_cb_param->cb_param.set_attr_value_param.cluster_id;
attr_id = device_cb_param->cb_param.set_attr_value_param.attr_id;
switch(cluster_id) {
case ZB_ZCL_CLUSTER_ID_ON_OFF_SWITCH_CONFIG:
switch(attr_id) {
case ZB_ZCL_ATTR_ON_OFF_SWITCH_CONFIGURATION_SWITCH_ACTIONS_ID:
uint8_t value = device_cb_param->cb_param.set_attr_value_param.values.data8;
uint8_t ep = device_cb_param->endpoint;
LOG_INF("New switch actions: %d - %d", ep, value);
zb_ret_t err = zb_nvram_write_dataset(ZB_NVRAM_APP_DATA1);
if(err){
LOG_ERR("Failed to write app1 nvram: %e", err);
} else {
LOG_INF("Wrote app1 nvram");
}
break;
default:
LOG_WRN("Unhandled on_off_switch_cfg attribute ID: %d", attr_id);
device_cb_param->status = RET_NOT_IMPLEMENTED;
break;
}
break;
default:
LOG_WRN("Unhandled cluster ID: %d", cluster_id);
device_cb_param->status = RET_NOT_IMPLEMENTED;
break;
}
break;
default:
LOG_WRN("Unhandled callback ID: %d", device_cb_param->device_cb_id);
device_cb_param->status = RET_NOT_IMPLEMENTED;
break;
}
}
int main(void)
{
LOG_INF("Starting main");
LOG_INF("Starting jamie");
/* Initialize */
configure_gpio();
/* Register device callback */
ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);
/* Register device context (endpoints). */
ZB_AF_REGISTER_DEVICE_CTX(&zigbee_ctx);
zigbee_init_clusters();
ZB_AF_SET_IDENTIFY_NOTIFICATION_HANDLER(INFO_ENDPOINT, identify_cb);
const struct gpio_dt_spec led_green = GPIO_DT_SPEC_GET(DT_NODELABEL(led0_green), gpios);
zb_nvram_register_app1_read_cb(app_nvram_read);
zb_nvram_register_app1_write_cb(app_nvram_write, app_nvram_size);
gpio_pin_configure_dt(&led_green, GPIO_OUTPUT | GPIO_ACTIVE_LOW);
zigbee_enable();
LOG_INF("Zigbee Stack Initialized");
while(true) {
gpio_pin_set_dt(&led_green, 1);
while(true) {
queue_item_t* item = (queue_item_t*) k_queue_get(&switch_queue, K_FOREVER);
if(item != NULL) {
int val = gpio_pin_get_dt(&sw[item->sw]);
if((item->time - sw_last[item->sw] > SW_DEBOUNCE_MS) && (val != app_ram.states[item->sw].on_off)) {
sw_last[item->sw] = item->time;
app_ram.states[item->sw].on_off = val;
k_msleep(500);
ZB_SCHEDULE_APP_CALLBACK(set_sw, ((item->sw & 0x0F) << 4) + ((val & 0x0F) << 0));
gpio_pin_set_dt(&led_green, 0);
LOG_INF("Queue input %d to %d change", item->sw, val);
}
k_free(item);
}
}
k_msleep(500);
}
return 0;
}