espressif / esp-zigbee-sdk

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

esp_zb_zdo_device_unbind_req not working (TZ-906) #351

Closed mundevx closed 4 months ago

mundevx commented 6 months ago

Question

I am using esp_zb_zdo_device_unbind_req to UNBIND the device, and my both device types are ROUTER. Device1 Endpoint1 bind to Device2 Endpoint1 successfully bind the Endpoints. But want to unbind the Device1 Endpoint1 with Device2 Endpoint1 using esp_zb_zdo_device_unbind_req function, it is not working. Then i tested only esp_zb_zdo_device_unbind_req function in another project, and found that using function esp_zb_zdo_device_unbind_req, instead of UNBIND, it BIND the DEVICE1(Switch) with DEVICE2(Light).

Additional context.

No response

xieqinan commented 6 months ago

@mundevx ,

Could you please attach the code tested by you?

mundevx commented 6 months ago

Yes Sure,

void simple_binding_request(esp_zb_ieee_addr_t ieee_addr, uint16_t dst_short_addr, uint8_t dst_endpoint, uint8_t src_endpoint, uint16_t cluster_id){ / bind the on-off light to on-off switch / esp_zb_zdo_bind_req_param_t bind_req; memcpy(&(bind_req.src_address), ieee_addr, sizeof(esp_zb_ieee_addr_t)); bind_req.src_endp = src_endpoint; bind_req.cluster_id = cluster_id; bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED; esp_zb_get_long_address(bind_req.dst_address_u.addr_long); bind_req.dst_endp = dst_endpoint; bind_req.req_dst_addr = dst_short_addr; ESP_LOGI(TAG, "short_addr=0x%x\n", dst_short_addr); ESP_LOGI(TAG, "Try to BIND Light dstep:%d On/Off srcep=%d cluster=%d\n", dst_endpoint, src_endpoint, cluster_id); esp_zb_zdo_device_bind_req(&bind_req, bind_cb, &dst_endpoint); } void simple_unbinding_request(esp_zb_ieee_addr_t ieee_addr, uint16_t dst_short_addr, uint8_t dst_endpoint, uint8_t src_endpoint, uint16_t cluster_id){ / bind the on-off light to on-off switch / esp_zb_zdo_bind_req_param_t bind_req; memcpy(&(bind_req.src_address), ieee_addr, sizeof(esp_zb_ieee_addr_t)); bind_req.src_endp = src_endpoint; bind_req.cluster_id = cluster_id; bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED; esp_zb_get_long_address(bind_req.dst_address_u.addr_long); bind_req.dst_endp = dst_endpoint; bind_req.req_dst_addr = dst_short_addr; ESP_LOGI(TAG, "dst_short_addr=0x%x\n", dst_short_addr); ESP_LOGI(TAG, "Try to UNBIND Light dstep:%d On/Off srcep=%d cluster=%d\n", dst_endpoint, src_endpoint, cluster_id); esp_zb_zdo_device_unbind_req(&bind_req, unbind_cb, &dst_endpoint); }

calling functions as

uint8_t dst_endpoint = 1; uint8_t src_endpoint = 1; simple_binding_request(ieee_addr, dst_short_addr, dst_endpoint, src_endpoint, ESP_ZB_ZCL_CLUSTER_ID_ON_OFF);

simple_unbinding_request(ieee_addr, dst_short_addr, dst_endpoint, src_endpoint, ESP_ZB_ZCL_CLUSTER_ID_ON_OFF);

xieqinan commented 6 months ago

@mundevx ,

I made a simple modification to esp_zb_switch to verify your question. It works as expected. Could you copy and test it with the light example? Please note test it after the network had been formed and this is only a test example.

#include "esp_check.h"
#include "esp_err.h"
#include "string.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ha/esp_zigbee_ha_standard.h"
#include "esp_zb_switch.h"

#if defined ZB_ED_ROLE
#error Define ZB_COORDINATOR_ROLE in idf.py menuconfig to compile light switch source code.
#endif
typedef struct light_bulb_device_params_s {
    esp_zb_ieee_addr_t ieee_addr;
    uint8_t  endpoint;
    uint16_t short_addr;
} light_bulb_device_params_t;

static switch_func_pair_t button_func_pair[] = {
    {GPIO_INPUT_IO_TOGGLE_SWITCH, SWITCH_ONOFF_TOGGLE_CONTROL}
};

static const char *TAG = "ESP_ZB_ON_OFF_SWITCH";

static esp_zb_zdo_bind_req_param_t bind_req;
static void zb_buttons_handler(switch_func_pair_t *button_func_pair)
{
    static uint8_t count = 0;
    esp_zb_lock_acquire(portMAX_DELAY);
    if (count++ % 2) {
        ESP_EARLY_LOGI(TAG, "unbind command");
        esp_zb_zdo_device_unbind_req(&bind_req, NULL, NULL);
    } else {
        ESP_EARLY_LOGI(TAG, "bind command");
        esp_zb_zdo_device_bind_req(&bind_req, NULL, NULL);
    }
    esp_zb_zcl_on_off_cmd_t cmd_req;
    cmd_req.zcl_basic_cmd.src_endpoint = HA_ONOFF_SWITCH_ENDPOINT;
    cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
    cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_TOGGLE_ID;
    ESP_EARLY_LOGI(TAG, "Send 'on_off toggle' command");
    esp_zb_zcl_on_off_cmd_req(&cmd_req);
    esp_zb_lock_release();
}

static esp_err_t deferred_driver_init(void)
{
    ESP_RETURN_ON_FALSE(switch_driver_init(button_func_pair, PAIR_SIZE(button_func_pair), zb_buttons_handler), ESP_FAIL, TAG,
                        "Failed to initialize switch driver");
    return ESP_OK;
}

static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask)
{
    ESP_RETURN_ON_FALSE(esp_zb_bdb_start_top_level_commissioning(mode_mask) == ESP_OK, , TAG, "Failed to start Zigbee bdb commissioning");
}

static void bind_cb(esp_zb_zdp_status_t zdo_status, void *user_ctx)
{
    if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
        ESP_LOGI(TAG, "Bound successfully!");
        if (user_ctx) {
            light_bulb_device_params_t *light = (light_bulb_device_params_t *)user_ctx;
            ESP_LOGI(TAG, "The light originating from address(0x%x) on endpoint(%d)", light->short_addr, light->endpoint);
            free(light);
        }
    }
}

static void user_find_cb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx)
{
    if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
        ESP_LOGI(TAG, "Found light");
        light_bulb_device_params_t *light = (light_bulb_device_params_t *)malloc(sizeof(light_bulb_device_params_t));
        light->endpoint = endpoint;
        light->short_addr = addr;
        esp_zb_ieee_address_by_short(light->short_addr, light->ieee_addr);
        esp_zb_get_long_address(bind_req.src_address);
        bind_req.src_endp = HA_ONOFF_SWITCH_ENDPOINT;
        bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF;
        bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
        memcpy(bind_req.dst_address_u.addr_long, light->ieee_addr, sizeof(esp_zb_ieee_addr_t));
        bind_req.dst_endp = endpoint;
        bind_req.req_dst_addr = esp_zb_get_short_address(); /* TODO: Send bind request to self */
        ESP_LOGI(TAG, "Try to bind On/Off");
        esp_zb_zdo_device_bind_req(&bind_req, bind_cb, (void *)light);
    }
}

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 = *p_sg_p;
    esp_zb_zdo_signal_device_annce_params_t *dev_annce_params = NULL;
    switch (sig_type) {
    case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
        ESP_LOGI(TAG, "Initialize Zigbee stack");
        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) {
            ESP_LOGI(TAG, "Deferred driver initialization %s", deferred_driver_init() ? "failed" : "successful");
            ESP_LOGI(TAG, "Device started up in %s factory-reset mode", esp_zb_bdb_is_factory_new() ? "" : "non");
            if (esp_zb_bdb_is_factory_new()) {
                ESP_LOGI(TAG, "Start network formation");
                esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_FORMATION);
            } else {
                ESP_LOGI(TAG, "Device rebooted");
            }
        } else {
            ESP_LOGE(TAG, "Failed to initialize Zigbee stack (status: %s)", esp_err_to_name(err_status));
        }
        break;
    case ESP_ZB_BDB_SIGNAL_FORMATION:
        if (err_status == ESP_OK) {
            esp_zb_ieee_addr_t extended_pan_id;
            esp_zb_get_extended_pan_id(extended_pan_id);
            ESP_LOGI(TAG, "Formed 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());
            esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
        } else {
            ESP_LOGI(TAG, "Restart network formation (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_FORMATION, 1000);
        }
        break;
    case ESP_ZB_BDB_SIGNAL_STEERING:
        if (err_status == ESP_OK) {
            ESP_LOGI(TAG, "Network steering started");
        }
        break;
    case ESP_ZB_ZDO_SIGNAL_DEVICE_ANNCE:
        dev_annce_params = (esp_zb_zdo_signal_device_annce_params_t *)esp_zb_app_signal_get_params(p_sg_p);
        ESP_LOGI(TAG, "New device commissioned or rejoined (short: 0x%04hx)", dev_annce_params->device_short_addr);
        esp_zb_zdo_match_desc_req_param_t  cmd_req;
        cmd_req.dst_nwk_addr = dev_annce_params->device_short_addr;
        cmd_req.addr_of_interest = dev_annce_params->device_short_addr;
        esp_zb_zdo_find_on_off_light(&cmd_req, user_find_cb, NULL);
        break;
    case ESP_ZB_NWK_SIGNAL_PERMIT_JOIN_STATUS:
        if (err_status == ESP_OK) {
            if (*(uint8_t *)esp_zb_app_signal_get_params(p_sg_p)) {
                ESP_LOGI(TAG, "Network(0x%04hx) is open for %d seconds", esp_zb_get_pan_id(), *(uint8_t *)esp_zb_app_signal_get_params(p_sg_p));
            } else {
                ESP_LOGW(TAG, "Network(0x%04hx) closed, devices joining not allowed.", esp_zb_get_pan_id());
            }
        }
        break;
    default:
        ESP_LOGI(TAG, "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 void esp_zb_task(void *pvParameters)
{
    /* initialize Zigbee stack */
    esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZC_CONFIG();
    esp_zb_init(&zb_nwk_cfg);
    esp_zb_on_off_switch_cfg_t switch_cfg = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG();
    esp_zb_ep_list_t *esp_zb_on_off_switch_ep = esp_zb_on_off_switch_ep_create(HA_ONOFF_SWITCH_ENDPOINT, &switch_cfg);
    esp_zb_device_register(esp_zb_on_off_switch_ep);
    esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
    ESP_ERROR_CHECK(esp_zb_start(false));
    esp_zb_main_loop_iteration();
}

void app_main(void)
{
    esp_zb_platform_config_t config = {
        .radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG(),
        .host_config = ESP_ZB_DEFAULT_HOST_CONFIG(),
    };
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_zb_platform_config(&config));

    xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 5, NULL);
}
mundevx commented 6 months ago

@xieqinan , ok, i will try and let you know. Thanks

mundevx commented 6 months ago

@xieqinan , yes, this code is working. I am checking my code and let you know what was the issue.

mundevx commented 6 months ago

@xieqinan , Please check this code on latest sdk, idf version 5.1.3, - And esp-zboss-lib to v1.3.2 , esp-zigbee-lib to v1.3.2. This code not working correctly and esp_zb_zdo_device_unbind_req not working. I installed idf version 5.1.3 in Visual Studio and esp-idf-v5.1.2 in Espressif IDE. This code works on v5.1.2 and not on version 5.1.3. That why i have issue in my project that i am building in latest idf version 5.1.3.

xieqinan commented 6 months ago

@mundevx ,

I can confirm that I tested the issue using esp-idf v5.1.3, esp-zigbee-lib v1.3.2, and esp-zboss-lib v1.3.2. image

Please note that static esp_zb_zdo_bind_req_param_t bind_req() is a global variable and will not persist after a reboot. Ensure you are using esp-idf v5.1.3 in your project, as the esp-zigbee-sdk libraries are compiled based on this version.

If you still encounter issues, please print all members of bind_req to console before sending the esp_zb_zdo_device_unbind_req() command and attach the log for me.

mundevx commented 6 months ago

@xieqinan

I confirmed, i am using esp-idf v5.1.3, and lib v1.3.2.

Here is logs screenshot for UNBINDING request


I (32657) ESP_ZB_ON_OFF_SWITCH: --------Unbind Request Logs--------

I (32665) ESP_ZB_ON_OFF_SWITCH: dst_short_addr=0x7648

I (32671) ESP_ZB_ON_OFF_SWITCH: dst_endp=1

I (32675) ESP_ZB_ON_OFF_SWITCH: dst_addr_mode=3

I (32681) ESP_ZB_ON_OFF_SWITCH: cluster_id=6

I (32686) ESP_ZB_ON_OFF_SWITCH: src_endp=1

I (32691) ESP_ZB_ON_OFF_SWITCH: IEEE Address: 7C:84:4A:FE:FF:CA:4C:40


And logs for BINDING request


I (32657) ESP_ZB_ON_OFF_SWITCH: --------Bind Request Logs--------

I (32665) ESP_ZB_ON_OFF_SWITCH: dst_short_addr=0x7648

I (32671) ESP_ZB_ON_OFF_SWITCH: dst_endp=1

I (32675) ESP_ZB_ON_OFF_SWITCH: dst_addr_mode=3

I (32681) ESP_ZB_ON_OFF_SWITCH: cluster_id=6

I (32686) ESP_ZB_ON_OFF_SWITCH: src_endp=1

I (32691) ESP_ZB_ON_OFF_SWITCH: IEEE Address: 7C:84:4A:FE:FF:CA:4C:40


here are functions

void simple_binding_request(esp_zb_ieee_addr_t ieee_addr, uint16_t dst_short_addr, uint8_t dst_endpoint, uint8_t src_endpoint) { / bind the on-off light to on-off switch / esp_zb_zdo_bind_req_param_t bind_req; esp_zb_ieee_address_by_short(dst_short_addr, ieee_addr); esp_zb_get_long_address(bind_req.src_address); bind_req.src_endp = src_endpoint; bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF; bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED; memcpy(bind_req.dst_address_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); bind_req.dst_endp = dst_endpoint; bind_req.req_dst_addr = dst_short_addr; ESP_LOGI(TAG, "--------Bind Request Logs--------\n"); ESP_LOGI(TAG, "dst_short_addr=0x%x\n", bind_req.req_dst_addr); ESP_LOGI(TAG, "dst_endp=%d\n", bind_req.dst_endp); ESP_LOGI(TAG, "dst_addr_mode=%d\n", bind_req.dst_addr_mode); ESP_LOGI(TAG, "cluster_id=%d\n", ESP_ZB_ZCL_CLUSTER_ID_ON_OFF); ESP_LOGI(TAG, "src_endp=%d\n", bind_req.src_endp); print_ieee_addr(bind_req.src_address); esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zdo_device_bind_req(&bind_req, bind_cb, NULL); esp_zb_lock_release(); }

void simple_unbinding_request(esp_zb_ieee_addr_t ieee_addr, uint16_t dst_short_addr, uint8_t dst_endpoint, uint8_t src_endpoint){ / unbind the on-off light to on-off switch / esp_zb_zdo_bind_req_param_t unbind_req; esp_zb_ieee_address_by_short(dst_short_addr, ieee_addr); esp_zb_get_long_address(unbind_req.src_address); unbind_req.src_endp = src_endpoint; unbind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF; unbind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED; memcpy(unbind_req.dst_address_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); unbind_req.dst_endp = dst_endpoint; unbind_req.req_dst_addr = dst_short_addr; ESP_LOGI(TAG, "--------Unbind Request Logs--------\n"); ESP_LOGI(TAG, "dst_short_addr=0x%x\n", unbind_req.req_dst_addr); ESP_LOGI(TAG, "dst_endp=%d\n", unbind_req.dst_endp); ESP_LOGI(TAG, "dst_addr_mode=%d\n", unbind_req.dst_addr_mode); ESP_LOGI(TAG, "cluster_id=%d\n", ESP_ZB_ZCL_CLUSTER_ID_ON_OFF); ESP_LOGI(TAG, "src_endp=%d\n", unbind_req.src_endp); print_ieee_addr(unbind_req.src_address); esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zdo_device_unbind_req(&unbind_req, unbind_cb, NULL); esp_zb_lock_release();

}

simple_binding_request function sworks ok and bind with every short address that are connected zigbee on network. but simple_unbinding_request not working. Callback repsonse are success for both unbind_cb and bind_cb

mundevx commented 6 months ago

Hey @xieqinan , I am using destination endpoint present,, You above example code works with ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT.

But instead of cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; use

cmd_req.zcl_basic_cmd.dst_endpoint = 10;  //light endpoint
cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = DST_SHORT_ADDR;

cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; , it will not work.

where DST_SHORT_ADDR is short_address of light device.

xieqinan commented 6 months ago

@mundevx

I confirmed, i am using esp-idf v5.1.3, and lib v1.3.2.

The discussion is chaotic, so let's synchronize a few things first.

mundevx commented 5 months ago

Can the code provided by me work in both esp-idf v5.1.3 and esp-idf v5.1.2? If not, please print all bind_req information for me based on my code (specifically, print bind_req.dst_address_u.addr_long). YES

If the issue is only triggered in your project: YES

The ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT address mode is used when the device has been bound. For the ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT address mode, please refer to this example. Its also not working in my project

xieqinan commented 5 months ago

@mundevx ,

Based on the responses above, the issue should be triggered by the application logic. I believe it would be more effective if you could provide a patch or a simple example for me to debug. Without this, it is difficult for me to identify where the problem is occurring.

lpy4105 commented 4 months ago

Hi @mundevx,

As @xieqinan has explained:

The ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT address mode is used when the device has been bound. For the ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT address mode, please refer to this example.

To conclude, only ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT is related to bindings. If you send commands using ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT, the communication should succeed because you know the "network address" of the peer device. Please also see: https://github.com/espressif/esp-zigbee-sdk/issues/383.

If your issue hasn't been resovled, please feel free to put comments.