Can't send and recieve ZCL on/off command properly with nrf 52840

Hi everyone,

I'm trying to send zcl on/off command between multiple nrf52840 but I can't achieve to do it properly. The project context is as follow:

Each nrf52840 controls addressable LED strip (by SPI communication) and play a LED chaser. When the first pattern come to the end off the strip, it should send an 'on' command to the following card in order to start LED chaser. The purpose of the project is to create a continuous ligth pattern.

My first try was with only two card so I used Zigbee Light Bulb Example to achieved this but now I have 8 cards to add in the chain. As Zigbee Light Bulb example use an automatical binding mechanism I can't use it to diferenciate cards and bind it in specific order.

In my current project I use zigbee network adresses provided by coordinator (I get them with sniffer). The problem I encounter is the unability of the card to interpret ZCL command twice. After startup the 'on' command is correctly processed but then, nothing... The program seem to never get interrupted by ZCL command again (I tried to put terminal print in zcl callback function).

I need to restart the card in order to get ZCL callback called again.

I know this approach, espacially if network adresses changes, isn't ideal so I'm looking for sulution to my problem or another method to achieve this.

I hope I gave as much information as needed (I'm still quit beginer with nrf products and it's my first prject of this kind ^^')

I joined my main.c to this message.

Thank you very much for your help!

/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

/** @file
 *
 * @brief Zigbee application template.
 */

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <dk_buttons_and_leds.h>

#include <zboss_api.h>
#include <zigbee/zigbee_error_handler.h>
#include <zigbee/zigbee_app_utils.h>
#include <zb_nrf_platform.h>
#include "zb_range_extender.h"
#include "zb_led_controller.h"
#include "zb_zcl_on_off.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 strip state */
#define STRIP_LED							DK_LED1

/* 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_DBG);

/* 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;
	zb_zcl_on_off_attrs_t on_off_attr;
	zb_bool_t led_status;
};

/* 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_ZCL_DECLARE_ON_OFF_ATTRIB_LIST(
	on_off_attr_list,
	&dev_ctx.on_off_attr.on_off);

ZB_DECLARE_LED_CONTROLLER_CLUSTER_LIST(
	app_template_clusters,
	basic_attr_list,
	identify_attr_list,
	on_off_attr_list);

ZB_DECLARE_LED_CONTROLLER_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 Function for reporting ON/OFF commands on board's led.
 *
 * @param[in]   on_off_cmd   Reference to Zigbee stack buffer
 *                      	 used to pass received data.
 */
void set_led_value(zb_bool_t on_off_cmd){
	if (on_off_cmd) {
		dk_set_led_on(STRIP_LED);
		dev_ctx.led_status = true;
		ZB_ZCL_SET_ATTRIBUTE(
			APP_TEMPLATE_ENDPOINT,
			ZB_ZCL_CLUSTER_ID_ON_OFF,
			ZB_ZCL_CLUSTER_SERVER_ROLE,
			ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
			true,
			ZB_FALSE);
	} else {
		//light_bulb_set_brightness(0U);
		dk_set_led_off(STRIP_LED);	
		dev_ctx.led_status = false;
		ZB_ZCL_SET_ATTRIBUTE(
			APP_TEMPLATE_ENDPOINT,
			ZB_ZCL_CLUSTER_ID_ON_OFF,
			ZB_ZCL_CLUSTER_SERVER_ROLE,
			ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
			false,
			ZB_FALSE);
	}
}

/**@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)
{
	LOG_DBG("ZCL callback processing");
	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);

	LOG_INF("%s id %hd", __func__, device_cb_param->device_cb_id);

	/* 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;

		if (cluster_id == ZB_ZCL_CLUSTER_ID_ON_OFF) {
			uint8_t value =
				device_cb_param->cb_param.set_attr_value_param
				.values.data8;

			LOG_INF("on/off attribute setting to %hd", value);
			if (attr_id == ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID) {
				set_led_value((zb_bool_t)value);
				LOG_INF("attr id on/off attribute setting to %hd", value);
			}
		} 
		else {
			/* Other clusters can be processed here */
			LOG_INF("Unhandled cluster attribute id: %d",
				cluster_id);
			device_cb_param->status = RET_NOT_IMPLEMENTED;
		}
		break;

	default:
		// if (zcl_scenes_cb(bufid) == ZB_FALSE) {
		// 	device_cb_param->status = RET_NOT_IMPLEMENTED;
		// }
		break;
	}

	LOG_INF("%s status: %hd", __func__, 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));

	/* 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);
	
	/* Register callback function for handling ZCL commands*/
	ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);

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

	// zb_uint16_t target_addr = 0xbc7e; //DK-3
	zb_uint16_t target_addr = 0xe24b; //DK-1
	// zb_uint16_t target_addr_list[] = {
	// 	0x138c,
	// 	0xd5fb,
	// 	0x8698,
	// 	0x9d1e,
	// 	0x1c96,
	// 	0x3084,
	// 	0xe0e7,
	// 	0xf785
	// };

	// set_led_value(true);

	while(1){
		if (dev_ctx.led_status == true){
			LOG_INF("LED was on with status %d", dev_ctx.led_status);
			k_sleep(K_SECONDS(2));
			zb_bufid_t bufid = zb_buf_get_out();
			if (bufid != 0){
				ZB_ZCL_ON_OFF_SEND_ON_REQ(
					bufid,
					target_addr,
					ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
					10,
					APP_TEMPLATE_ENDPOINT,
					ZB_AF_HA_PROFILE_ID,
					ZB_ZCL_DISABLE_DEFAULT_RESPONSE,
					NULL
				);
			}
			else{
				LOG_ERR("Failed to get output buffer");
			}
			
			set_led_value(false);
			LOG_INF("LED status set to %d", dev_ctx.led_status);
		}
		else{
			LOG_INF("Waiting %d", dev_ctx.led_status);
		};
		k_sleep(K_SECONDS(2));

	}
}

Parents
  • Hi, 

    I couldn't see anything obvious in the code. Could you provide a sniffer log? That might give some insight.

    I know this approach, espacially if network adresses changes, isn't ideal so I'm looking for sulution to my problem or another method to achieve this.

    And regarding this. The IEEE EUI64 address, that is, the long network address, will not change, so you should be safe using this to identify the devices. 

    If you have the long address you can get the network address with zb_zdo_nwk_addr_req().

    Regards,
    Amanda H.

  • Hi,

    Thank you for your answer.

    Joined to this message, you'll find a trace from wireshark. To complete the log:

    - Development kit n°1 as the adress 0xe24b

    Development kit n°2 as the adress 0xbc7e

    - DK 1 starts with its led "on"

    - At the end of the waiting time (Time = 24.29s), its led turns "off" and an "on" command is sent to DK 2

    - DK 2 turns its led "on"

    - At the end of the waiting time (Time = 28.29s), its led turns "off" and an "on" command is sent to DK 1

    - DK 1 turns its led "on"

    - At the end of the waiting time (Time = 32.35s), its led turns "off" and an "on" command is sent to DK 2

    The nothing happend anymore.

    Best regards

    Clément6366.log.pcapng

  • Hi, 

    The packets in the sniffer log are encrypted. Please share the network key or capture a new sniffer log where you start the sniffer before the coordinator starts the network. It would be best to have a new log showing the communication from the beginning, i.e. from when the devices join the network.

    -Amanda H.

Reply Children
No Data
Related