Hello, I' trying to use the macro here below, but there are no sample in SDK.
Have you any example for using it?
Many thanks
Abele
Hello, I' trying to use the macro here below, but there are no sample in SDK.
Have you any example for using it?
Many thanks
Abele
Hi,
Take a look at the source code of the Zigbee CLI example and see inside zigbee_cli_cmd_attr.c function readattr_send().
I can also share with you a code snippet from an on-going tutorial project I am working on where I want to read the on/off state from the on/off cluster from a device with index idx from a list of devices stored in a custom light switch entry structure:
/*Read device On/Off state from the On/Off cluster. */ static void read_device_state(zb_uint8_t param, zb_uint16_t idx) { zb_uint8_t * p_cmd_buf; zb_buf_t * p_buf = ZB_BUF_FROM_REF(param); light_switch_entry_t * p_entry = get_by_idx(idx); if (p_entry == NULL) { NRF_LOG_WARNING("Unable to read device state. Device with idx: %d not found.", idx); ZB_FREE_BUF_BY_REF(param); return; } NRF_LOG_INFO("Resolve device state for device with idx: %d", idx); ZB_ZCL_GENERAL_INIT_READ_ATTR_REQ(p_buf, p_cmd_buf, ZB_ZCL_ENABLE_DEFAULT_RESPONSE); ZB_ZCL_GENERAL_ADD_ID_READ_ATTR_REQ(p_cmd_buf, ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID); ZB_ZCL_GENERAL_SEND_READ_ATTR_REQ(p_buf, p_cmd_buf, p_entry->short_address, ZB_APS_ADDR_MODE_16_ENDP_PRESENT, p_entry->endpoint, LIGHT_COORDINATOR_EP, //Sending endpoint ZB_AF_HA_PROFILE_ID, ZB_ZCL_CLUSTER_ID_ON_OFF, read_device_state_cb); // For this type of requests the response will not arrive inside the callback. Please take a look at the endpoint handler. You can use the callback for further configuration (call another function) but if not you can just use it to free the buffer, see frame_acked_cb in CLI example. }
I hope this was of help, let me know if you had more questions.
Best regards,
Marjeris
Hi Marjeris,
many thanks for your answer.
Waiting for, I'm also seen CLI example. then I added this code to my application (function name is different but the code is like).
Using PC10059 dongle and Wireshark as Zigbee communication sniffer, I see the EndNode answer that contains expected data, but I'm not be able to get the received data to store it in my custom database. (the callback as commented on your code is not the right way)
Have you any suggest or any example how to get the received data??
/**@brief Actually construct the Read Attribute frame and send it.
*
* @param param ZBOSS buffer to fill.
* @param cb_param Row of the read attribute table to refer to.
*/
static zb_void_t readattr_send(zb_uint8_t param, zb_uint16_t cb_param)
{
zb_ret_t zb_err_code;
zb_buf_t * p_buf = ZB_BUF_FROM_REF(param);
zb_uint8_t row = cb_param;
zb_uint8_t * p_cmd_buf;
attr_query_t * p_row = &(m_attr_table[row]);
p_row->seq_num = ZCL_CTX().seq_number;
ZB_ZCL_GENERAL_INIT_READ_ATTR_REQ_A(p_buf, p_cmd_buf, p_row->direction, ZB_ZCL_ENABLE_DEFAULT_RESPONSE);
ZB_ZCL_GENERAL_ADD_ID_READ_ATTR_REQ(p_cmd_buf, p_row->attr_id);
ZB_ZCL_GENERAL_SEND_READ_ATTR_REQ(p_buf, p_cmd_buf,
p_row->remote_node,
p_row->remote_addr_mode,
p_row->remote_ep,
LIGHT_SWITCH_ENDPOINT,
p_row->profile_id,
p_row->cluster_id,
frame_acked_cb);
zb_err_code = ZB_SCHEDULE_ALARM(invalidate_row_cb, row,
ATTRIBUTE_ROW_TIMEOUT_S * ZB_TIME_ONE_SECOND);
ZB_ERROR_CHECK(zb_err_code);
}
Hi Abele,
Yes, you need to register a endpoint handler with ZB_AF_SET_ENDPOINT_HANDLER() to intercept the ZCL packets, see more information in here.
Inside the handler you should check the command direction, cluster ID and command ID, and call the dedicated API for getting the contents of the read attribute response frame ZB_ZCL_GENERAL_GET_NEXT_READ_ATTR_RES, then free the buffer and return ZB_TRUE, otherwise return ZB_FALSE to indicate to the stack to perform the default command processing.
See "Implementing algorithm for overriding the handling of ZCL commands" in the link I provided above. I attach an example code from the same project I mentioned above:
static zb_uint8_t print_read_attr_on_off(zb_buf_t * p_zcl_cmd_buf, light_switch_entry_t * p_entry) { zb_zcl_read_attr_res_t * p_attr_resp; /* Get the contents of Read Attribute Response frame */ ZB_ZCL_GENERAL_GET_NEXT_READ_ATTR_RES(p_zcl_cmd_buf, p_attr_resp); if((p_attr_resp->status == ZB_ZCL_STATUS_SUCCESS) && (p_attr_resp->attr_id == ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID)) { if(p_attr_resp->attr_value[0] == 1) { p_entry->on_off_state = true; } else { p_entry->on_off_state = false; } NRF_LOG_INFO("Received device on/off state, idx %d, on/off state: %d", p_entry->idx, p_entry->on_off_state); zb_free_buf(p_zcl_cmd_buf); return ZB_TRUE; } return ZB_FALSE; } static zb_uint8_t on_off_switch_ep_handler(zb_uint8_t param) { zb_buf_t * zcl_cmd_buf = (zb_buf_t *)ZB_BUF_FROM_REF(param); zb_zcl_parsed_hdr_t * cmd_info = ZB_GET_BUF_PARAM(zcl_cmd_buf, zb_zcl_parsed_hdr_t); zb_uint16_t short_addr; light_switch_entry_t * p_entry; // Address resolution. if (cmd_info->addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT) { short_addr = cmd_info->addr_data.common_data.source.u.short_addr; } else if (cmd_info->addr_data.common_data.source.addr_type== ZB_ZCL_ADDR_TYPE_IEEE) { short_addr = zb_address_short_by_ieee(cmd_info->addr_data.common_data.source.u.ieee_addr); } else { NRF_LOG_ERROR("Endpoint handler: unknown destination address type: %d", cmd_info->addr_data.common_data.source.addr_type); return ZB_FALSE; } // On/Off cluster read attribute response. if ((cmd_info->cmd_direction == ZB_ZCL_FRAME_DIRECTION_TO_CLI) && \ (cmd_info->cmd_id == ZB_ZCL_CMD_READ_ATTRIB_RESP) && \ (cmd_info->cluster_id == ZB_ZCL_CLUSTER_ID_ON_OFF)) { //Process the incoming command response, in this case I store the on/off state of the switch in the correct light switch entry with the correct address and source endpoint of the response message p_entry = get_by_short_address_endpoint(short_addr, cmd_info->addr_data.common_data.src_endpoint); if (p_entry == NULL) { NRF_LOG_INFO("ON/OFF attr resp: entry not found endpoint %d, dst_addr %d", cmd_info->addr_data.common_data.src_endpoint, short_addr); return ZB_FALSE; } return print_read_attr_on_off(zcl_cmd_buf, p_entry); } return ZB_FALSE; }
In main function you will also need to register the device context and set the endpoint handler receive hook, for example after setting the device role:
/* Set channels on which the coordinator will try to create a new network. */ zb_set_network_coordinator_role(IEEE_CHANNEL_MASK); zb_set_max_children(MAX_CHILDREN); /* Register dimmer switch device context (endpoints). */ ZB_AF_REGISTER_DEVICE_CTX(&on_off_switch_ctx); /* Set the endpoint receive hook */ ZB_AF_SET_ENDPOINT_HANDLER(LIGHT_COORDINATOR_EP, on_off_switch_ep_handler);
BR,
Marjeris
Hi Abele,
Yes, you need to register a endpoint handler with ZB_AF_SET_ENDPOINT_HANDLER() to intercept the ZCL packets, see more information in here.
Inside the handler you should check the command direction, cluster ID and command ID, and call the dedicated API for getting the contents of the read attribute response frame ZB_ZCL_GENERAL_GET_NEXT_READ_ATTR_RES, then free the buffer and return ZB_TRUE, otherwise return ZB_FALSE to indicate to the stack to perform the default command processing.
See "Implementing algorithm for overriding the handling of ZCL commands" in the link I provided above. I attach an example code from the same project I mentioned above:
static zb_uint8_t print_read_attr_on_off(zb_buf_t * p_zcl_cmd_buf, light_switch_entry_t * p_entry) { zb_zcl_read_attr_res_t * p_attr_resp; /* Get the contents of Read Attribute Response frame */ ZB_ZCL_GENERAL_GET_NEXT_READ_ATTR_RES(p_zcl_cmd_buf, p_attr_resp); if((p_attr_resp->status == ZB_ZCL_STATUS_SUCCESS) && (p_attr_resp->attr_id == ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID)) { if(p_attr_resp->attr_value[0] == 1) { p_entry->on_off_state = true; } else { p_entry->on_off_state = false; } NRF_LOG_INFO("Received device on/off state, idx %d, on/off state: %d", p_entry->idx, p_entry->on_off_state); zb_free_buf(p_zcl_cmd_buf); return ZB_TRUE; } return ZB_FALSE; } static zb_uint8_t on_off_switch_ep_handler(zb_uint8_t param) { zb_buf_t * zcl_cmd_buf = (zb_buf_t *)ZB_BUF_FROM_REF(param); zb_zcl_parsed_hdr_t * cmd_info = ZB_GET_BUF_PARAM(zcl_cmd_buf, zb_zcl_parsed_hdr_t); zb_uint16_t short_addr; light_switch_entry_t * p_entry; // Address resolution. if (cmd_info->addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT) { short_addr = cmd_info->addr_data.common_data.source.u.short_addr; } else if (cmd_info->addr_data.common_data.source.addr_type== ZB_ZCL_ADDR_TYPE_IEEE) { short_addr = zb_address_short_by_ieee(cmd_info->addr_data.common_data.source.u.ieee_addr); } else { NRF_LOG_ERROR("Endpoint handler: unknown destination address type: %d", cmd_info->addr_data.common_data.source.addr_type); return ZB_FALSE; } // On/Off cluster read attribute response. if ((cmd_info->cmd_direction == ZB_ZCL_FRAME_DIRECTION_TO_CLI) && \ (cmd_info->cmd_id == ZB_ZCL_CMD_READ_ATTRIB_RESP) && \ (cmd_info->cluster_id == ZB_ZCL_CLUSTER_ID_ON_OFF)) { //Process the incoming command response, in this case I store the on/off state of the switch in the correct light switch entry with the correct address and source endpoint of the response message p_entry = get_by_short_address_endpoint(short_addr, cmd_info->addr_data.common_data.src_endpoint); if (p_entry == NULL) { NRF_LOG_INFO("ON/OFF attr resp: entry not found endpoint %d, dst_addr %d", cmd_info->addr_data.common_data.src_endpoint, short_addr); return ZB_FALSE; } return print_read_attr_on_off(zcl_cmd_buf, p_entry); } return ZB_FALSE; }
In main function you will also need to register the device context and set the endpoint handler receive hook, for example after setting the device role:
/* Set channels on which the coordinator will try to create a new network. */ zb_set_network_coordinator_role(IEEE_CHANNEL_MASK); zb_set_max_children(MAX_CHILDREN); /* Register dimmer switch device context (endpoints). */ ZB_AF_REGISTER_DEVICE_CTX(&on_off_switch_ctx); /* Set the endpoint receive hook */ ZB_AF_SET_ENDPOINT_HANDLER(LIGHT_COORDINATOR_EP, on_off_switch_ep_handler);
BR,
Marjeris
Hi Marjeris
I have seen the code you suggest; I think that it is intended for End-Node devices.
I'm developing a Zigbee Coordinator + BLE and I need populate the GATT database with info fro all device commissioned and connected to network
I use the readattr_send function I got from CLI example, that I have adapted to insert on my code and with radio sniffer I see the request from Coordinator to Endnode, the answer from Endnode to Coordinator and all related ACK. My purpose is to get data received from endnode.
How can I reach my goal? How can I get the read_attr data on Coordinator side?
Hi Abele,
The code is from a coordinator node. I have a table with entries for each light switch connected to the coordinator and I update this table with the current on/off state of the switch/lightbulb by reading their on/off state attribute. Have you tried to implement an endpoint handler to intercept the answer from the endnode to the coordinator?
Hi Marjeris,
I apologize for the delay, but I was busy with other works. Now I'm on this project, and I tried to add the functions you suggest, but I have some errors and I'm not be able to find the right definitions, for example, for the light_switch_entry_t typedef and LIGHT_COORDINATOR_EP macro.
Where I can find these? What the sample code you refer to?
Many thanks for your help!
Abele
Hi Abele,
The code snippet I shared was just so you could see an example on how the ZB_ZCL_GENERAL_SEND_READ_ATTR_REQ macro is implemented and how to implement an endpoint handler to intercept the response, I don't recommend copying it directly, as some functions and structs in that snippet are defined by myself, sorry for the confusion about that. For example light_switch_entry_t is just a selfmade structure I defined to help me store the on/off state of the devices in the network.
And for example should change the "LIGHT_COORDINATOR_EP" for the name you have defined for your sending endpoint (from which endpoint are you sending the send read attribute request?).
I think it would be easier for me to help you if you could share your code (main.c) here.
BR,
Marjeris
Hi Marjeris,
many thanks for your help, now I'm be able to intercept the ZCL attr request response.
In my code I forgot the ZB_AF_REGISTER_DEVICE_CTX() initialization, then my macro call ZB_AF_SET_ENDPOINT_HANDLER(ZIGBEE_ZC_MAIN_ENDPOINT, callback_ep_handler) had no effects.
Now, when i send a read_attr request, the callback_ep_handler() function is invoked.
Two further questions:
- the callback_ep_handler() return the "zb_uint8_t param" value set to 0x04, it's right? What's the meaning of this parameter?
- I need using Groupcast command: for example, I have set cluster 4 inside two light bulb with the command "add group" (both with same group code, of course). How can I send the "groupcast" on/off command? There is an example how to make this?
Kind regards
Abele