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 Rohit,

    I am not sure what you are trying to do in your callback. The thermostat cluster does not receive or send any commands related to the local temperature attribute, so you should not use the callback function for ZCL commands for this. Instead, you should read the attribute using either read attribute request command, or attribute reporting, which are explained here and here. You can also read about these commands in the Zigbee cluster library specification. The read attributes request command should be sent by the other device, so the gateway in your case. Same goes for configuring attribute reporting. 

    The implementation of the Pressure Measurement cluster in the SDK can be used to see how to implement attributes, both zb_zcl_pressure_measurement.h and zb_zcl_pressure_measurement.c, which can be found in components/zigbee/pressure_cluster/. You must add the attribute identifiers (ZB_ZCL_ATTR_NAME_VALUE_ID), implement the attributes according to your specification (for example set max value if they have that etc.), set the attribute description (ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_NAME_ID), declare your attribute list (ZB_ZCL_DECLARE_NAME_ATTRIB_LIST). All of this can be found in the Pressure Measurement cluster implementation. You must also remember to add it to your attribute list that you declare in main.c. Since you are using custom clusters, you cannot use ZB_ZCL_DECLARE_THERMOSTAT_ATTRIB_LIST to declare the attributes, but you can use ZB_ZCL_DECLARE_THERMOSTAT_ATTRIB_LIST_CUSTOM or something similar instead.

    Best regards,

    Marte

  • Hi Marte,

    Thanks for the feedback,

    This means, local temperature is just a read attribute and we can not use it in the callback function. We need to create other function where we can just read only. Correct?

    Since you are using custom clusters, you cannot use ZB_ZCL_DECLARE_THERMOSTAT_ATTRIB_LIST to declare the attributes, but you can use ZB_ZCL_DECLARE_THERMOSTAT_ATTRIB_LIST_CUSTOM or something similar instead.

    - I didn't get this statement. I have not yet designed any custom attributes. I want to design custom for that I need how to enable them. Which Id's are aviable? And in which file do we need declare them?

    - And pressure_measurement is custom file ?

    Thanks and Regards

    Rohit R

  • Hi  Marte,

    Sorry but I am very disappointed with you, you do not answer any queries properly.

    As I told you I am new to this, I am going through the list that is suggested by you but your documentation is not complete, explain few parts then some parts not. So only we ask for help. But you are not responding.

    Either you connect to someone else or reply as early as possible. I have told many times that I have very little time and you do not reply for 3/4 days.

    Here only see, you told to follow the pressesure_measurement, file to create our own cluster, I did but were to declare Custer ID? In your SDK for pressesure_measurement, it is declared in zb_zcl_common.h file already then is it possible to alter that file or no is not mentioned?

    Then how to declare descriptions, endpoints and etc for a custom that are not explained?

    I request please help me as early as possible or else connect to someone who can guide me in the proper direction.

    I hope you understand my situation.

    Thanks and Regards

    Rohit

  • Hi Rohit,

    I am sorry to hear that you are disappointed. I am trying to help you as well as I can, but we have multiple customers to help, and because of public holidays in Norway in may the office has been closed a few days.

    I will try to help you and guide you in implementing the thermostat, but you will have to implement it yourself, as I cannot create the whole application for you. However, I will try to explain more in depth.

    Rohit Rajapure said:
    This means, local temperature is just a read attribute and we can not use it in the callback function. We need to create other function where we can just read only. Correct?

    You are correct that you cannot use it in the callback function as you are doing. The callback function is used when the device receives an ZCL command from another device. An example of this is the light bulb, which receives ZCL commands from the light switch, such as the On/Off command. When the light bulb receives a ZCL command from the light switch, it will handle that command in the ZCL callback function, as you have probably seen, since you used some similar code to this in your project:

    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_ON_OFF)
        {
            uint8_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data8;
    
            NRF_LOG_INFO("on/off attribute setting to %hd", value);
            if (attr_id == ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID)
            {
                on_off_set_value((zb_bool_t) value);
            }
        }

     However, this will only be the case when the device receives a ZCL command. To know what commands a cluster sends or receives, you should look in the Zigbee Cluster Library specification. This specification has a lot of useful information about the different clusters, so I strongly recommend looking at it for clusters you are working with. Each cluster in the specification has chapters and tables with the cluster's attributes and commands. In the case of the light bulb and light switch, they are using the On/Off cluster, and in the cluster specification, you can find the cluster commands, or ZCL commands, of this cluster under the chapter Commands Received. There you will see, among others, the commands Off and On. Following this, there is a description for what the device shall do when it receives different commands, such as entering its "Off" state when receiving the Off command. And this is what happens in the ZCL callback in the light bulb example. The light bulb receives a cluster command, and then does as specified in the ZCL specification in the ZCL callback function.

    However, if you look at the Thermostat cluster in the ZCL specification, you will see that there is no command related to the local temperature. There are some other commands there, but they are not relevant for this (such as set/get weekly schedule etc.). So you should not handle it in the callback function.

    All attributes have different accesses. You can find what access they have in the attribute table of the specific cluster. For the thermostat you will find that the LocalTemperature attribute has "RP", which means that it is readable and reportable. So it supports global commands that read the attribute value, and it supports global commands that report the value or configure the attribute for reporting. This is what you should make us of in your thermostat. 

    A lot of the things related to this happens internally in the stack. For other devices to be able to read an attribute or configure attribute reporting, there are some things you must declare and configure in your project. You must declare the devices endpoint and device context, so that other devices are able to send commands to read/report the attribute. You will also have to declare it's simple description, endpoint description, and declare reporting if you want that. This is already done in the project you have:

    • You have an endpoint ID, which other devices can use to find your device, which you have defined to 9 with HA_THERMOSTAT_ENDPOINT
    • The endpoint, simple description, endpoint description and reporting is handled where you define ZB_HA_DECLARE_THERMOSTAT_EP, lines 102-113 in the project you attached.
    • The endpoint is declared so that other devices can make use of it on line ZB_HA_DECLARE_THERMOSTAT_EP
    • The device context is declared right under this ZB_HA_DECLARE_THERMOSTAT_CTX
    • Then the thermostat device context is registered on line 506, in main.c: ZB_AF_REGISTER_DEVICE_CTX(&thermostat_ctx)

    Since you have all of this, and the attribute is declared as it should, you have what you need for other devices to be able to read the LocalValue attribute. So you do not have to do anything more on the thermostat side. You have declared the context and endpoint for other devices to use, and everything else is handled by the stack. You do not need to implement a function to send the attribute value, as attributes (and handling them) and the thermostat cluster are implemented in our SDK as specified by the Zigbee specification, so as long as you declare the endpoint and context of your device, then the rest is handled automatically. The only thing you need is to update the value of the local temperature locally on your device, which is done by zb_set_attr_val (line 295 in your project). What new_value is, will of course be up to the application and how you get new temperature values.

    As for getting the value on another device, you will have to use attribute read request commands (or attribute reporting if you want it to report the value every set time instead). This is something you will have to do on the other device, the one that wants to receive the local temperature value from the thermostat device. This device does not need to use our SDK or stack, it only need to be implemented according to the Zigbee specification. From this device, you will have to send a read attributes command to your thermostat. This is a global command that Zigbee devices should have support for. However, I do not know what kind of device your other device, the gateway, is or how it is implemented, so I do not know what you must do to trigger a read attributes command. But when your gateway send a read attributes command to the thermostat, the thermostat will respond with a read attributes response command. The response command can contain multiple attributes, but for each attribute it will contain the following fields: attribute identifier, status, attribute data type, and attribute value. So the actual local temperature value will be the attribute value field.

    As I said, I do not know your gateway is implemented, but if you were using our SDK, you should be able to form and send a read attributes request with the following:

    ZB_ZCL_GENERAL_INIT_READ_ATTR_REQ(zcl_cmd_buf, cmd_ptr, ZB_ZCL_ENABLE_DEFAULT_RESPONSE);
    ZB_ZCL_GENERAL_ADD_ID_READ_ATTR_REQ(cmd_ptr, ZB_ZCL_ATTR_THERMOSTAT_LOCAL_TEMPERATURE_ID);
    ZB_ZCL_GENRAL_SEND_READ_ATTR_REQ(zcl_cmd_buf, cmd_ptr, DUT_ADDR, DUT_ADDR_MODE, DUT_ENDPOINT,
                                     TH_ENDPOINT, ZB_AF_HA_PROFILE_ID,
                                     ZB_ZCL_CLUSTER_ID_THERMOSTAT, NULL);

    You can find more information about two of these functions here: ZB_ZCL_GENERAL_INIT_READ_ATTR_REQ and ZB_ZCL_GENERAL_ADD_ID_READ_ATTR_REQ. 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

    When the gateway receives a read attributes response from the thermostat, you could parse it with ZB_ZCL_GENERAL_GET_NEXT_READ_ATTR_RES:

    ZB_ZCL_GENERAL_GET_NEXT_READ_ATTR_RES(buf, read_attr_resp);

    As I stated, the read attributes command will be on the gateway side, so do not have to do anything else in your project for other devices to be able to read the local temperature value attribute.

    Rohit Rajapure said:
    I didn't get this statement. I have not yet designed any custom attributes. I want to design custom for that I need how to enable them. Which Id's are aviable? And in which file do we need declare them?

    When you create custom attributes, you cannot use the ZB_ZCL_DECLARE_THERMOSTAT_ATTRIB_LIST to declare them, as the attribute list will no longer be implemented according to the specification. Therefore, you should instead create a custom attribute list with all your attributes instead.

    You can use IDs in the range 0x0000 – 0x4fff. This is stated in the ZCL specification. You should of course not use an ID which another attribute in the thermostat cluster is already using. 

    I would suggest creating a new file for this, as is done in the pressure_measurement. The pressure_measurement files in our SDK are implemented according to the ZCL specification for the Pressure Measurement cluster. However, since parts of the stack are internal parts and not visible in the SDK, the pressure_measurement files were created so customers can see how to implement clusters themselves. So the pressure_measurement files includes a complete implementation of a cluster. That is why we recommend that customers look at that when they try to implement a cluster, or something else custom in a cluster, as they should be able to see how it is done there and use that in their own application.

    Rohit Rajapure said:
    were to declare Custer ID?

    You can declare it wherever you want, as long as it is available to the functions you use that use this ID. If you want to create a custom cluster you should create the cluster header with the name corresponding to the following template: zb_zcl_<cluster_name>.h. You can for example declare the cluster ID in this file. The cluster header is where you should declare the required attributes with read, write, or report functionality (as is done in zb_zcl_pressure_measurement.h).

    As I have said, you should really look at the pressure measurement cluster to see what you need for a custom cluster, or for custom attributes. I will try to explain here what you will need to add, using the pressure measurement cluster (which as I mentioned earlier can be found in components/zigbee/pressure_cluster/), and referring to code lines in the .h and .c files.

    For zb_zcl_<cluster_name>.h

    • You will need to add them to a zb_zcl_<cluster_name>_attrs_t struct such as the zb_zcl_pressure_measurement_attrs_t (lines 48-54)
    • The cluster attribute identifiers (such as ZB_ZCL_ATTR_THERMOSTAT_TRV_MODE_ID from your other case) must be added to an enum zb_zcl_<cluster_name>_attr_e (lines 59-73)
    • I do not have enough information about your different attributes to say what the next should be, but if any of them have any ranges they should be between, or any default values, then you should add that next. For example the MinMeasuredValue attribute in the pressure measurement cluster can have values in the range 0x8001– 0x7ffe, and 0x8000 indicates that the attribute is not defined (this is found in the ZCL specification). Therefore, you find the following in the pressure measurement file:
      #define ZB_ZCL_ATTR_PRES_MEASUREMENT_VALUE_UNKNOWN                  ((zb_int16_t)0x8000)
      #define ZB_ZCL_ATTR_PRES_MEASUREMENT_MIN_VALUE_MIN_VALUE        ((zb_int16_t)0x8001)
      #define ZB_ZCL_ATTR_PRES_MEASUREMENT_MIN_VALUE_MAX_VALUE            0x7FFE
      So you should define default, min, max, and unknown values here as shown above, if your attributes have any (lines 76-106)
    • Next comes the description of your attribute, for example. I will try to use one of the attributes from your other case to show how this should be implemented, but as I do not know everything about the attribute, I cannot add everything:
      #define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ZB_ZCL_ATTR_THERMOSTAT_TRV_MODE_ID(data_ptr) \
      {                                                                              \
          ZB_ZCL_ATTR_THERMOSTAT_TRV_MODE_ID,                                     \
          ZB_ZCL_ATTR_TYPE_<attr_type>,                                                      \
          ZB_ZCL_ATTR_ACCESS_<access_type>,               \
          (zb_voidp_t) data_ptr                                                      \
      }

      Here you must set ZB_ZCL_ATTR_TYPE_<attr_type> to the data type your attribute value is. The list of different data types is found here. You must also set access type, which says what access other devices and clusters have to that attribute. The different types can be found here. One attribute can have more accesses than just one. (lines 108-137)
    • You must also include the rest of what is in the pressure measurement header file in your custom cluster, where the attribute list is declared (lines 148-155), as well as the functions for initializing the server and client sides of the cluster. Simply switch the names and the attributes in the attribute list to match your cluster and attributes.

    For zb_zcl_<cluster_name>.c

    • The first function, check_value_pres_measurement(), is only used to check the attribute values before they are written, to make sure they are valid. Since some of the attributes have min and max values, it makes sure that the values are within the range of valid values. This is something you can add if your attributes have a set of valid values, but it is not necessary (lines 53-140).
    • zb_zcl_pres_measurement_write_attr_hook() is a ZCL cluster write attribute hook handler. This handler is called before attribute change. If any cluster-specific action need to be bound to the attribute change, you can add it here. You should have the function, but whether you want any actions to happen when a specific attribute changes is up to you and the application. (lines 148-158)
    • You should add both functions that initialize the server and client sides of the cluster, and they should be similar to how they are in the pressure measurement file, simply calling zb_zcl_add_cluster_handlers and then having an assert. Documentation for and the parameters of the function zb_zcl_add_cluster_handlers() can be found here. (lines 161-180)

    I hope this explains things well enough so that you are able to implement things yourself! As we cannot implement the projects of every customer, we try to have the resources to help our customers be able to implement thing themselves, which is why we have the pressure measurement cluster, as well as some guides (such as Implementing a Zigbee end product with ZCL). In addition to this, you should really use the Zigbee Cluster Library specification, as you will find a lot of information you are looking for regarding the different clusters and how clusters work there. 

    Best regards,

    Marte

  • Hi Marte,

    Thanks for the response,

    I have created custom files with reference to the pressure_measurement file as you said. Also declared cluster ID for same in my custom header file as you said.

    Before your response, I also referred to multi_sensor code(used pressure measurement) to verify if there any mistakes made in the main.c file to call/declare my files but then I came to know that everything is correct in main.c file. Then I ran the code on my Dev-kit and open my Gateway GUI to check whether I can see my ID there or not. And I found it is not present.

    See the below image, (This image took when I disconnect my dev-kit for some other work) But during connection also it is same.

    1.

    Now, As I told you already that our Radiator device is developed on NXP MCU using a thermostat. Now, we are porting or developing the same functionality using nRF52840. Because Hardware streamlines as we are using nRF52840 for one more product that will be in-market very soon. So, we are developing this Radiator project to nRF52840.

    Here 2 points,

    1. If I use the radiator with already developed functionality and connect Gateway to it, you can see the GUI image where they declared custom attributes (mentioned in my other post) in the Thermostat cluster itself. Below is the Image for reference and Id name EUROtronic mentioned in the image.

    Images,

    1,

    2,

    2. From the above images it is clear that for controlling of radiator we need to implement a custom file.

    So, as you suggested in the previous post I followed the pressure measurement file to enable a custom cluster and it did not work.

    Then, thought let's change or alter the thermostat ha file and add my custom ID to it and declare it in the list. The code is attached in another post. Built the code and no error occurred so hoping that this time it will work and ran the code on dev-kit, then also no result. Still, the result remains the same as attached in the image no id found.

    Though let's change IDs and check, so I used ID from 0x4000 to 0x4008( code is in another post). Then also it did not work. It means I am following your steps but still not able to execute why? either I have missed something which I am not able to identity.

    And this state only I am stuck from the last 5days. Unable to find the cause. So I wrote a message like that. Sorry for this but I am not understanding after all following the steps why it is not enabling?

    I hope this explanation helps you to understand my point.

    It is okay if any SDK file alters because I am developing a single project so my team said no problem. Let me know What I missed from your suggestion.

    Thanks and Regards

    Rohit R

Related