espressif / esp-zigbee-sdk

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

esp_zb_zdo_nwk_addr_req() not getting short address for devices in the network different than the coordinator (TZ-1251) #467

Closed jkano closed 2 weeks ago

jkano commented 1 month ago

Question

Hi, I'm trying to use the esp_zb_zdo_nwk_addr_req() to get the short address of all the devices currently in the binding table of my end device (esp32-c6). Im using library version 1.5.1.

I have one coordinator, ieee: 0x00158d0000c38703, the esp32-c6 end device, ieee: 0x404ccafffe566270 and two devices with ieee: 0x00158d0000c39956 and 0x00158d00007c6e9e.

To do this at will, I added a button callback function, button_cb so when I press the button once I send the request for the binding table on the device and when I get the response, I iterate on all the entries of the binding table and request the short addresses of them:

This is the part of my code that is involved:

// Nwk address callback function
static void nwk_addr_req_cb(esp_zb_zdp_status_t zdo_status, uint16_t nwk_addr, void *user_ctx)
{
    ESP_LOGI(TAG, "nwk_addr_req_cb(), status: %02x", zdo_status);
    if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS)
    {
        ESP_LOGI(TAG, "got nwk addr: 0x%04x", nwk_addr);
    }
}

// Bind table callback function
static void bind_table_cb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx)
{
    esp_zb_ieee_addr_t ieee_addr;
    esp_zb_get_long_address(ieee_addr);

    uint8_t start_idx = table_info->index;
    esp_zb_zdo_binding_table_record_t *record = table_info->record;

    if (table_info->count == 0)
    {
        ESP_LOGW(TAG, "No records in binding table");
        return;
    }

    ESP_LOGI(TAG, "| Index |       SrcAddr      | SrcEp | Cluster |       DstAddr      | DstEp |");
    for (int i = 0; i < table_info->count; i++)
    {
        switch (record->dst_addr_mode)
        {
        case ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT:
            ESP_LOGW(TAG, "DST_ADDR_MODE_16_GROUP_NO_ENDPOINT not supported");
            break;

        case ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT:
            ESP_LOGI(TAG, "|  %3d  | 0x%016" PRIx64 " |  %3d  | 0x%04hx  | 0x%016" PRIx64 " |  %3d  |",
                     start_idx + i, *(uint64_t *)record->src_address,
                     record->src_endp, record->cluster_id,
                     *(uint64_t *)record->dst_address.addr_long,
                     record->dst_endp);

            esp_zb_zdo_nwk_addr_req_param_t cmd_req;
            cmd_req.dst_nwk_addr = 0;
            memcpy(cmd_req.ieee_addr_of_interest, record->dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));

            cmd_req.request_type = 0x00;
            cmd_req.start_index = 0;

            ESP_LOGI(TAG, "nwk addr req for %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
                     cmd_req.ieee_addr_of_interest[7], cmd_req.ieee_addr_of_interest[6], cmd_req.ieee_addr_of_interest[5],
                     cmd_req.ieee_addr_of_interest[4], cmd_req.ieee_addr_of_interest[3], cmd_req.ieee_addr_of_interest[2],
                     cmd_req.ieee_addr_of_interest[1], cmd_req.ieee_addr_of_interest[0]);

            esp_zb_zdo_nwk_addr_req(&cmd_req, nwk_addr_req_cb, NULL);
            break;

        default:
            break;
        }

        record = record->next;
    }
}

 // Request the binding table when button is pressed
void button_cb(void)
{
   ESP_LOGI(TAG, "Button pressed, requesting binding table");
   esp_zb_zdo_mgmt_bind_param_t req;
   req.dst_addr = esp_zb_get_short_address();
   req.start_index = 0;
   esp_zb_zdo_binding_table_req(&req, bind_table_cb, NULL);
}

I have previously added the coordinator to the binding table.

These are my logs:

I (10:50:18.984) APP: Device is NOT factory reset, joined as 0xbd29   <--- esp32-c6 joined to the coordinator
I (10:50:20.428) APP: Button pressed, requesting binding table
I (10:50:20.430) APP: | Index |       SrcAddr      | SrcEp | Cluster |       DstAddr      | DstEp |
I (10:50:20.434) APP: |    0  | 0x404ccafffe566270 |    1  | 0x0006  | 0x00158d0000c38703 |    1  |
I (10:50:20.444) APP: nwk addr req for 00:15:8d:00:00:c3:87:03
I (10:50:20.512) APP: nwk_addr_req_cb(), status: 00
I (10:50:20.516) APP: got nwk addr: 0x0000

Here I open the network for a new device to join:

I (10:50:35.895) APP: Network (0x50de) is open for 30 seconds
I (10:50:48.047) APP: New device commissioned or rejoined (short: 0xbf1c)     <--- New device joined with addr 0xbf1c
W (10:51:03.413) APP: Network (0x50de) closed, devices joining not allowed.

Now, from my coordinator I send a bind request to this new device, to add it to the binding table.

Now I press the button again to send the requests:

I (10:51:16.538) APP: Button pressed, requesting binding table
I (10:51:16.540) APP: | Index |       SrcAddr      | SrcEp | Cluster |       DstAddr      | DstEp |
I (10:51:16.544) APP: |    0  | 0x404ccafffe566270 |    1  | 0x0006  | 0x00158d0000c38703 |    1  |
I (10:51:16.554) APP: nwk addr req for 00:15:8d:00:00:c3:87:03
I (10:51:16.563) APP: |    1  | 0x404ccafffe566270 |    1  | 0x0006  | 0x00158d0000c39956 |    1  |
I (10:51:16.573) APP: nwk addr req for 00:15:8d:00:00:c3:99:56
I (10:51:16.650) APP: nwk_addr_req_cb(), status: 00
I (10:51:16.654) APP: got nwk addr: 0x0000
I (10:51:16.703) APP: nwk_addr_req_cb(), status: 81

As you can see, for the Coordinator (ieee: 0x00158d0000c38703) the request was ok and it got the nwk addr 0x0000 correctly but for the device that just joined (ieee: 0x00158d0000c39956, short: 0xbf1c) the status is ESP_ZB_ZDP_STATUS_DEVICE_NOT_FOUND.

Now I removed the device from the network and the binding table to add a new device, first opened the network and repeat the steps:

I (10:59:04.222) APP: Network (0x50de) is open for 30 seconds
I (10:59:09.918) APP: New device commissioned or rejoined (short: 0x4f25)    <--- New device joined
I (10:59:30.538) APP: Button pressed, requesting binding table
I (10:59:30.540) APP: | Index |       SrcAddr      | SrcEp | Cluster |       DstAddr      | DstEp |
I (10:59:30.544) APP: |    0  | 0x404ccafffe566270 |    1  | 0x0006  | 0x00158d0000c38703 |    1  |
I (10:59:30.555) APP: nwk addr req for 00:15:8d:00:00:c3:87:03
I (10:59:30.563) APP: |    1  | 0x404ccafffe566270 |    1  | 0x0006  | 0x00158d00007c6e9e |    1  |
I (10:59:30.573) APP: nwk addr req for 00:15:8d:00:00:7c:6e:9e
I (10:59:30.657) APP: nwk_addr_req_cb(), status: 00
I (10:59:30.661) APP: got nwk addr: 0x0000
I (10:59:30.807) APP: nwk_addr_req_cb(), status: 81

Here as you can see, I have the same behavior, for the coordinator it could get the short address but for the other device in the network it couldn't.

Do you know what I may be missing here of if this is a bug?

Thanks!

Additional context.

No response

jkano commented 1 month ago

Also, just for testing, I changed the behavior to send a esp_zb_zdo_ieee_addr_req() to get the ieee address knowing the short address. I know the nwk address of both the devices.

This is the code:

static void ieee_addr_req_cb(esp_zb_zdp_status_t zdo_status, esp_zb_ieee_addr_t ieee_addr, void *user_ctx)
{
    ESP_LOGI(TAG, "ieee_addr_req_cb(), status: %02x", zdo_status);
    if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS)
    {
        ESP_LOGI(TAG, "got ieee addr: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
                 ieee_addr[7], ieee_addr[6], ieee_addr[5],
                 ieee_addr[4], ieee_addr[3], ieee_addr[2],
                 ieee_addr[1], ieee_addr[0]);
    }
}

void button_cb(void)
{
    ESP_LOGI(TAG, "Button pressed, sending ieee request");
    esp_zb_zdo_ieee_addr_req_param_t ieee_req;
    ieee_req.addr_of_interest = 0xbd29;      <--- Here I tested with the device own short address and remote device short addr
    ieee_req.dst_nwk_addr = 0;
    ieee_req.request_type = ZB_ZDO_SINGLE_DEVICE_RESP;
    ieee_req.start_index = 0;

    ESP_LOGI(TAG, "ieee addr req for 0x%04x", ieee_req.addr_of_interest);
    esp_zb_zdo_ieee_addr_req(&ieee_req, ieee_addr_req_cb, NULL);
}

And here are the logs:

I (23) boot: ESP-IDF v5.3.1 2nd stage bootloader
I (23) boot: compile time Oct 23 2024 21:37:15
I (24) boot: chip revision: v0.1
I (26) boot.esp32c6: SPI Speed      : 80MHz
I (31) boot.esp32c6: SPI Mode       : DIO
I (35) boot.esp32c6: SPI Flash Size : 8MB
. . .
I (00:15:16.280) APP: Started in zigbee mode
W (00:15:16.299) APP: Network (0x50de) closed, devices joining not allowed.
I (00:15:16.304) APP: Device is NOT factory reset, joined as 0xbd29
I (00:15:16.748) APP: Button pressed, sending ieee request
I (00:15:16.749) APP: ieee addr req for 0xbd29     <--- Here also tested with other device address which is 0xbf1c
I (00:15:16.868) APP: ieee_addr_req_cb(), status: 81

As you can see, requesting it's own nwk address results in a ESP_ZB_ZDP_STATUS_DEVICE_NOT_FOUND (also requested the other device nwk address and same result).

Also, I tested changing the addr_of_interest to the coordinator one (0x0000) and for it, the request works:

I (23) boot: ESP-IDF v5.3.1 2nd stage bootloader
I (23) boot: compile time Oct 23 2024 21:37:15
I (24) boot: chip revision: v0.1
I (26) boot.esp32c6: SPI Speed      : 80MHz
I (31) boot.esp32c6: SPI Mode       : DIO
I (35) boot.esp32c6: SPI Flash Size : 8MB
. . .
I (00:22:38.978) APP: Started in zigbee mode
W (00:22:38.996) APP: Network (0x50de) closed, devices joining not allowed.
I (00:22:39.001) APP: Device is NOT factory reset, joined as 0xbd29
I (00:22:39.446) APP: Button pressed, sending ieee request
I (00:22:39.446) APP: ieee addr req for 0x0000
I (00:22:39.593) APP: ieee_addr_req_cb(), status: 00
I (00:22:39.593) APP: got ieee addr: 00:15:8d:00:00:c3:87:03

So, I'm not sure what could be happening with these address requests to devices other than the coordinator.

xieqinan commented 1 month ago

Hi,

two devices with ieee: 0x00158d0000c39956 and 0x00158d00007c6e9e.

What is the type of these devices? Are they routers or end devices?

Additionally, what is the purpose of performing the above operation? It seems a bit unusual.

jkano commented 1 month ago

Hi, thanks for the reply.

Both devices are routers.

The use case is the following: The device we are building periodically sends a custom cluster command to all the devices in its binding table, making them do something. In the example I showed you, the periodic part was replaced with the button press, so I can try whenever I want.

But the binding table have the dst address (ieee) and the dst endpoint, so, to send the custom command I need the short address of those devices, so I first need to request the nwk addr and that’s what is not working now.

if there was a way to send a custom cluster command to a ieee address I could use that but I could not find a way to do that (even zboss API says it only supports short address).

xieqinan commented 1 month ago

@jkano ,

Both devices are routers.

The Zigbee device can only provide the zdo_nwk_addr response for itself and its children. Therefore, if you attempt to get the nwk_addr of a router from the coordinator, it will return ESP_ZB_ZDP_STATUS_DEVICE_NOT_FOUND.

The device we are building periodically sends a custom cluster command to all the devices in its binding table, making them do something. In the example I showed you, the periodic part was replaced with the button press, so I can try whenever I want.

What command are you expecting to send? Based on the above description, the attribute reporting mechanism might be more suitable for your project. Please refer to the custom switch/light example.

if there was a way to send a custom cluster command to a ieee address I could use that but I could not find a way to do that (even zboss API says it only supports short address).

You can set the address_mode of the custom command to either ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT or ESP_ZB_APS_ADDR_MODE_64_PRESENT_ENDP_NOT_PRESENT to send commands using the IEEE address.

jkano commented 1 month ago

Hi!

The Zigbee device can only provide the zdo_nwk_addr response for itself and its children. Therefore, if you attempt to get the nwk_addr of a router from the coordinator, it will return ESP_ZB_ZDP_STATUS_DEVICE_NOT_FOUND.

Oh I see.. gonna test this again changing the devices to be end devices to check.

What command are you expecting to send? Based on the above description, the attribute reporting mechanism might be more suitable for your project. Please refer to the custom switch/light example.

Zigbee reporting have its constraints for the payload you can send, the command we are sending is a Custom Cluster command which has a custom payload with multiple values so reporting is not useful in this case.

You can set the address_mode of the custom command to either ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT or ESP_ZB_APS_ADDR_MODE_64_PRESENT_ENDP_NOT_PRESENT to send commands using the IEEE address.

I know I can use esp_zb_zcl_custom_cluster_cmd_req() to send a custom cluster command, but our command has a fixed payload which is an array of different sized values, i.e. uint8_t, uint16_t, uint64_t, so we can't use the normal array types.

I tried to send the custom command like this:

// This test is just an example of a real payload, it contains a uint8_t, uint16_t, uint64_t
uint8_t test[] = {0x8D, 0x00, 0x01, 0xB2, 0x28, 0xBE, 0x01, 0x00, 0x01, 0x28, 0x11};

esp_zb_zcl_custom_cluster_cmd_req_t req_params = {
    .zcl_basic_cmd.dst_endpoint = dst_ep,
    .zcl_basic_cmd.src_endpoint = src_ep,
    .address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT,
    .profile_id = ESP_ZB_AF_HA_PROFILE_ID,
    .cluster_id = custom_cluster_id,
    .custom_cmd_id = custom_cluster_cmd_id,
    .direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV,
};

// Here I use the coordinator ieee address to test
memcpy(req_params.zcl_basic_cmd.dst_addr_u.addr_long, coord_ieee, sizeof(esp_zb_ieee_addr_t));

req_params.data.value = test;
req_params.data.size = sizeof(test);
// Don't know which one to use so I set to invalid
req_params.data.type = ESP_ZB_ZCL_ATTR_TYPE_INVALID;

esp_zb_zcl_custom_cluster_cmd_req(&req_params);

But after sending this command, any of our devices receives it (tested by changing the ieee dst_addr).

So, instead of using the esp_zb_zcl_custom_cluster_cmd_req() I used the zboss API to build the ZCL command manually like this:

ZB_ZCL_CONSTRUCT_SET_FRAME_CONTROL( ... );
cmd_ptr = zb_zcl_start_command_header( ... );

// Use ZB_ZCL_PACKET_PUT_DATAXX to add the payload
ZB_ZCL_PACKET_PUT_DATA8( ... );
ZB_ZCL_PACKET_PUT_DATA16( ... );
ZB_ZCL_PACKET_PUT_DATA8( ... );
ZB_ZCL_PACKET_PUT_DATA32( ... );
ZB_ZCL_PACKET_PUT_DATA64( ... );

// Send frame
esp_zb_lock_acquire(portMAX_DELAY);
esp_err_t err = zb_zcl_finish_and_send_packet(zbuf_id, cmd_ptr, (const zb_addr_u *)(const void *)(&(addr)),
                                              dst_addr_mode, src_ep, dst_ep, profile_id,
                                              cluster_id, NULL);
esp_zb_lock_release();

But this function zb_zcl_finish_and_send_packet() in the documentation says:

// @param dst_addr_mode destination address mode (only @ref ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT and @ref ZB_APS_ADDR_MODE_16_ENDP_PRESENT are supported)

So I'm not sure how I can send a very custom ZCL cluster command to a device using its ieee address.

xieqinan commented 3 weeks ago

@jkano ,

Zigbee reporting have its constraints for the payload you can send, the command we are sending is a Custom Cluster command which has a custom payload with multiple values so reporting is not useful in this case.

Considering the above discussion, the application logic seems quite complex. You want to retrieve the remote device's address from the binding table, which implies that remote devices are already aware of the current device’s address and need to send a bind_req() to it. Then, you would retrieve the short address of the remote devices with zdo_nwk_req() and ultimately send messages using that short address.

However, there might be a simpler approach. If the current device receives the bind_req(), it should also have access to the remote device’s short address. You could use esp_zb_address_short_by_ieee() to obtain this address directly.

Additionally, it seems there might be a more straightforward way to get the remote device’s address. Could you share the reason for obtaining it from the binding table? If possible, please consider closing this issue and opening a separate issue to discuss the background and design logic of your project for further clarification.

jkano commented 2 weeks ago

Hi, I already found a way to send the custom ZCL command, by sending to myself a esp_zb_zdo_binding_table_req() to get the list of devices in the binding table and then using esp_zb_zcl_custom_cluster_cmd_req() to send the custom ZCL command to each one of the devices on the binding table.

Btw, I used ESP_ZB_ZCL_ATTR_TYPE_SET as the data type and added my custom payload, this way the command payload is just as I needed.

Thanks for the ideas. Closing as the main question was answered here:

@jkano ,

Both devices are routers.

The Zigbee device can only provide the zdo_nwk_addr response for itself and its children. Therefore, if you attempt to get the nwk_addr of a router from the coordinator, it will return ESP_ZB_ZDP_STATUS_DEVICE_NOT_FOUND.

The device we are building periodically sends a custom cluster command to all the devices in its binding table, making them do something. In the example I showed you, the periodic part was replaced with the button press, so I can try whenever I want.

What command are you expecting to send? Based on the above description, the attribute reporting mechanism might be more suitable for your project. Please refer to the custom switch/light example.

if there was a way to send a custom cluster command to a ieee address I could use that but I could not find a way to do that (even zboss API says it only supports short address).

You can set the address_mode of the custom command to either ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT or ESP_ZB_APS_ADDR_MODE_64_PRESENT_ENDP_NOT_PRESENT to send commands using the IEEE address.