espressif / esp-zigbee-sdk

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

ZHA Thermostat Occupancy attribute esp_zb_zcl_get_attribute causes core panic (TZ-1056) #403

Closed remenyo closed 3 months ago

remenyo commented 3 months ago

Answers checklist.

IDF version.

v5.1.4

esp-zigbee-lib version.

1.4.1

esp-zboss-lib version.

1.4.1

Espressif SoC revision.

ESP32-C6

What is the expected behavior?

esp_zb_zcl_get_attribute should work on a read-only attribute, in this example, thermostat occupancy (Cluster ID: 0x0201, Attribute ID: 0x0002)

What is the actual behavior?

Guru Meditation Error: Core  0 panic'ed (Load access fault). Exception was unhandled.
MEPC    : 0x4200898a  RA      : 0x4200898a  SP      : 0x40846590  GP      : 0x4080f0f0
TP      : 0x407f8aa0  T0      : 0x40022494  T1      : 0x40808b70  T2      : 0x00000000
S0/FP   : 0x00000000  S1      : 0x00000000  A0      : 0x00000001  A1      : 0x00000000
A2      : 0x40846648  A3      : 0x00000006  A4      : 0x00000001  A5      : 0x20001000
A6      : 0x00000001  A7      : 0x00000000  S2      : 0x00000000  S3      : 0x00000003
S4      : 0x00000000  S5      : 0x008b6b52  S6      : 0x4081a000  S7      : 0x00000005
S8      : 0x00000004  S9      : 0x000002bb  S10     : 0x00004e20  S11     : 0x03000000
T3      : 0x00000000  T4      : 0x00000000  T5      : 0x00000000  T6      : 0x00000000
MSTATUS : 0x00001881  MTVEC   : 0x40800001  MCAUSE  : 0x00000005  MTVAL   : 0x00000007

Steps to reproduce.

This works fine:

esp_zb_lock_acquire(pdTICKS_TO_MS(10000));
int16_t temp = (int16_t)(DS18B20::measure() * 100);
esp_zb_zcl_status_t status = esp_zb_zcl_set_attribute_val(1, 0x0201, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, 0, &temp, false);
LOGI("Write status: %d", status);
esp_zb_zcl_attr_t *attr = esp_zb_zcl_get_attribute(1, 0x0201, esp_zb_zcl_cluster_role_t::ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, 0);
esp_zb_lock_release();
if (attr->data_p != nullptr) {
    ESP_LOGI(TAG,"read temp %f with esp_zb_zcl_get_attribute command", static_cast<float>(*static_cast<int16_t *>(attr->data_p)) / 100.0);
}
else {
    ESP_LOGE(TAG,"attr->data_p = nullptr");
}

Outputs:

I :      temperature read from sensor: 27.50C
...
I :       Write status: 0
I :       read temp 27.500000 with esp_zb_zcl_get_attribute command

This does not work:

esp_zb_lock_acquire(pdTICKS_TO_MS(10000));
uint8_t buf = 0; // it's a map8 value, 0x00 is a valid value for it: Space is "Unoccupied"
esp_zb_zcl_status_t status = esp_zb_zcl_set_attribute_val(1, 0x0201, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, 2, &buf, false);
ESP_LOGI(TAG, "Write status: %d", status);
esp_zb_zcl_attr_t *attr = esp_zb_zcl_get_attribute(1, 0x0201, esp_zb_zcl_cluster_role_t::ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, 2);
esp_zb_lock_release();
if (attr->data_p != nullptr) {
    ESP_LOGI(TAG, "0x%02x", *static_cast<uint8_t *>(attr->data_p));
}
else {
    ESP_LOGE(TAG,"attr->data_p = nullptr");
}

Outputs: I : Write status: 1 then crash.

More Information.

I hope that with the first (working) example I proved that I set up the thermostat cluster correctly, but feel free to check it:

// ...
thermostat_cfg.basic_cfg.power_source = 0x01; // "Mains (single phase)"
esp_zb_attribute_list_t *thermostat_cluster_attr_list = esp_zb_thermostat_cluster_create(&(thermostat_cfg.thermostat_cfg));
// ...
ESP_ERROR_CHECK(esp_zb_cluster_list_add_thermostat_cluster(cluster_list, thermostat_cluster_attr_list, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE));
// ...
esp_zb_ep_list_t *ep_list = esp_zb_ep_list_create();
esp_zb_endpoint_config_t endpoint_config = {
      .endpoint = 1,
      .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
      .app_device_id = ESP_ZB_HA_THERMOSTAT_DEVICE_ID,
      .app_device_version = 1,
};
esp_zb_ep_list_add_ep(ep_list, cluster_list, endpoint_config);
esp_zb_device_register(ep_list);
xieqinan commented 3 months ago

@remenyo ,

The issue is caused by the absence of an attribute in the thermostat cluster. The esp_zb_thermostat_cluster_create() function only creates the mandatory attributes defined in the ZCL specification. The ZB_ZCL_ATTR_THERMOSTAT_OCCUPANCY_ID is an optional attribute, which requires to be added by the user. You can refer to the code below to resolve this issue.

uint8_t thermostat_occupancy = 0;
esp_zb_attribute_list_t *thermostat_cluster_attr_list = esp_zb_thermostat_cluster_create(&(thermostat_cfg.thermostat_cfg));
esp_zb_thermostat_cluster_add_attr(thermostat_cluster_attr_list, ESP_ZB_ZCL_ATTR_THERMOSTAT_OCCUPANCY_ID, &thermostat_occupancy);
remenyo commented 3 months ago

Thank you for your help, this was the solution to my problem indeed. So this is not a bug, optional attributes are not created with esp_zb_*clustername*_cluster_create

May I also ask how to traverse through the cluster list, to examine the list?

// this does _not_ work correctly
for (esp_zb_cluster_list_t *c = cluster_list; cluster_list != NULL; c = c->next)
{
    LOGI("Cluster ID: 0x%04x", c->cluster.cluster_id);
    for (esp_zb_attribute_list_t *a = c->cluster.attr_list; a != NULL; a = a->next)
    {
        LOGI("Attribute ID: 0x%04x, Type: 0x%02x, Access: 0x%02x)", a->attribute.id, a->attribute.type, a->attribute.access);
    }
}

This outputs:

I esp_zb_task:  Cluster ID: 0x0000
I esp_zb_task:  Cluster ID: 0x0b05
I esp_zb_task:  Attribute ID: 0x0000, Type: 0x00, Access: 0x00)
I esp_zb_task:  Attribute ID: 0xfffd, Type: 0x21, Access: 0x01)
I esp_zb_task:  Cluster ID: 0x0201
I esp_zb_task:  Attribute ID: 0x0000, Type: 0x00, Access: 0x00)
I esp_zb_task:  Attribute ID: 0xfffd, Type: 0x21, Access: 0x01)
I esp_zb_task:  Attribute ID: 0x0000, Type: 0x29, Access: 0x05)
I esp_zb_task:  Attribute ID: 0x0011, Type: 0x29, Access: 0x13)
I esp_zb_task:  Attribute ID: 0x0012, Type: 0x29, Access: 0x13)
I esp_zb_task:  Attribute ID: 0x001b, Type: 0x30, Access: 0x03)
I esp_zb_task:  Attribute ID: 0x001c, Type: 0x30, Access: 0x13)
...
Guru Meditation Error: Core  0 panic'ed (Load access fault). Exception was unhandled.
Core  0 register dump:
MEPC    : 0x42006d18  RA      : 0x42006d56  SP      : 0x40851900  GP      : 0x4080f0f0
TP      : 0x40803e0c  T0      : 0x40022494  T1      : 0xffffffe0  T2      : 0x00000200
S0/FP   : 0x00000000  S1      : 0x00000001  A0      : 0x0000003f  A1      : 0x40857fec
A2      : 0x00000010  A3      : 0x00000800  A4      : 0x00000001  A5      : 0x00000004
A6      : 0x00000001  A7      : 0x42005444  S2      : 0x40857ef4  S3      : 0x42121000
S4      : 0x42121000  S5      : 0x00000000  S6      : 0x42121000  S7      : 0x00000000
S8      : 0x00000000  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000
T3      : 0x00000000  T4      : 0x00000000  T5      : 0x0000000f  T6      : 0xffffffb1
MSTATUS : 0x00001881  MTVEC   : 0x40800001  MCAUSE  : 0x00000005  MTVAL   : 0x00000000
MHARTID : 0x00000000
xieqinan commented 3 months ago

@remenyo ,

suggestion:

for (esp_zb_cluster_list_t *c = cluster_list; c != NULL; c = c->next)
{
    LOGI("Cluster ID: 0x%04x", c->cluster.cluster_id);
    for (esp_zb_attribute_list_t *a = c->cluster.attr_list; a != NULL; a = a->next)
    {
        LOGI("Attribute ID: 0x%04x, Type: 0x%02x, Access: 0x%02x)", a->attribute.id, a->attribute.type, a->attribute.access);
    }
}
remenyo commented 3 months ago

Again, thank you very much.