Hi, this is my first post here, so please forgive if category/description is not complete.
I'm trying to do a quick proof of concept using ZB ZCL Tunneling. ZED is a client side that opens a tunnel and sends some data and ZC is a server side receiving that data and sends something back to the client. Just for testing I'm doing it with ZB_AF_HA_PROFILE_ID & ZB_ZCL_CLUSTER_ID_TUNNELING.
On ZED I'm trying to request tunnel in the following way:
static void tunneling_request_tunnel(zb_bufid_t bufid) { zb_ret_t zb_err_code; zb_uint16_t short_addr = 0x0000; if (bufid) { NRF_LOG_INFO("%s", __FUNCTION__); ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL(bufid, \ short_addr, \ ZB_APS_ADDR_MODE_16_ENDP_PRESENT, \ TUNNEL_CLI_ENDPOINT, \ TUNNEL_CLI_ENDPOINT, \ ZB_AF_HA_PROFILE_ID, \ ZB_ZCL_DISABLE_DEFAULT_RESPONSE, \ NULL, \ 200, \ 0xDEAD, \ ZB_FALSE, \ 16); } else { zb_err_code = zb_buf_get_out_delayed(tunneling_request_tunnel); ZB_ERROR_CHECK(zb_err_code); } }
On ZC I have a tunnel endpoint handler:
zb_uint8_t tunnel_ep_handler(zb_bufid_t bufid) { static zb_bool_t tunnel_opened = ZB_FALSE; zb_zcl_parsed_hdr_t * header = ZB_BUF_GET_PARAM(bufid, zb_zcl_parsed_hdr_t); if(ZB_ZCL_CLUSTER_ID_TUNNELING != header->cluster_id) { return ZB_FALSE; } if(ZB_AF_HA_PROFILE_ID != header->profile_id) { return ZB_FALSE; } switch(header->cmd_id) { case ZB_ZCL_TUNNELING_CLI_CMD_REQUEST_TUNNEL: { zb_zcl_tunneling_request_tunnel_t tunnel_req; zb_zcl_parse_status_t status; zb_bufid_t buf_resp = zb_buf_get_out(); ZB_ZCL_TUNNELING_GET_REQUEST_TUNNEL(&tunnel_req, bufid, status); (void)status; if(ZB_TRUE == tunnel_opened) { ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL_RESPONSE(buf_resp, header->addr_data.common_data.source.u, header->addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT ? ZB_APS_ADDR_MODE_16_ENDP_PRESENT : ZB_APS_ADDR_MODE_64_ENDP_PRESENT, TUNNEL_SRV_ENDPOINT, TUNNEL_SRV_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, ZB_ZCL_GET_SEQ_NUM(), NULL, 0x0, ZB_ZCL_TUNNELING_STATUS_NO_MORE_IDS, tunnel_req.max_incoming_transfer_size ); } else { tunnel_opened = ZB_TRUE; ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL_RESPONSE(buf_resp, header->addr_data.common_data.source.u, header->addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT ? ZB_APS_ADDR_MODE_16_ENDP_PRESENT : ZB_APS_ADDR_MODE_64_ENDP_PRESENT, TUNNEL_SRV_ENDPOINT, TUNNEL_SRV_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, ZB_ZCL_GET_SEQ_NUM(), NULL, tunnel_id, ZB_ZCL_TUNNELING_STATUS_SUCCESS, tunnel_req.max_incoming_transfer_size ); } return ZB_TRUE; } case ZB_ZCL_TUNNELING_CLI_CMD_TRANSFER_DATA: { zb_zcl_tunneling_transfer_data_payload_t data_payload; zb_zcl_parse_status_t status; zb_bufid_t buf_data = zb_buf_get_out(); ZB_ZCL_TUNNELING_GET_TRANSFER_DATA(&data_payload, bufid, status); NRF_LOG_INFO("Data received."); tunnel_out_buf[0] = 'O'; tunnel_out_buf[1] = 'K'; zb_zcl_tunneling_server_send_transfer_data(buf_data, TUNNEL_SRV_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, NULL, tunnel_id, 2, tunnel_out_buf); (void)status; return ZB_TRUE; } case ZB_ZCL_TUNNELING_CLI_CMD_CLOSE_TUNNEL: { zb_zcl_tunneling_close_tunnel_t tunnel_close; zb_zcl_parse_status_t status; ZB_ZCL_TUNNELING_GET_CLOSE_TUNNEL(&tunnel_close, bufid, status); (void)status; if(ZB_FALSE == tunnel_opened) { // TBD } else { tunnel_opened = ZB_FALSE; } return ZB_TRUE; } default: break; } return ZB_FALSE; }
On ZED I have a tunnel endpoint handler as well:
zb_uint8_t tunnel_ep_handler(zb_bufid_t bufid) { zb_zcl_parsed_hdr_t * header = ZB_BUF_GET_PARAM(bufid, zb_zcl_parsed_hdr_t); (void)tunnel_opened; if(ZB_ZCL_CLUSTER_ID_TUNNELING != header->cluster_id) { return ZB_FALSE; } if(ZB_AF_HA_PROFILE_ID != header->profile_id) { return ZB_FALSE; } NRF_LOG_INFO("Tunnel Handler, cmd_id: %x", header->cmd_id); switch(header->cmd_id) { case ZB_ZCL_TUNNELING_SRV_CMD_REQUEST_TUNNEL_RESPONSE: { zb_zcl_tunneling_request_tunnel_response_t tunnel_req_resp; zb_zcl_parse_status_t status; ZB_ZCL_TUNNELING_GET_REQUEST_TUNNEL_RESPONSE(&tunnel_req_resp, bufid, status); (void)status; NRF_LOG_INFO("Tunnel status: %x", tunnel_req_resp.tunnel_status); if(ZB_ZCL_TUNNELING_STATUS_SUCCESS != tunnel_req_resp.tunnel_status) { return ZB_FALSE; } tunnel_id = tunnel_req_resp.tunnel_id; tunnel_opened = ZB_TRUE; tunnel_out_buf[0] = 'O'; tunnel_out_buf[1] = 'K'; zb_bufid_t buf_data = zb_buf_get_out(); zb_zcl_tunneling_client_send_transfer_data(buf_data, TUNNEL_CLI_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, NULL, tunnel_id, 2, tunnel_out_buf); return ZB_TRUE; } case ZB_ZCL_TUNNELING_SRV_CMD_TUNNEL_CLOSURE_NOTIFICATION: { tunnel_opened = ZB_FALSE; return ZB_TRUE; } default: break; } return ZB_FALSE; }
On Wireshark I can see proper flow - ZED requests tunnel and ZC sends request tunnel response with status success. So I expect then ZED to call zb_zcl_tunneling_client_send_transfer_data and send data, but zb_zcl_tunneling_client_send_transfer_data returns with code -1.
By accident I found a workaround - ZC must first send request tunnel response with error status followed by the second one reponse with success - then I'm getting transfer data from client to server, but still server to client transfer data request (zb_zcl_tunneling_server_send_transfer_data) returns -1.
Could you please give me a hint what I'm doing wrong?