ESP32-C6 Zigbee seMetering non binding, non reporting

tomfuerstner
Posts: 1
Joined: Mon Sep 29, 2025 7:30 am

ESP32-C6 Zigbee seMetering non binding, non reporting

Postby tomfuerstner » Mon Sep 29, 2025 7:48 am

hi,

these days I'm doing a lot of Zigbee coding on the ESP32-C6. Especially for metering and while the Electric Measurement cluster works well, the Simple Metering cluster gives me a lot of headache. It is simply not possible to get the CurrentSummDelivered ( with its attributes Divisor and Multiplicator) value binded and then reported to the coordinator.

I'm confident that the correct mechanic is used as the haMeasurement cluster with its rmsCurrent, activePower etc. works flawless. I'm also confident that the known challenge of the seMetering cluster with endianess and interger types is addressed the correct way.

What follows is the critical code sequence:

Code: Select all


/* Electrical Measurement convenience adder */
static void try_add_em_attr(esp_zb_attribute_list_t *alist, uint16_t attr_id, void *ptr) {
    esp_err_t rc = esp_zb_electrical_meas_cluster_add_attr(alist, attr_id, ptr);
    if (rc == ESP_ERR_INVALID_ARG) {
        ESP_LOGW(TAG, "EM attr 0x%04X already exists; using SDK default", attr_id);
        return;
    }
    ESP_ERROR_CHECK(rc);
}

/* Ensure REPORTING access bit is set on given attribute (covers SDK-precreated ones) */
static void ensure_attr_reportable(uint16_t cluster_id, uint16_t attr_id) {
    esp_zb_lock_acquire(portMAX_DELAY);
    esp_zb_zcl_attr_t *ad = esp_zb_zcl_get_attribute(EP, cluster_id, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, attr_id);
    if (ad && !(ad->access & ESP_ZB_ZCL_ATTR_ACCESS_REPORTING)) {
        ad->access |= ESP_ZB_ZCL_ATTR_ACCESS_REPORTING;
        ESP_LOGI(TAG, "Forced REPORTING on cl 0x%04X attr 0x%04X", cluster_id, attr_id);
    }
    esp_zb_lock_release();
}

/* ---------- Configure local reporting: exactly every 5 seconds (CSD) ---------- */
static void configure_reporting_metering_every_5s(void) {
    esp_zb_zcl_config_report_cmd_t rc = (esp_zb_zcl_config_report_cmd_t){0};
    rc.zcl_basic_cmd.dst_addr_u.addr_short = esp_zb_get_short_address();
    rc.zcl_basic_cmd.src_endpoint = EP;
    rc.zcl_basic_cmd.dst_endpoint = EP;
    rc.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
    rc.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV;
    rc.clusterID = ESP_ZB_ZCL_CLUSTER_ID_METERING;

    static uint64_t csd_change = 0; /* 0 => heartbeat via max interval */

    esp_zb_zcl_config_report_record_t rec[] = {
        {
            .direction = ESP_ZB_ZCL_REPORT_DIRECTION_SEND,
            .attributeID = ESP_ZB_ZCL_ATTR_METERING_CURRENT_SUMMATION_DELIVERED_ID,
            .attrType = ESP_ZB_ZCL_ATTR_TYPE_U64,
            .min_interval = REPORT_INTERVAL_S,
            .max_interval = REPORT_INTERVAL_S,
            .reportable_change = &csd_change,
        },
    };
    rc.record_number = sizeof(rec)/sizeof(rec[0]);
    rc.record_field  = rec;

    esp_zb_lock_acquire(portMAX_DELAY);
    esp_zb_zcl_config_report_cmd_req(&rc);
    esp_zb_lock_release();

    ESP_LOGI(TAG, "Configured 0x0702/CSD reporting every %ds", REPORT_INTERVAL_S);
}

/* ---------- Bind to coordinator (dest EP fixed to 1) ---------- */
static void bind_cb(esp_zb_zdp_status_t status, void *user_ctx) {
    uint16_t cl = (uint16_t)(uintptr_t)user_ctx;
    if (status == ESP_ZB_ZDP_STATUS_SUCCESS) {
        ESP_LOGI(TAG, "Bind OK: cluster 0x%04X -> coordinator EP %d", cl, COORD_ENDPOINT);
        if (cl == ESP_ZB_ZCL_CLUSTER_ID_METERING) {
            g_csd_u64 = g_sum_wh;
            set_attr(ESP_ZB_ZCL_CLUSTER_ID_METERING,
                     ESP_ZB_ZCL_ATTR_METERING_CURRENT_SUMMATION_DELIVERED_ID, &g_csd_u64);
        }
    } else {
        ESP_LOGW(TAG, "Bind FAIL (%u) cl=0x%04X", (unsigned)status, cl);
    }
}

static void bind_cluster_to_coordinator(uint16_t cluster_id) {
    esp_zb_zdo_bind_req_param_t req = {0};
    req.req_dst_addr = esp_zb_get_short_address(); /* source binding */
    req.src_endp = EP;
    req.dst_endp = COORD_ENDPOINT;
    req.cluster_id = cluster_id;
    req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
    esp_zb_ieee_address_by_short(COORD_SHORT_ADDR, req.dst_address_u.addr_long);
    esp_zb_get_long_address(req.src_address);
    esp_zb_zdo_device_bind_req(&req, bind_cb, (void*)(uintptr_t)cluster_id);
}
Therefore, my two questions:

1. Is the a reference example how to use the seMetering cluster with CurrentSummDelievered?
2. Is the seMetering cluster fully developed?

thanks in advance for potential advice, hints, support, learnings, ...

tom

Who is online

Users browsing this forum: No registered users and 2 guests