espressif / esp-zigbee-sdk

Espressif Zigbee SDK
Apache License 2.0
178 stars 31 forks source link

Custom clusters: esp_zb_zcl_get_attribute() and esp_zb_zcl_get_manufacturer_attribute() not working (TZ-1150) #434

Closed jkano closed 1 month ago

jkano commented 2 months ago

Question

Hi,

I'm working on an application which has the OnOff cluster and a Custom Cluster (Manufacturer Specific) based on the example on how to create custom clusters: https://docs.espressif.com/projects/esp-zigbee-sdk/en/latest/esp32/user-guide/zcl_custom.html

In my code, I create an endpoint list on endpoint 1, add mandatory clusters (Basic, Identify), add the OnOff cluster and then I add a custom cluster (0xFF02) with one custom attribute (0x0000, with a custom value of 10):

#define CUSTOM_ENDPOINT 1
#define CUSTOM_CLUSTER_ID 0xFF02
#define CUSTOM_ATTR_ID 0x0000

static uint32_t custom_cluster_attr_0 = 10;

static bool zb_raw_cmd_handler(uint8_t bufid) ...

...

static void esp_zb_task(void *pvParameters)
{
    /* initialize Zigbee stack */
    esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG();
    esp_zb_init(&zb_nwk_cfg);

    esp_zb_ep_list_t *ep_list = esp_zb_ep_list_create();

    esp_zb_endpoint_config_t endpoint_config = {
        .endpoint = CUSTOM_ENDPOINT,
        .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
        .app_device_id = ESP_ZB_HA_CUSTOM_ATTR_DEVICE_ID,
        .app_device_version = 0,
    };

    // Create custom cluster
    esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create();
    esp_zb_attribute_list_t *custom_cluster = esp_zb_zcl_attr_list_create(CUSTOM_CLUSTER_ID);

    const uint16_t attr_id = CUSTOM_ATTR_ID;
    void *attr_value = &custom_cluster_attr_0;
    esp_zb_custom_cluster_add_custom_attr(custom_cluster, attr_id, ESP_ZB_ZCL_ATTR_TYPE_U32, ESP_ZB_ZCL_ATTR_MANUF_SPEC | ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, attr_value);

    // Mandatory clusters
    esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
    esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);

    // OnOff cluster
    esp_zb_cluster_list_add_on_off_cluster(cluster_list, esp_zb_on_off_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);

    // Custom cluster
    esp_zb_cluster_list_add_custom_cluster(cluster_list, custom_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);

    // Add endpoint and register the device
    esp_zb_ep_list_add_ep(ep_list, cluster_list, endpoint_config);
    esp_zb_device_register(ep_list);

    esp_zb_raw_command_handler_register(zb_raw_cmd_handler);
    esp_zb_core_action_handler_register(zb_action_handler);
    esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
    ESP_ERROR_CHECK(esp_zb_start(false));
    esp_zb_stack_main_loop();
}

When I join this device to the network I can see the custom cluster (with a simple descriptor request) and the custom attribute (with a discover attributes request).

I can read the OnOff cluster OnOff attribute without any issues, but when I send a read attribute request to the custom cluster (0xFF02) with manufacturer specific flag set and manuf. code 0x1075, the device receives it but it can't read the attribute value.

So I added the esp_zb_raw_command_handler_register(zb_raw_cmd_handler); to do intercept (but not really) the zcl messages coming to the device so I can print the data there:

This is my zb_raw_cmd_handler function:

static bool zb_raw_cmd_handler(uint8_t bufid)
{
    ESP_LOGI(TAG, "zb_raw_cmd_handler()");

    zb_zcl_parsed_hdr_t *cmd_info = ZB_BUF_GET_PARAM(bufid, zb_zcl_parsed_hdr_t);

    ESP_LOGW(TAG, "Dst endpoint: %d", cmd_info->addr_data.common_data.dst_endpoint);
    ESP_LOGW(TAG, "Cluster id  : 0x%04x, Command id: 0x%02x", cmd_info->cluster_id, cmd_info->cmd_id);
    ESP_LOGW(TAG, "Cmd common  : %s", cmd_info->is_common_command ? "true" : "false");
    ESP_LOGW(TAG, "Manuf Spec  : %s", cmd_info->is_manuf_specific ? "true" : "false");
    ESP_LOGW(TAG, "Manuf data  : 0x%04x", cmd_info->manuf_specific);

    uint8_t data[zb_buf_len(bufid)];
    memcpy(data, zb_buf_begin(bufid), sizeof(data));

    if ((cmd_info->is_common_command))
    {
        uint8_t endpoint = cmd_info->addr_data.common_data.dst_endpoint;

        if (cmd_info->cmd_id == ZB_ZCL_CMD_READ_ATTRIB)
        {
            int data_len = sizeof(uint16_t);
            for (int i = 0; i < sizeof(data); i += data_len)
            {
                uint16_t attr_id = (data[i + 1] << 8) | data[i];

                zb_af_endpoint_desc_t *ep = get_endpoint_by_cluster_with_role(cmd_info->cluster_id, ZB_ZCL_CLUSTER_SERVER_ROLE);
                zb_zcl_cluster_desc_t *cluster = get_cluster_desc(ep, cmd_info->cluster_id, ZB_ZCL_CLUSTER_SERVER_ROLE);

                ESP_LOGI(TAG, "- Cluster: 0x%04x, manuf code: 0x%04x", cluster->cluster_id, cluster->manuf_code);
                ESP_LOGI(TAG, "- Attr_count: %d", cluster->attr_count);
                for (uint8_t i = 0; i < cluster->attr_count; ++i)
                {
                    ESP_LOGI(TAG, "   Attr[%d]: 0x%04x, type: 0x%02x, manuf code: 0x%04x", i, cluster->attr_desc_list[i].id,
                             cluster->attr_desc_list[i].type,
                             cluster->attr_desc_list[i].manuf_code);
                }

                // Get this attribute value
                esp_zb_zcl_attr_t *val = esp_zb_zcl_get_attribute(endpoint, cluster->cluster_id, ZB_ZCL_CLUSTER_SERVER_ROLE, attr_id);
                ESP_LOGI(TAG, "esp_zb_zcl_get_attribute() = %s", val == NULL ? "Error" : "OK");

                // Get this attribute value with manufacturer code
                esp_zb_zcl_attr_t *val2 = esp_zb_zcl_get_manufacturer_attribute(endpoint, cluster->cluster_id, ZB_ZCL_CLUSTER_SERVER_ROLE, attr_id, 0xffff);
                ESP_LOGI(TAG, "esp_zb_zcl_get_manufacturer_attribute() = %s", val2 == NULL ? "Error" : "OK");
            }
        }
    }

    return false;
}

As you can see there, I print all the data about the received command and also if the command is a Read Attribute Request, I search the cluster attribute and print the data found in the cluster descriptor. Finally I try to get the attribute value using esp_zb_zcl_get_attribute() and esp_zb_zcl_get_manufacturer_attribute().

There are the logs when I run the application:

  1. Sending a Read Attribute request to the OnOff cluster, OnOff attribute:
I (761600) ESP_ZB_ON_OFF_LIGHT: zb_raw_cmd_handler()
W (761600) ESP_ZB_ON_OFF_LIGHT: Dst endpoint: 1
W (761600) ESP_ZB_ON_OFF_LIGHT: Cluster id  : 0x0006, Command id: 0x00
W (761600) ESP_ZB_ON_OFF_LIGHT: Cmd common  : true
W (761610) ESP_ZB_ON_OFF_LIGHT: Manuf Spec  : false
W (761620) ESP_ZB_ON_OFF_LIGHT: Manuf data  : 0x0000
I (761620) ESP_ZB_ON_OFF_LIGHT: - Cluster: 0x0006, manuf code: 0x0000
I (761630) ESP_ZB_ON_OFF_LIGHT: - Attr_count: 3
I (761630) ESP_ZB_ON_OFF_LIGHT:    Attr[0]: 0xfffd, type: 0x21, manuf code: 0xffff
I (761640) ESP_ZB_ON_OFF_LIGHT:    Attr[1]: 0x0000, type: 0x10, manuf code: 0xffff
I (761650) ESP_ZB_ON_OFF_LIGHT:    Attr[2]: 0xffff, type: 0x00, manuf code: 0xffff
I (761660) ESP_ZB_ON_OFF_LIGHT: esp_zb_zcl_get_attribute() = OK
I (761660) ESP_ZB_ON_OFF_LIGHT: esp_zb_zcl_get_manufacturer_attribute() = OK
  1. Sending a Read Attribute request to the custom cluster (0xff02), attribute 0x0000:
    I (809330) ESP_ZB_ON_OFF_LIGHT: zb_raw_cmd_handler()
    W (809330) ESP_ZB_ON_OFF_LIGHT: Dst endpoint: 1
    W (809330) ESP_ZB_ON_OFF_LIGHT: Cluster id  : 0xff02, Command id: 0x00
    W (809330) ESP_ZB_ON_OFF_LIGHT: Cmd common  : true
    W (809340) ESP_ZB_ON_OFF_LIGHT: Manuf Spec  : true
    W (809340) ESP_ZB_ON_OFF_LIGHT: Manuf data  : 0x1075
    I (809350) ESP_ZB_ON_OFF_LIGHT: - Cluster: 0xff02, manuf code: 0x0000
    I (809360) ESP_ZB_ON_OFF_LIGHT: - Attr_count: 3
    I (809360) ESP_ZB_ON_OFF_LIGHT:    Attr[0]: 0xfffd, type: 0x21, manuf code: 0xffff
    I (809370) ESP_ZB_ON_OFF_LIGHT:    Attr[1]: 0x0000, type: 0x23, manuf code: 0xffff
    I (809380) ESP_ZB_ON_OFF_LIGHT:    Attr[2]: 0xffff, type: 0x00, manuf code: 0xffff
    I (809390) ESP_ZB_ON_OFF_LIGHT: esp_zb_zcl_get_attribute() = Error
    I (809390) ESP_ZB_ON_OFF_LIGHT: esp_zb_zcl_get_manufacturer_attribute() = Error

As you can see, the first request is fine, both esp_zb_zcl_get_attribute and esp_zb_zcl_get_manufacturer_attribute are returning the attribute value.

But on the second one, as you can see, in the cluster descriptor the manuf. code is 0x0000 and for each attribute on that cluster the manuf. code is 0xffff. I think this is why neither esp_zb_zcl_get_attribute and esp_zb_zcl_get_manufacturer_attribute are working to get the attribute value.

Also, the response for the Read Attribute Request is code 0x86 (ESP_ZB_ZCL_STATUS_UNSUP_ATTRIB).

I dont see where to set the manuf. code for the cluster and attributes and I'm not sure if this is why the esp_zb_zcl_get_manufacturer_attribute are not working correctly.

So my question is, what do I have to do for this custom clusters to work correctly? Read/Write attribute, Reporting, and cluster commands.

Thanks!

Additional context.

No response

lpy4105 commented 2 months ago

For time being, setting manufacturer code is not supported. Don't set the access of the attribute to ESP_ZB_ZCL_ATTR_MANUF_SPEC when creating the custom attribute:

esp_zb_custom_cluster_add_custom_attr(custom_cluster, attr_id, 
                                      ESP_ZB_ZCL_ATTR_TYPE_U32, 
                                      ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, 
                                      attr_value);
jkano commented 2 months ago

Hi. Thanks for the response.

The workaround works but it will not comply with Zigbee Spec.