How to send readresponse on OnOff Output Cluster to Coordinator?

I am trying to send a OnOff output cluster information to Co-ordinator when a button is pressed in the nrf52840 DK.  The problem is the during the initial registration, the OnOff command is correctly sent, but after the button is pressed, I am unable to find the correct method to send the readresponse

Here is the complete program

#include <zephyr/types.h>
#include <zephyr.h>
#include <device.h>
#include <soc.h>
#include <drivers/pwm.h>
#include <logging/log.h>
#include <dk_buttons_and_leds.h>
#include <settings/settings.h>

#include <zboss_api.h>
#include <zboss_api_addons.h>
#include <zb_mem_config_med.h>
#include <zigbee/zigbee_app_utils.h>
#include <zigbee/zigbee_error_handler.h>
#include <zigbee/zigbee_zcl_scenes.h>
#include <zb_nrf_platform.h>

/* Switch endpoint */
#define HA_SWITCH_ENDPOINT      12

/* Version of the application software (1 byte). */
#define SWITCH_INIT_BASIC_APP_VERSION     01

/* Version of the implementation of the Zigbee stack (1 byte). */
#define SWITCH_INIT_BASIC_STACK_VERSION   10

/* Version of the hardware of the device (1 byte). */
#define SWITCH_INIT_BASIC_HW_VERSION      11

/* Manufacturer name (32 bytes). */
#define SWITCH_INIT_BASIC_MANUF_NAME      "DongleNordic"

/* Model number assigned by manufacturer (32-bytes long string). */
#define SWITCH_INIT_BASIC_MODEL_ID        "DongleSwitch"

/* First 8 bytes specify the date of manufacturer of the device
 * in ISO 8601 format (YYYYMMDD). The rest (8 bytes) are manufacturer specific.
 */
#define SWITCH_INIT_BASIC_DATE_CODE       "202201207777"

/* Type of power sources available for the device.
 * For possible values see section 3.2.2.2.8 of ZCL specification.
 */
#define SWITCH_INIT_BASIC_POWER_SOURCE    ZB_ZCL_BASIC_POWER_SOURCE_BATTERY

/* Describes the physical location of the device (16 bytes).
 * May be modified during commisioning process.
 */
#define SWITCH_INIT_BASIC_LOCATION_DESC   "Home"

/* Describes the type of physical environment.
 * For possible values see section 3.2.2.2.10 of ZCL specification.
 */
#define SWITCH_INIT_BASIC_PH_ENV          ZB_ZCL_BASIC_ENV_UNSPECIFIED

#define TOGGLE_SWITCH					DK_BTN3_MSK

/* Nordic PWM nodes don't have flags cells in their specifiers, so
 * this is just future-proofing.
 */
#define FLAGS_OR_ZERO(node) \
	COND_CODE_1(DT_PHA_HAS_CELL(node, pwms, flags), \
		    (DT_PWMS_FLAGS(node)), (0))


#ifndef ZB_ED_ROLE
#error Define ZB_END_DEVICE_ROLE to compile enddevice source code.
#endif

static void switch_send_on_off(zb_bufid_t bufid, zb_uint16_t on_off);

LOG_MODULE_REGISTER(app);

/** 
 * Placeholder for the bulb 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_scenes_attrs_t            scenes_attr;
	zb_zcl_groups_attrs_t            groups_attr;
 	zb_zcl_on_off_attrs_t            on_off_attr;
} on_off_device_ctx_t;
/* Zigbee device application context storage. */
static on_off_device_ctx_t dev_ctx;

ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST(
	identify_attr_list,
	&dev_ctx.identify_attr.identify_time);

ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST(
	groups_attr_list,
	&dev_ctx.groups_attr.name_support);

ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST(
	scenes_attr_list,
	&dev_ctx.scenes_attr.scene_count,
	&dev_ctx.scenes_attr.current_scene,
	&dev_ctx.scenes_attr.current_group,
	&dev_ctx.scenes_attr.scene_valid,
	&dev_ctx.scenes_attr.name_support);

ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT(
	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);

/* On/Off cluster attributes additions data */
ZB_ZCL_DECLARE_ON_OFF_ATTRIB_LIST(
	on_off_attr_list,
	&dev_ctx.on_off_attr.on_off);

ZB_HA_DECLARE_ON_OFF_OUTPUT_CLUSTER_LIST(on_off_output_clusters,
    on_off_attr_list, basic_attr_list, identify_attr_list, groups_attr_list,
    scenes_attr_list);

ZB_HA_DECLARE_ON_OFF_OUTPUT_EP(on_off_output_ep, HA_SWITCH_ENDPOINT, on_off_output_clusters);

ZB_HA_DECLARE_ON_OFF_OUTPUT_CTX(on_off_output_ctx, on_off_output_ep);

/**@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)
{
	zb_ret_t zb_err_code;
	zb_uint16_t cmd_id = ZB_ZCL_CMD_ON_OFF_ON_ID;
	zb_err_code = zb_buf_get_out_delayed_ext(switch_send_on_off, cmd_id, 0);
	ZB_ERROR_CHECK(zb_err_code);
}

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

/**@brief Function for sending ON/OFF requests to the network / co-ordinator.
 *
 * @param[in]   bufid    Non-zero reference to Zigbee stack buffer that will be
 *                       used to construct on/off request.
 * @param[in]   cmd_id   ZCL command id.
 */
static void switch_send_on_off(zb_bufid_t bufid, zb_uint16_t cmd_id)
{
	LOG_INF("Send ON/OFF command: %d", cmd_id);
	zb_uint16_t coordinator_address = 0x0000;
	dev_ctx.on_off_attr.on_off = (zb_bool_t)ZB_ZCL_ON_OFF_IS_ON;				

	ZB_ZCL_ON_OFF_SEND_REQ(bufid,
				coordinator_address,
				ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
				HA_SWITCH_ENDPOINT,
				HA_SWITCH_ENDPOINT,
				ZB_AF_HA_PROFILE_ID,
				ZB_ZCL_ENABLE_DEFAULT_RESPONSE,
				cmd_id,
				NULL);
}

/**@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_uint8_t endpoint_invoked;	
	zb_zcl_device_callback_param_t  *device_cb_param =
		ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);
	
	endpoint_invoked = device_cb_param->endpoint;

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

	LOG_INF("EndPoint Called is %d ", endpoint_invoked);

	/* Set default response value. */
	device_cb_param->status = RET_OK;
	LOG_INF("ZB Callback started %c", bufid);	

	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)
{
	/* 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 error(void)
{
	while (true) {
		/* Spin forever */
		k_sleep(K_MSEC(1000));
	}
}

void main(void)
{
	int err;

	LOG_INF("Starting Switch example");

	/* Initialize */
	configure_gpio();
	err = settings_subsys_init();
	if (err) {
		LOG_ERR("settings initialization failed");
	}

	LOG_INF("Registering Device");
	/* Register callback for handling ZCL commands. */
	ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);

	LOG_INF("Setting Switch Context");
	/* Register dimmer switch device context (all the endpoints). */
	ZB_AF_REGISTER_DEVICE_CTX(&on_off_output_ctx);

	/* Initialize ZCL scene table */
	// zcl_scenes_init();

	/* Settings should be loaded after zcl_scenes_init */
	err = settings_load();
	if (err) {
		LOG_ERR("settings loading failed");
	}

	/* Start Zigbee default thread */
	zigbee_enable();

	LOG_INF("Switch example started");

	while (1) {
		// k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
	}
}

1. When the image is flashed in DK, it properly registers to the Zigbee2MQTT and send the correct response  - please see the the Part 1 in the snapshot below.  The Cluster is 'genOnOff, type 'readResponse' and json data {'OnOff':0}

2. After button is clicked - DK BTN3 in this case, the type changes to 'commandOn' with json data being empty - please see the Part 2 in the snapshot below.  It is obviously due to the macro ZB_ZCL_ON_OFF_SEND_REQ. But I would need the cluster and the payload sent just like # 1.  Which macro should I use to retain the type 'readResponse' for cluster 'genOnOff' and fill in the json data? This is for the OnOff Output ZCL

  • The DK should start reporting the attribute automatically if the Configure Reporting command is successful and the attribute is reportable.

    Thanks. While the manual push (via Z2M UI) of the configure reporting from Z2M works (I still have not be able to do it automatically when DK joins the Z2M), I would still want to make it work from the DK.  I used the methods you mentioned in earlier post, but the reporting still is not working. Can you please let me know from where I should call this?

    Can you please indicate where should I call this? in the void zboss_signal_handler(zb_bufid_t bufid) for the case ZB_BDB_SIGNAL_STEERING: ?  Since I could not find anything like case like ZB_BDB_JOIN.

    my nrf dongle is broken, I think during one of the experimentation in another thread, so will have to check that on weekend to recover. So sniffer data will have to wait a bit longer

  • Here is the status. 

    1. I was not able to get the reporting configure from server to client (Dongle/DK to Z2M).

    2. I was able to get the reporting automatically configured  from Z2M to Dongle (without clicking anything on the Z2M). Hence there was no need to add any reporting configuration code in the dongle / DK

    So if somebody is working with Z2M and needs to have the reporting configured from client to server, they could utilize the below. The key point here is to use both reporting.bind  & endpoint.configureReporting calls in the .js file.  Not sure why the bind call is required, but without the bind the configure reporting call is not sent to Dongle/ DK from Z2M.   The bind is technically doing nothing as the endpoint specified can be open

    const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
    const tz = require('zigbee-herdsman-converters/converters/toZigbee');
    const exposes = require('zigbee-herdsman-converters/lib/exposes');
    const reporting = require('zigbee-herdsman-converters/lib/reporting');
    const extend = require('zigbee-herdsman-converters/lib/extend');
    const e = exposes.presets;
    const ea = exposes.access;
    
    const definition = {
        zigbeeModel: ['DongleSwitch'], // The model ID from: Device with modelID 'lumi.sens' is not supported.
        model: '1.0', // Vendor model number, look on the device for a model number
        vendor: 'DongleNordic', // Vendor of the device (only used for documentation and startup logging)
        description: 'dongle_52840', // Description of the device, copy from vendor site. (only used for documentation and startup logging)
        fromZigbee: [fz.on_off],
        toZigbee: [tz.on_off],
        exposes: [e.switch()],
        configure: async (device, coordinatorEndpoint, logger) => {
           const endpoint = device.getEndpoint(12);
           await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
           const payload = [{
               attribute: 'onOff',
               minimumReportInterval: 0,
               maximumReportInterval: 0,
                reportableChange: 0,
               }];
           await endpoint.configureReporting('genOnOff', payload);
       },
    };

    I will spend sometime later to sniff the packets from Dongle / DK to Z2M when the server side reporting is configured and find out why this is not working. This will be required, since server (Dongle / DK) as end device are expected to contain all the functionality than depending upon coordinator / network to provide that functionality

  • Hi

    I am glad to hear that you figured this out, and thank you for sharing your solution!

    Manju_rn said:
    Not sure why the bind call is required, but without the bind the configure reporting call is not sent to Dongle/ DK from Z2M.

    When you configure reporting from client to server you need to first create a binding on the cluster. From the ZCL spec when the direction field of the configure reporting command indicates that it is sent from client to server:

    The receiver of the Configure Reporting command SHALL Configure Reporting to send to each destination as resolved by the bindings for the cluster hosting the attributes to be reported.

    So in order for the server to configure reporting on a cluster, you must first have sent a bind request to create a binding on that cluster.

    Manju_rn said:
    I will spend sometime later to sniff the packets from Dongle / DK to Z2M when the server side reporting is configured and find out why this is not working. This will be required, since server (Dongle / DK) as end device are expected to contain all the functionality than depending upon coordinator / network to provide that functionality

    Please upload the sniffer logs when you do so, and I can help you look. If you are able to share your code I can also test it on my side and see if I can figure out why this is not working.

    Best regards,

    Marte

Related