This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Thermostat cluster enable issue

Hi All,

I am developing product using nRF52840 and SDK nRF5_SDK_for_Thread_and_Zigbee_v3.0.0_d310e71.

This product is basically, using the Thermostat cluster and Zigbee protocol to communicate with the Zigbee gateway.

So, I referred light_bulb example and followed steps to enable "Thermostat cluster and attributes".

I have included the respective files also but I am unable to execute the below callback function case statement,

static zb_void_t zcl_device_cb(zb_uint8_t param)
{
    zb_uint8_t                       cluster_id;
    zb_uint8_t                       attr_id;
    zb_buf_t                       * p_buffer = ZB_BUF_FROM_REF(param);
    zb_zcl_device_callback_param_t * p_device_cb_param =
                     ZB_GET_BUF_PARAM(p_buffer, zb_zcl_device_callback_param_t);

    NRF_LOG_INFO("zcl_device_cb id %hd", p_device_cb_param->device_cb_id);

    /* Set default response value. */
    p_device_cb_param->status = RET_OK;

    switch (p_device_cb_param->device_cb_id)
    {
        case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
            cluster_id = p_device_cb_param->cb_param.set_attr_value_param.cluster_id;
            attr_id    = p_device_cb_param->cb_param.set_attr_value_param.attr_id;

            if (cluster_id == ZB_ZCL_CLUSTER_ID_THERMOSTAT)
            {
                uint16_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data16;

                NRF_LOG_INFO("thermostat local temperature: %d", value);
                if (attr_id == ZB_ZCL_ATTR_THERMOSTAT_LOCAL_TEMPERATURE_ID)
                {
                    local_temperature_value(value);
                }
            }
            else
            {
                /* Other clusters can be processed here */
                NRF_LOG_INFO("Unhandled cluster attribute id: %d", cluster_id);
            }
        break;

        default:
            p_device_cb_param->status = RET_ERROR;
            break;
    }

    NRF_LOG_INFO("zcl_device_cb status: %hd", p_device_cb_param->status);
}

Also, I am attaching code for reference please let me know what else I forgot to enable the functions. It is urgent for me to solve this issue.

Radiator_nRF_v0.9.0.zip

And, with these attributes, for some of the commands, I need to implement custom attributes so if you share how to add custom attributes to this cluster.

Thanks in advance.

Regards,

Rohit R

  • Hi Marte,

    Any update on my queries?

    Also, I would like to know to send read request from EndPoint device to Coordinator device (nRF to Gateway).

    For my project, time and custom cluster I required send read request function. I have followed this explanation which you have shared previously. 

    As for ZB_ZCL_GENRAL_SEND_READ_ATTR_REQ, the different parameters are:

    • zcl_cmd_buf - buffer to place data to
    • cmd_ptr - pointer to the memory area after the command data end
    • addr - address to send packet to
    • dst_addr_mode - addressing mode
    • dst_ep - destination endpoint
    • ep - sending endpoint
    • ZB_AF_HA_PROFILE_ID - profile identifier
    • ZB_ZCL_CLUSTER_ID_THERMOSTAT - cluster identifier
    • cb (here NULL) - callback for getting command send status

    But few points are not clear to me,

    addr - how to get coordinator address in endpoint

    dst_addr_mode - in which file I get address mode

    dst-ep - is this coordinator end point ? if yes how to read this ?

    ep- this means my endpoint device id - (example THERMOSTAT_EP)

    cb - it should be NULL or we can give function to read data?

    And in the link it not mentioned to enable these function which file to be included? I had searched in SDK but it says no matching function?

    Hope, I get quick reply on this.

    Thanks and Regards,

    Rohit R.

  • Hi Rohit,

    I am sorry for the delayed answer. I have been looking at and testing the latest project you attached, v0.9.3, and I am still working on it. I have not figured out a solution yet, but I will answer your questions and update you on what I have found so far.

    I am still testing with the CLI example, since that is the best alternative I have to the program you are using. The way I tested was by having a coordinator with the CLI example sending attribute requests to the radiator. I saw some of the same behavior that you are seeing. When sending attribute read request for the time status attribute in the Time cluster, or the battery voltage attribute in the Power Configuration cluster, the radiator sent a successful read attributes response, and I got the attribute value. When I tried to read battery percentage remaining, I got unsupported attribute. I also used a sniffer to look at the network traffic to confirm this. You can see the two read attribute response packets below, one for battery voltage, and one for battery percentage remaining.

    From what I can see, the attribute is implemented correctly, so I cannot see any reason why attributes from the extended attribute list are not working, while attributes from the regular list are. I will ask internally to see if anyone knows why this might be.

    Rohit Rajapure said:
    addr - how to get coordinator address in endpoint

     If you have the short address of the coordinator, you can use the function zb_zdo_ieee_addr_req to send a IEEE_addr_req primitive to get the IEEE, or long, address of the coordinator, which is what you should use as addr. Since it is a coordinator, the short address should be 0x0000.

    Rohit Rajapure said:
    dst_addr_mode - in which file I get address mode

     The address modes are found here. You should use ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT or ZB_APS_ADDR_MODE_16_ENDP_PRESENT, depending on whether the destination endpoint is present or not.

    Rohit Rajapure said:
    dst-ep - is this coordinator end point ? if yes how to read this ?

     Yes, this is the endpoint of the node you are sending the command to, so in this case it will be the coordinator. If you have the coordinators short address, then you can get the endpoint by sending an active endpoint request using zb_zdo_active_ep_req.

    Rohit Rajapure said:
    ep- this means my endpoint device id - (example THERMOSTAT_EP)

     Correct. This is the endpoint of the source, the one sending the message, which in this case is the thermostat endpoint you have set in your code.

    Rohit Rajapure said:
    cb - it should be NULL or we can give function to read data?

     It can be NULL or a pointer to a callback function for getting command send status.

    The definition of ZB_ZCL_GENERAL_SEND_READ_ATTR_REQ can be found in the file external/zboss/include/zcl/zb_zcl_commands.h.

    Best regards,

    Marte

  • Hi Marte,

    Thank you so much for the feedback.

    - Yes, even the same issue I am facing for thermostat ids. I have initialized local_temperature Id first but the same issue. When I tried to access using GUI it disappears like power percentage.

    - So, I again crossed checked your file (the one which you have shared in the previous post). But it looks the same. Still the same issue.

    - Power cluster and thermostat cluster not working as expected to be.

    And, thank you for sending the read request details. I will create functions and check if I get any reply from the coordinator.

    Hello Update,

    I tried to read the request,

    - Can you please check is correct,

    static void active_ep_callback(zb_uint8_t param)
    {
      zb_buf_t *buf = ZB_BUF_FROM_REF(param);
    
      zb_uint8_t *zdp_cmd = ZB_BUF_BEGIN(buf);
    
      zb_zdo_ep_resp_t *resp = (zb_zdo_ep_resp_t*)zdp_cmd;
    
    //  zb_uint8_t *ep_list = zdp_cmd + sizeof(zb_zdo_ep_resp_t);
    //
    //  if (resp->status != ZB_ZDP_STATUS_SUCCESS || resp->nwk_addr != 0x0)
    //  {
    //    TRACE_MSG(TRACE_APS1, "Error incorrect status/addr", (FMT__0));
    //    g_error++;
    //  }
    //  
    //  if (resp->ep_count != 1 || *ep_list != 1)
    //  {
    //    TRACE_MSG(TRACE_APS3, "Error incorrect ep count or ep value", (FMT__0));
    //    g_error++;
    //  }
     // send_match_desc_req(buf);
    }
    
    //static void send_active_ep_req(zb_buf_t *buf)
    //{
    //  zb_zdo_active_ep_req_t *req;
    //  ZB_BUF_INITIAL_ALLOC(buf, sizeof(zb_zdo_active_ep_req_t), req);
    //  req->nwk_addr = 0; //coord addr
    //  zb_zdo_active_ep_req(ZB_REF_FROM_BUF(buf), active_ep_callback);
    //}
    
    ////////////////////////////////////////////////////////////////////////////////
    ///
    /// \brief Requests the RTC from the coordinator
    ///
    /// \param RTC is sent in seconds format. Seconds from the year 2000
    ///
    /// \return void (no return value)
    ///
    /// \par example
    /// Call e.g. RTC_vRequestTimeFromCoordiantor() \n
    ///
    ////////////////////////////////////////////////////////////////////////////////
    void vRTC_vRequestTimeFromCoordiantor(void) 
    {
    
        zb_buf_t * p_buf;
    
        zb_uint8_t *cmd_ptr = NULL;
    
        ui16Addr = 0x0000;
    
        ui8Dst_addr = zb_zdo_active_ep_req(ZB_REF_FROM_BUF(p_buf), active_ep_callback);
    
    //    send_active_ep_req(p_buf);
    
        ZB_ZCL_GENERAL_INIT_READ_ATTR_REQ(p_buf, cmd_ptr, ZB_ZCL_ATTR_TIME_TIME_ID);
    
        ZB_ZCL_GENERAL_ADD_ID_READ_ATTR_REQ(cmd_ptr, ZB_ZCL_ATTR_TIME_TIME_ID);
    
        ZB_ZCL_GENERAL_SEND_READ_ATTR_REQ(p_buf, 
                                          cmd_ptr, 
                                          ui16Addr, 
                                          ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
                                          ui8Dst_addr,
                                          THERMOSTAT_SENSOR_ENDPOINT, 
                                          ZB_AF_HA_PROFILE_ID,
                                          ZB_ZCL_CLUSTER_ID_TIME, 
                                          NULL);
    }

    - I didn't get the ieee_request so I kept 0x0000 there and from the address field, I wrote function as i understood but I didn't get any response.

    Can you please help me with what I did wrong?

    Or do i need to create client also for this time cluster or is it server only? Because. I am requesting this from End point device to Gateway (nRF to Gateway).

    Please guide me.

    Thanks and Regards

    Rohit R

  • Hi,

    Does the gateway have the time cluster implemented? Do you know whether the gateway receives the read attributes request or not? I know you said earlier that you do not have another DK, but having a sniffer such as the nRF Sniffer for 802.15.4 to sniff the network traffic can be very helpful when working with Zigbee. With it you can see the packets sent on the network (if the sniffer has the network key), so you can see if the read attributes request seems to be sent correctly, and whether the gateway receives it and tries to send a response back. It is hard to help with this issue without knowing where the packet fails.

    The command itself looks correct. Be aware that ZB_ZCL_GENERAL_SEND_READ_ATTR_REQ only sends the read attributes request to the other device, but it does not parse it. This is done internally by the stack. If you want to parse it yourself (instead of the stack doing it internally), you should use ZB_ZCL_GENERAL_GET_NEXT_READ_ATTR_RES(data_buf, read_attr_resp), with the following parameters:

    • data_buf: ID zb_bufid_t of a buffer containing read attribute response data
    • read_attr_resp: out pointer to zb_zcl_read_attr_res_t, containing Read attribute status record

    An example of this in use is in the CLI example, you can see this in the file components/zigbee/cli/zigbee_cli_cmd_attr.c. First, the function readattr_send() sends the read attributes command, as you do above. The other device (your gateway in this example) receives the command, and responds with a read attributes response, as described in the ZCL specification. The original device that sent the read command shall then be notified of the result and the value of the attribute. In the CLI example, this is handled by the function cli_agent_ep_handler_attr(), which is a handler used to 'intercept' every frame coming to the endpoint. When it is a read attributes command response, it calls the function print_read_attr_response(), which then parses the read attributes response.

    The stack does handle required commands internally be default, so it should handle read attribute response commands as well. The reason this is done the way it is in the CLI example is because this example wants to do something else with the response than what the stack does. Therefore, it should handle the response in another way, so it overrides the default handler such that cli_agent_ep_handler_attr() is called instead. If you want more control over the response, you can implement a handler to override this as well. It should not be necessary, and I am not sure if it will fix the problem, as I do not know how the gateway responds to the read attributes command, or if it even gets it. If you want to do this, you should set an endpoint receive hook with ZB_AF_SET_ENDPOINT_HANDLER().

    If you want to use this, you should add it to your main function where you are registering the device context:

    /* Register thermostat device context (endpoints). */
    ZB_AF_REGISTER_DEVICE_CTX(&thermostat_ctx);
    
    /* Set the endpoint receive hook */
    ZB_AF_SET_ENDPOINT_HANDLER(THERMOSTAT_SENSOR_ENDPOINT, thermostat_ep_handler);

    Then you must implement the endpoint handler function (here I called it thermostat_ep_handler), which will be the same as what the function cli_agent_ep_handler_attr() is for the CLI example. As I said, I do not see why this should be necessary unless you want to override what happens when you receive a response to the read attributes command. The first thing you should do anyway is to try and figure out where the read attributes command fails.

    Best regards,

    Marte

  • Hi Marte,

    Does the gateway have the time cluster implemented? Do you know whether the gateway receives the read attributes request or not? I

    - yes, I am sure because with this gateway they have developed using NXP. Which I am porting as I said previously. So, what they do is when RTC is invalid they are requesting from Gateway. Here is a snippet of nxp code.

    PUBLIC void vRTC_vRequestTimeFromCoordiantor(void) {
    	void * thisNet = ZPS_pvAplZdoGetNwkHandle();
    	ZPS_tsNwkNib * thisNib = ZPS_psNwkNibGetHandle(thisNet);
    
    	uint8 u8Seq;
    	tsZCL_Address sAddress;
    
    	uint16 au16AttributeRequestList[] = {E_CLD_TIME_ATTR_ID_TIME };
    	//sAddress.eAddressMode = E_ZCL_AM_BROADCAST;
    
    	sAddress.eAddressMode = E_ZCL_AM_BOUND;
    	sAddress.uAddress.u16DestinationAddress = thisNib->sTbl.psNtDisc[0].u16NwkAddr;
    
    	DBG_vPrintf(TRACE_RTC, "\nAPP State Event:  dst Address %04x \n",thisNib->sTbl.psNtDisc[0].u16NwkAddr);
    	DBG_vPrintf(TRACE_RTC, "\nAPP State Event:  dst Endpoint %02x \n",sDeviceState.sMatchDev[sDeviceState.u8Index].u8Ep);
    
    			eZCL_SendReadAttributesRequest(
    			0x01   /*u8MyEndpoint*/, //uint8            u8SourceEndPointId,
    			u8DestinationEndpointID,//sDeviceState.sMatchDev[sDeviceState.u8Index].u8Ep,//0x0A,  					 //uint8            u8DestinationEndPointId,
    			GENERAL_CLUSTER_ID_TIME,                  //Time uint16      u16ClusterId,
    			FALSE,               	 //bool_t           bDirectionIsServerToClient,
    			&sAddress,               //tsZCL_Address    *psDestinationAddress,
    			&u8Seq,            		 //uint8            *pu8TransactionSequenceNumber,
    			1, 						 //uint8             Anzahl der reuqests die durchgeführt werden,
    			FALSE,                   //bool_t            bIsManufacturerSpecific,
    			ZCL_MANUFACTURER_CODE,                  //uint16            u16ManufacturerCode,
    			au16AttributeRequestList //uint16            *pu16AttributeRequestList
    			);
    
    }

    I know you said earlier that you do not have another DK, but having a sniffer such as the nRF Sniffer for 802.15.4 to sniff the network traffic

    - Ya, I know, to be honest, we are working from home due to this covid and I don't have other Dv-kit or dongle. But in few days I will arrange the dongle.

    Be aware that ZB_ZCL_GENERAL_SEND_READ_ATTR_REQ only sends the read attributes request to the other device, but it does not parse it. This is done internally by the stack. If you want to parse it yourself (instead of the stack doing it internally), you should use ZB_ZCL_GENERAL_GET_NEXT_READ_ATTR_RES(data_buf, read_attr_resp), with the following parameters:

    - Okay, as per my understanding I did like this, attached snippet.

    - So, as said in the first point this function I request when RTC is valid.

    void vRTC_vRequestTimeFromCoordiantor(void) 
    {
    
        zb_buf_t * p_buf;
    
        zb_uint8_t *cmd_ptr = NULL;
    
        ui16Addr = 0x0000;
    
        ui8Dst_addr = zb_zdo_active_ep_req(ZB_REF_FROM_BUF(p_buf), active_ep_callback);
    
        ZB_ZCL_GENERAL_INIT_READ_ATTR_REQ(p_buf, cmd_ptr, ZB_ZCL_ATTR_TIME_TIME_ID);
    
        ZB_ZCL_GENERAL_ADD_ID_READ_ATTR_REQ(cmd_ptr, ZB_ZCL_ATTR_TIME_TIME_ID);
    
        ZB_ZCL_GENERAL_SEND_READ_ATTR_REQ(p_buf, 
                                          cmd_ptr, 
                                          ui16Addr, 
                                          ZB_APS_ADDR_MODE_16_ENDP_PRESENT,
                                          ui8Dst_addr,
                                          THERMOSTAT_SENSOR_ENDPOINT, 
                                          ZB_AF_HA_PROFILE_ID,
                                          ZB_ZCL_CLUSTER_ID_TIME, 
                                          NULL);
    
        zb_buf_t *buf;
        zb_zcl_read_attr_res_t *read_attr_resp;
    
        ZB_ZCL_GENERAL_GET_NEXT_READ_ATTR_RES(buf, read_attr_resp);
    
    }

    In the CLI example, this is handled by the function cli_agent_ep_handler_attr(),

    - yes, I saw this code But what are these points. Is it required?. I got confused so I did not include it in my code.

    static zb_uint8_t cli_agent_ep_handler_attr(zb_uint8_t param)
    {
        zb_buf_t * p_zcl_cmd_buf = (zb_buf_t *)ZB_BUF_FROM_REF(param);
        zb_zcl_parsed_hdr_t * p_cmd_info = ZB_GET_BUF_PARAM(p_zcl_cmd_buf, zb_zcl_parsed_hdr_t);
        zb_int8_t row;
    
        /* Get the row in the requests table according by the sequence number */
        row = get_attr_table_row_by_sn(p_cmd_info->seq_number);
        if (row == -1)
        {
            return ZB_FALSE;
        }
    
        attr_query_t * p_row = &(m_attr_table[row]);
        if (!is_response(p_cmd_info, p_row))
        {
            return ZB_FALSE;
        }
    
        if (p_cmd_info->cmd_id == ZB_ZCL_CMD_DEFAULT_RESP)
        {
            zb_zcl_default_resp_payload_t * p_def_resp;
            p_def_resp = ZB_ZCL_READ_DEFAULT_RESP(p_zcl_cmd_buf);
            nrf_cli_fprintf(p_row->p_cli, NRF_CLI_ERROR, "Error: Default Response received; ");
            nrf_cli_fprintf(p_row->p_cli, NRF_CLI_ERROR, "Command: %d, Status: %d ",
                            p_def_resp->command_id, p_def_resp->status);
        }
        else
        {
            if (p_row->req_type == ATTR_READ_REQUEST)
            {
                print_read_attr_response(p_zcl_cmd_buf, p_row);
            }
            else
            {
                print_write_attr_response(p_zcl_cmd_buf, p_row);
            }
        }
        /* Cancel the ongoing alarm which was to erase the row... */
        UNUSED_RETURN_VALUE(ZB_SCHEDULE_ALARM_CANCEL(invalidate_row_cb, row));
        /* ...and erase it manually */
        invalidate_row(row);
    
        ZB_FREE_BUF(p_zcl_cmd_buf);
        return ZB_TRUE;
    }

    1)  row = get_attr_table_row_by_sn(p_cmd_info->seq_number) ?

    2) attr_query_t * p_row = &(m_attr_table[row]);?

    3) If and else statments from above snippet?

    - Or can copy this functions in my code for handler creation as you said?

    And, one more this is observed that if I run the code, (previously attached snippet)

    These 2 lines of code not affecting from active_ep_callback()

    1) zb_zdo_ep_resp_t *resp = (zb_zdo_ep_resp_t*)zdp_cmd;

    2) zb_uint8_t *ep_list = zdp_cmd + sizeof(zb_zdo_ep_resp_t);

    These are my test observation. Let me know your feedback on this same.

    Thanks and Regards

    Rohit R

Related