espressif / esp-zigbee-sdk

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

How to send one-time value to coordinator? (TZ-935) #367

Closed matthiasbuettner159 closed 2 months ago

matthiasbuettner159 commented 5 months ago

Question

I am trying to setup a battery powered zigbee device/sensor with deep sleep to ensure long battery life. However I am struggling to send a new value to the corrdinator after the device wakes up.

I have been trying to follow the temperature sensor example but it does not update the value with the setup reporting mechanism (esp_zb_zcl_set_attribute_val and esp_zb_zcl_update_reporting_info). As alternative I am also struggling to get the esp_zb_zcl_write_attr_cmd_req command to work, as I do not yet fully understand which parameters are needed and how to aquire the dst_addr_u and dst_endpoint values. And I am also not sure which is the proper approach.

What I want to achieve is:

  1. Connect device to coordinator (zigbee2mqtt) -> successfully done.
  2. Send device to deep_sleep mode to preserve battery life -> successfully done.
  3. When device wakes up reconnect to coordinator -> successfully done.
  4. After connecting to coorinator send one-time meassured sensor value -> this is not working: How can I achieve this?
  5. Send device back to deep_sleep -> successfully done.

Additional question: I also want to meassure the battery level/voltage and send this as well to the coordinator. How can I send multiple values (probably to different endpoints?)?

Thanks a lot for your input :)

Additional context.

This is the source code of my sensor.

#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif

#include "esp_zigbee_core.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ha/esp_zigbee_ha_standard.h"
#include <BH1750.h>
#include <Wire.h>

/* Default End Device config */
#define ESP_ZB_ZED_CONFIG()                                                                 \
  {                                                                                         \
    .esp_zb_role = ESP_ZB_DEVICE_TYPE_ED, .install_code_policy = INSTALLCODE_POLICY_ENABLE, \
    .nwk_cfg = {                                                                            \
      .zed_cfg =                                                                            \
        {                                                                                   \
          .ed_timeout = ED_AGING_TIMEOUT,                                                   \
          .keep_alive = ED_KEEP_ALIVE,                                                      \
        },                                                                                  \
    },                                                                                      \
  }

#define ESP_ZB_DEFAULT_RADIO_CONFIG() \
  { .radio_mode = ZB_RADIO_MODE_NATIVE, }

#define ESP_ZB_DEFAULT_HOST_CONFIG() \
  { .host_connection_mode = ZB_HOST_CONNECTION_MODE_NONE, }

/* Zigbee configuration */
#define INSTALLCODE_POLICY_ENABLE   false /* enable the install code policy for security */
#define ED_AGING_TIMEOUT            ESP_ZB_ED_AGING_TIMEOUT_64MIN
#define ED_KEEP_ALIVE               3000                                 /* 3000 millisecond */
#define HA_ESP_SENSOR_ENDPOINT      10                                   /* esp temperature sensor device endpoint, used for temperature measurement */
#define ESP_ZB_PRIMARY_CHANNEL_MASK ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK /* Zigbee primary channel mask use in the example */

/* Illuminance sensor configuration */
#define ESP_ILLU_SENSOR_UPDATE_INTERVAL (1)     /* Local sensor update interval (second) */
#define ESP_ILLU_SENSOR_MIN_VALUE       (0)     /* Local sensor min measured value (lux) */
#define ESP_ILLU_SENSOR_MAX_VALUE       (65535) /* Local sensor max measured value (lux) */

/* Attribute values in ZCL string format
 * The string should be started with the length of its own.
 */
#define MANUFACTURER_NAME \
  "\x0B"                  \
  "ESPRESSIF"
#define MODEL_IDENTIFIER "\x09" CONFIG_IDF_TARGET

typedef struct esp_zb_illuminance_sensor_cfg_s {
    esp_zb_basic_cluster_cfg_t basic_cfg;                /*!<  Basic cluster configuration, @ref esp_zb_basic_cluster_cfg_s */
    esp_zb_identify_cluster_cfg_t identify_cfg;          /*!<  Identify cluster configuration, @ref esp_zb_identify_cluster_cfg_s */
    esp_zb_illuminance_meas_cluster_cfg_t illu_meas_cfg; /*!<  Illuminance measurement cluster configuration, @ref esp_zb_illuminance_meas_cluster_cfg_s */
} esp_zb_illuminance_sensor_cfg_t;

#define ESP_ZB_DEFAULT_ILLUMINANCE_SENSOR_CONFIG()                                                  \
    {                                                                                               \
        .basic_cfg =                                                                                \
            {                                                                                       \
                .zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE,                          \
                .power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE,                        \
            },                                                                                      \
        .identify_cfg =                                                                             \
            {                                                                                       \
                .identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE,                   \
            },                                                                                      \
        .illu_meas_cfg =                                                                            \
            {                                                                                       \
                .measured_value = ESP_ZB_ZCL_ATTR_ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_INVALID,   \
                .min_value = ESP_ZB_ZCL_ATTR_ILLUMINANCE_MEASUREMENT_MIN_MEASURED_VALUE_MIN_VALUE,  \
                .max_value = ESP_ZB_ZCL_ATTR_ILLUMINANCE_MEASUREMENT_MAX_MEASURED_VALUE_MAX_VALUE,  \
            },                                                                                      \
    }

BH1750 lightMeter;

static const char *TAG = "ESP_ZB_DEEP_SLEEP";
static esp_timer_handle_t s_oneshot_timer;

/********************* Zigbee functions **************************/
static int16_t zb_illuminance_to_s16(float temp) {
  return (int16_t)(temp);
}

static void esp_app_illu_sensor_handler(float illuminance) {
  int16_t measured_value = zb_illuminance_to_s16(illuminance);
  log_i("Updating illuminance sensor value 3...");
  log_i("%d", measured_value);
  /* Update illuminance sensor measured value */
  esp_zb_lock_acquire(portMAX_DELAY);
  esp_zb_zcl_set_attribute_val(HA_ESP_SENSOR_ENDPOINT, ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ID, &measured_value, false);
  esp_zb_lock_release();
}

static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) {
  ESP_ERROR_CHECK(esp_zb_bdb_start_top_level_commissioning(mode_mask));
}

void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
  uint32_t *p_sg_p = signal_struct->p_app_signal;
  esp_err_t err_status = signal_struct->esp_err_status;
  esp_zb_app_signal_type_t sig_type = (esp_zb_app_signal_type_t)*p_sg_p;
  switch (sig_type) {
    case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
      log_i("Zigbee stack initialized");
      esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
      break;
    case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:
    case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:
      if (err_status == ESP_OK) {
        log_i("Start network steering");
        log_i("Device started up in %s factory-reset mode", esp_zb_bdb_is_factory_new() ? "" : "non");
        if (esp_zb_bdb_is_factory_new()) {
          log_i("Start network steering");
          esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
        } else {
          log_i("Device rebooted");
          illu_sensor_value_update();
          zb_deep_sleep_start();
        }
      } else {
        /* commissioning failed */
        log_w("Failed to initialize Zigbee stack (status: %s)", esp_err_to_name(err_status));
      }
      break;
    case ESP_ZB_BDB_SIGNAL_STEERING:
      if (err_status == ESP_OK) {
        esp_zb_ieee_addr_t extended_pan_id;
        esp_zb_get_extended_pan_id(extended_pan_id);
        log_i(
          "Joined network successfully (Extended PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: 0x%04hx, Channel:%d, Short Address: 0x%04hx)",
          extended_pan_id[7], extended_pan_id[6], extended_pan_id[5], extended_pan_id[4], extended_pan_id[3], extended_pan_id[2], extended_pan_id[1],
          extended_pan_id[0], esp_zb_get_pan_id(), esp_zb_get_current_channel(), esp_zb_get_short_address()
        );
        illu_sensor_value_update();
        zb_deep_sleep_start();
      } else {
        log_i("Network steering was not successful (status: %s)", esp_err_to_name(err_status));
        esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_NETWORK_STEERING, 1000);
      }
      break;
    default: log_i("ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type, esp_err_to_name(err_status)); break;
  }
}

static esp_zb_cluster_list_t *custom_illuminance_sensor_clusters_create(esp_zb_illuminance_sensor_cfg_t *illuminance_sensor) {
  esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create();
  esp_zb_attribute_list_t *basic_cluster = esp_zb_basic_cluster_create(&(illuminance_sensor->basic_cfg));
  ESP_ERROR_CHECK(esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, (void *)MANUFACTURER_NAME));
  ESP_ERROR_CHECK(esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, (void *)MODEL_IDENTIFIER));
  ESP_ERROR_CHECK(esp_zb_cluster_list_add_basic_cluster(cluster_list, basic_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE));
  ESP_ERROR_CHECK(
    esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(&(illuminance_sensor->identify_cfg)), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE)
  );
  ESP_ERROR_CHECK(
    esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE)
  );
  ESP_ERROR_CHECK(esp_zb_cluster_list_add_illuminance_meas_cluster(
    cluster_list, esp_zb_illuminance_meas_cluster_create(&(illuminance_sensor->illu_meas_cfg)), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE
  ));
  return cluster_list;
}

static esp_zb_ep_list_t *custom_illuminance_sensor_ep_create(uint8_t endpoint_id, esp_zb_illuminance_sensor_cfg_t *illuminance_sensor) {
  esp_zb_ep_list_t *ep_list = esp_zb_ep_list_create();
  esp_zb_endpoint_config_t endpoint_config = {
    .endpoint = endpoint_id, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_TEST_DEVICE_ID, .app_device_version = 0
  };
  esp_zb_ep_list_add_ep(ep_list, custom_illuminance_sensor_clusters_create(illuminance_sensor), endpoint_config);
  return ep_list;
}

static void esp_zb_task(void *pvParameters) {
  esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG();
  esp_zb_init(&zb_nwk_cfg);

  /* Create customized temperature sensor endpoint */
  esp_zb_illuminance_sensor_cfg_t sensor_cfg = ESP_ZB_DEFAULT_ILLUMINANCE_SENSOR_CONFIG();
  /* Set (Min|Max)MeasuredValure */
  esp_zb_ep_list_t *esp_zb_sensor_ep = custom_illuminance_sensor_ep_create(HA_ESP_SENSOR_ENDPOINT, &sensor_cfg);
  /* Register the device */
  esp_zb_device_register(esp_zb_sensor_ep);

  /* Config the reporting info  */
  esp_zb_zcl_reporting_info_t reporting_info = {
    .direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV,
    .ep = HA_ESP_SENSOR_ENDPOINT,
    .cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT,
    .cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,
    .attr_id = ESP_ZB_ZCL_ATTR_ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ID,
    .u =
      {
        .send_info =
          {
            .min_interval = 1,
            .max_interval = 0,
            .delta =
              {
                .u16 = 100,
              },
            .def_min_interval = 1,
            .def_max_interval = 0,
          },
      },
    .dst =
      {
        .profile_id = ESP_ZB_AF_HA_PROFILE_ID,
      },
    .manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC,
  };
  esp_zb_zcl_update_reporting_info(&reporting_info);
  esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);

  //Erase NVRAM before creating connection to new Coordinator
  //esp_zb_nvram_erase_at_start(true); //Comment out this line to erase NVRAM data if you are conneting to new Coordinator
  esp_zb_secur_network_min_join_lqi_set(0);

  ESP_ERROR_CHECK(esp_zb_start(false));
  esp_zb_main_loop_iteration();
}

static void zb_deep_sleep_start(void)
{
    /* Start the one-shot timer */
    const int before_deep_sleep_time_sec = 5;
    ESP_LOGI(TAG, "Start one-shot timer for %ds to enter the deep sleep", before_deep_sleep_time_sec);
    ESP_ERROR_CHECK(esp_timer_start_once(s_oneshot_timer, before_deep_sleep_time_sec * 1000000));
}

static void s_oneshot_timer_callback(void* arg)
{
    /* Enter deep sleep */
    ESP_LOGI(TAG, "Enter deep sleep");
    esp_deep_sleep_start();
}

/************************ Illu sensor *****************************/
static void illu_sensor_value_update(void) {
  float lux = lightMeter.readLightLevel();
  esp_app_illu_sensor_handler(lux);
}

static void zb_deep_sleep_init(void) {
  const esp_timer_create_args_t s_oneshot_timer_args = {
            .callback = &s_oneshot_timer_callback,
            .name = "one-shot"
  };

  ESP_ERROR_CHECK(esp_timer_create(&s_oneshot_timer_args, &s_oneshot_timer));

  const int wakeup_time_sec = 10;                                             
  log_i("Enabling timer wakeup.");
  ESP_ERROR_CHECK(esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000));
}

/********************* Arduino functions **************************/
void setup() {
  Serial.begin(115200);

  // Perform light measurement
  log_i("Setup I2C");
  Wire.begin();
  lightMeter.begin();

  // Init Zigbee
  esp_zb_platform_config_t config = {
    .radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG(),
    .host_config = ESP_ZB_DEFAULT_HOST_CONFIG(),
  };
  ESP_ERROR_CHECK(esp_zb_platform_config(&config));

  zb_deep_sleep_init();

  // Start Zigbee task
  xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 5, NULL);
}

void loop() {

}
xieqinan commented 5 months ago

@matthiasbuettner159 ,

Could you please provide the log_i("%d", measured_value); for me? According to the code above, the value will only be reported when the measured_value changes by more than delta.u16 = 100.

matthiasbuettner159 commented 5 months ago

@xieqinan ,

the value ranges from 0 to about 1000 in rare cases. However changes between individual measurements are ususally significantly smaller than 100. But I also do not get an updated value at my coordinator if I change the delta to delta.u16 = 1.

The logs I receive are:

[  1278][I][Zigbee_LightMeter_Sensor.ino:169] esp_zb_app_signal_handler(): ZDO signal: ZDO Config Ready (0x17), status: ESP_FAIL
[  1278][I][Zigbee_LightMeter_Sensor.ino:132] esp_zb_app_signal_handler(): Zigbee stack initialized
[  3649][I][Zigbee_LightMeter_Sensor.ino:138] esp_zb_app_signal_handler(): Start network steering
[  3650][I][Zigbee_LightMeter_Sensor.ino:139] esp_zb_app_signal_handler(): Device started up in non factory-reset mode
[  3650][I][Zigbee_LightMeter_Sensor.ino:144] esp_zb_app_signal_handler(): Device rebooted
[  3651][I][Zigbee_LightMeter_Sensor.ino:114] esp_app_illu_sensor_handler(): Updating illuminance sensor value 3...
[  3651][I][Zigbee_LightMeter_Sensor.ino:115] esp_app_illu_sensor_handler(): 127
[  3652][I][Zigbee_LightMeter_Sensor.ino:253] zb_deep_sleep_start(): [ESP_ZB_DEEP_SLEEP] Start one-shot timer for 5s to enter the deep sleep
[  8653][I][Zigbee_LightMeter_Sensor.ino:260] s_oneshot_timer_callback(): [ESP_ZB_DEEP_SLEEP] Enter deep sleep

with different measurement values, e.g. 14 -> 25 -> 127 -> 145 -> 45 -> etc.

xieqinan commented 5 months ago

@matthiasbuettner159 ,

Alright, it appears that a bind operation is missing for the attribute report. Could you refer to this example to bind the attribute you expect to send to the coordinator? It is similar to the issue mentioned in https://github.com/espressif/esp-zigbee-sdk/issues/344#issuecomment-2162954461.

xieqinan commented 5 months ago

You can also try to proactively send the report using esp_zb_zcl_report_attr_cmd_req() to ensure the Zigbee_LightMeter_Sensor can receive the report command.

matthiasbuettner159 commented 5 months ago

@xieqinan ,

Thanks a lot for your responses and your effort in supporting. I tried fiddling around with the example and your answer in the linked comment.

It is somewhat working now. My solution is to setup the esp_zb_zdo_device_bind_req in ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT. The code is as follows:

case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:
      if (err_status == ESP_OK) {
        log_i("Start network steering");
        log_i("Device started up in %s factory-reset mode", esp_zb_bdb_is_factory_new() ? "" : "non");
        if (esp_zb_bdb_is_factory_new()) {
          log_i("Start network steering");
          esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
        } else {
          log_i("Device rebooted");
          esp_zb_zdo_bind_req_param_t bind_req;
          bind_req.req_dst_addr = esp_zb_get_short_address();
          esp_zb_ieee_address_by_short(bind_req.req_dst_addr, bind_req.src_address);
          bind_req.src_endp = HA_ESP_SENSOR_ENDPOINT;
          bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT;
          bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
          esp_zb_ieee_address_by_short(0x0000, bind_req.dst_address_u.addr_long);
          bind_req.dst_endp = 1;
          esp_zb_zdo_device_bind_req(&bind_req, NULL, NULL);

          log_i("Update value");
          illu_sensor_value_update();
        }
      } else {
        /* commissioning failed */
        log_w("Failed to initialize Zigbee stack (status: %s)", esp_err_to_name(err_status));
      }
      break;

This does eventually report the updated value back to my coordinator. However the whole process takes about 7-10 seconds which is quite long for a battery powered device imo and would like to have it shorter (if possible). These are the logs on my coordinator with timestamps for reference:

Debug 2024-06-13 09:21:09 Device '0x543204fffe11dec0' announced itself
Debug 2024-06-13 09:21:11 Retrieving state of '0x543204fffe11dec0' after reconnect
Debug 2024-06-13 09:21:16 Received Zigbee message from '0x543204fffe11dec0', type 'attributeReport', cluster 'msIlluminanceMeasurement', data '{"measuredValue":32}' from endpoint 10 with groupID 0

I also tried proactively using the esp_zb_zcl_report_attr_cmd_req(), but fail to set it up correctly. This is what I tried adding after the abovementioned illu_sensor_value_update() call:

esp_zb_zcl_report_attr_cmd_t cmd_req;
esp_zb_ieee_address_by_short(0x0000, cmd_req.zcl_basic_cmd.dst_addr_u.addr_long);
cmd_req.zcl_basic_cmd.dst_endpoint = 1;
cmd_req.zcl_basic_cmd.src_endpoint = HA_ESP_SENSOR_ENDPOINT;
cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
cmd_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT;
cmd_req.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE;
cmd_req.attributeID = ESP_ZB_ZCL_ATTR_ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ID;
esp_zb_zcl_report_attr_cmd_req(&cmd_req);

I assume I have not fully understand which addresses and values need to be provided to get it to work. When adding this code to my file no updates are reported as before and I receive the following log after about 60s on the sensor device:

[ 68574][I][Zigbee_LightMeter_Sensor.ino:208] esp_zb_app_signal_handler(): ZDO signal: ZDO Device Unavailable (0x3c), status: ESP_OK

xieqinan commented 5 months ago

@matthiasbuettner159

This does eventually report the updated value back to my coordinator. However the whole process takes about 7-10 seconds which is quite long for a battery powered device imo and would like to have it shorter (if possible).

The report time depends on the configuration of esp_zb_zcl_reporting_info_t reporting_info. If max_interval = 0 and min_interval != 0xffff, there will be no periodic reporting, but change-based reporting will still be operational. If you want periodic reports, set max_interval = 1 and min_interval = 0 so that reports will be generated every second. Alternatively, you can call esp_zb_zcl_report_attr_cmd_req() to send reports proactively.

esp_zb_zcl_report_attr_cmd_t cmd_req;
cmd_req.zcl_basic_cmd.dst_endpoint = 1;
cmd_req.zcl_basic_cmd.src_endpoint = HA_ESP_SENSOR_ENDPOINT;
cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = 0x0000;
cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
cmd_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT;
cmd_req.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE;
cmd_req.attributeID = ESP_ZB_ZCL_ATTR_ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ID;
esp_zb_zcl_report_attr_cmd_req(&cmd_req);
matthiasbuettner159 commented 5 months ago

@xieqinan , Thanks a lot - this did the trick and now my sensor is reporting one value before going back to sleep.

One additional question: How can I add reporting for a second value? I would like to send the current battery voltage alongside the measurement. Trying to add a new attribute to the cluster does not work, (at least it is not showing up on my corrdinator). Do I have to setup a new cluster for this?

jrossouw commented 3 months ago

@matthiasbuettner159 would it be possible for you to post your complete source somewhere? I am trying to do something similar - and report battery voltage/percentage back via the ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG cluster but it does not seem to be working. I am trying to integrate to a Homey Pro.

xieqinan commented 3 months ago

@matthiasbuettner159 @jrossouw ,

How can I add reporting for a second value?

You can call the esp_zb_scheduler_alarm() to set a alarm to send esp_zb_zcl_report_attr_cmd_req(), or refer to this example to configuure the attribute reporting interval.

I would like to send the current battery voltage alongside the measurement. Trying to add a new attribute to the cluster does not work, (at least it is not showing up on my corrdinator). Do I have to setup a new cluster for this?

Yes, you need to add a POWER CONFIG cluster to implement this. Please note that only attributes with REPORT access, as defined by the ZCL specification, can be reported, such as ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID attribute of POWER CONFIG cluster.

jrossouw commented 3 months ago

I have started a github project with the aim to share a working example of a temperature sensor that reports temperature, its batttery voltage - as well as having one config parameter that can be set remotely (say for instance to control the sleep period of the device). This sensor will mostly sleep to extend battery life. When it wakes it will take new sensor reading, report and then go back to sleep. The code can be found here:

https://github.com/jrossouw/esp32c6-temp-sensor

I have not tested the code yet. I don't currently have access to my test setup.

jrossouw commented 3 months ago

I have started with the light sleep example, and updated to add the battery voltage and a temperature sensor. The repository is available here:

https://github.com/jrossouw/esp32c6-zigbee-light

I have a number of issues with this code:

  1. The battery voltage claims the attribute is not reportable:

E (8638) ESP_ZIGBEE_COMMAND: esp_zb_zcl_report_attr_cmd_req(310): This attribute: 0x20 is not reportable!

  1. The code to update the temperature value crashes with an assertion error:

esp_zb_zcl_set_attribute_val(HA_ESP_LIGHT_ENDPOINT, ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, &temperature, false);

  1. Nothing is reported back to my HA router.

@xieqinan could you please have a quick look and see if I am doing anything stupid? I am a novice as this still.

xieqinan commented 3 months ago

@jrossouw ,

  1. The battery voltage claims the attribute is not reportable:

This issue is triggered by the ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID attribute, which lacks REPORT access. You can refer to the figure below. The ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID supports the report access.

image

  1. The code to update the temperature value crashes with an assertion error:

Please also add the minimum and maximum values for the temperature cluster, as they are mandatory requirements.

image

jrossouw commented 3 months ago

@xieqinan thank you for the feedback. The updated code now works perfectly. I have updated my git repository for those who are interested.

xieqinan commented 3 months ago

@matthiasbuettner159 ,

Do you have any other questions regarding this issue?

jrossouw commented 3 months ago

@xieqinan I have one more question. I have a custom cluster with a read/write attribute. I can see this cluster in my home automation router, and the router can send attribute updates to it - but I never receive it on the device. I was expecting the function registered via esp_zb_core_action_handler_register to get called? How do i configure the device to listen for these attribute updates? My code to create the cluster: `

// Create custom cluster/attribute list
esp_zb_cluster_list_t *cluster_list = esp_zb_ep_list_get_ep(ep_list, HA_ESP_SENSOR_ENDPOINT);;
esp_zb_attribute_list_t *esp_zb_eshed_cluster = esp_zb_zcl_attr_list_create(ZB_ZCL_CLUSTER_ID_ESHED_TANK);
ESP_ERROR_CHECK(esp_zb_custom_cluster_add_custom_attr(esp_zb_eshed_cluster, ESHED_TANK_DIAMETER_CONFIG_ATTR_ID,
    ESP_ZB_ZCL_ATTR_TYPE_16BIT, ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, &tankDiameter));
ESP_ERROR_CHECK(esp_zb_custom_cluster_add_custom_attr(esp_zb_eshed_cluster, ESHED_TANK_SENSOR_RANGE_ATTR_ID,
    ESP_ZB_ZCL_ATTR_TYPE_8BIT, ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, &sensorRange));
ESP_ERROR_CHECK(esp_zb_cluster_list_add_custom_cluster(cluster_list, esp_zb_eshed_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE));

` I also then create a bind request for the cluster.

xieqinan commented 3 months ago

@jrossouw ,

How do i configure the device to listen for these attribute updates? My code to create the cluster:

Regarding this issue, I believe that you can see the custom cluster user guide as reference, please feel free to discuss the problems you encountered when you read this user guide.

jrossouw commented 3 months ago

@xieqinan thanks for the feedback - I did manage to get it working. I had incompatible data types between my Automation controller and the end device. Once I corrected that i started receiving the attribute update messages. Thanks for the support.

chshu commented 2 months ago

Closing as the questions are all answered.