This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Binding an nrf52 Zigbee light switch to a light bulb

I have been playing with an Ikea Tradfri E1812 button, a Philips Hue bulb, and zigbee2mqtt.  I see that when I bind the button's OnOff output cluster to the bulb's OnOff input cluster, the sniffed traffic indicates that the ZC sends a Bind Request to the button indicating the OnOff cluster and the source/dest endpoints + addresses.  After this binding request succeeds, subsequent button presses send out a unicast ON command directly to the light bulb.  So the binding operation alters the internal state of the Ikea button such that it knows to target the one specific light bulb that it is bound to.

I would like to make a similar button using the nrf52840.  But I'm not sure how to handle the binding part.  The provided light_switch example just iterates through the network looking for any available light bulb.  I don't want to do it this way, because I have over a dozen light bulbs on this Zigbee network and I don't want the button to randomly pick one of them to turn on.  Instead, I want my button to only target the one light bulb that it was explicitly bound to, through the controller's UI.

Is my application code supposed to query the binding table from zboss in order to see which device(s) to send its commands to?  Is there an example of how to do this?

Parents
  • Hi,

    Which light bulb to send the On/Off commands to is decided by the short address and endpoint passed to the macro ZB_ZCL_ON_OFF_SEND_REQ. You do not necessarily need to create a binding between the light bulb and light switch. As long as the light switch has found the light bulb and knows its short address and endpoint you can use this as parameters in ZB_ZCL_ON_OFF_SEND_REQ to unicast On/Off commands. 

    If you still want to create a binding between the two you can use zb_zdo_bind_req() to send a bind request. You need to create and fill the bind request parameters, zb_zdo_bind_req_param_t, which will be passed to the bind request. If you want each button to be uniquely binded to one light bulb then each button must have its own endpoint.

    Best regards,

    Marte

Reply
  • Hi,

    Which light bulb to send the On/Off commands to is decided by the short address and endpoint passed to the macro ZB_ZCL_ON_OFF_SEND_REQ. You do not necessarily need to create a binding between the light bulb and light switch. As long as the light switch has found the light bulb and knows its short address and endpoint you can use this as parameters in ZB_ZCL_ON_OFF_SEND_REQ to unicast On/Off commands. 

    If you still want to create a binding between the two you can use zb_zdo_bind_req() to send a bind request. You need to create and fill the bind request parameters, zb_zdo_bind_req_param_t, which will be passed to the bind request. If you want each button to be uniquely binded to one light bulb then each button must have its own endpoint.

    Best regards,

    Marte

Children
  • I want the controller to create the bind request.  Then I want my nRF52840 based switch to accept the binding, and obtain the bulb's address from the binding table to pass to ZB_ZCL_ON_OFF_SEND_REQ() so that it behaves just like the Ikea button.

    I do not want my switch to have to find the light bulb on its own, because a switch has a very limited UI compared to the controller's web interface.

    What is the best way to read the local binding table so my nRF52840 can find the address of the bound light bulb(s)?

    Do I need to do anything special to allow the controller to bind my nRF52840 to a light bulb?  Or will ZBOSS automatically accept incoming binding requests?

  • Hi,

    When you were testing with Ikea Tradfri E1812 button and Philips Hue bulb, did you see whether they sent a regular bind request or if it were an end device bind? If it were an end device bind request that you can use zb_end_device_bind_req() to get a similar behavior. 

    mytzyiay said:
    What is the best way to read the local binding table so my nRF52840 can find the address of the bound light bulb(s)?

    There is no way to get the binding table on a local device. You can use zb_zdo_mgmt_bind_req() to send a Mgmt_Bind_req to a remote device (or the device can send it to itself) to get the binding table. The function zb_zdo_find_bind_src() can be used to check if there are any binding table entries on the specific endpoint and cluster ID, but not to get the binding table itself. 

    One way to solve this is to handle it locally on your device when you send a bind request. If you are using a normal bind request you must know the address, endpoint, and cluster in order to send the bind request, so the device will already have this information. When you call zb_zdo_bind_req() to send a bind request the second parameter is the callback function that should be called when the device gets a bind response. This response does not contain much, but it does contain a parameter for whether the bind was successful or not, so if it were successful you know that you were able to create a binding with that specific device and endpoint. You can see an example of how I have done binding in bind_req() and zb_bind_callback() here: https://github.com/martelmy/Zigbee-examples-nRF5-SDK/blob/main/bind_req_cmd/main.c#L147.

    Best regards,

    Marte

  • The function zb_zdo_find_bind_src() can be used to check if there are any binding table entries on the specific endpoint and cluster ID, but not to get the binding table itself. 

    That could be helpful if it actually returned the destination address + endpoint (so I could use the binding table entry for something), but I don't understand why it would be useful if it's only returning a boolean?

    One way to solve this is to handle it locally on your device when you send a bind request. If you are using a normal bind request you must know the address, endpoint, and cluster in order to send the bind request, so the device will already have this information.

    Right, but my device (a simple light switch with no LCD or other UI) doesn't send the binding request.  Instead, I rely on the controller, which has a nice rich web GUI that lets users perform bindings using human-readable device names / room locations and other contextual clues.

    There is no way to get the binding table on a local device.

    The binding table is stored by ZBOSS in flash, right?

    What is the point of storing this binding table if there is no way to use it?  It seems like the API may be incomplete.

    Also it looks like there is an addressing mode that references the binding table ( https://devzone.nordicsemi.com/f/nordic-q-a/85840/is-zb_aps_addr_mode_bind_tbl_id-actually-supported ) but I wasn't able to get it to work.  Do we have any examples or additional information on what this is intended to do?

  • Hi,

    mytzyiay said:

    The binding table is stored by ZBOSS in flash, right?

    What is the point of storing this binding table if there is no way to use it?  It seems like the API may be incomplete.

    I apologize, it seems like I was mistaken. While testing this last week I was not able to get the binding table entries. However, it seemed to be some error on my end, as today I was able to get the source and destination binding tables on both light switch and light bulb.

    The binding table is stored in gc_aps_bind_src_table and gc_aps_bind_dst_table.

    These functions are what I used to test, and will print the binding table entries:

    void get_src_bind_table()
    {
        uint32_t i;
    	zb_uint16_t addr;
    	LOG_INF("Source bind table");
        for (i = 0; i < gc_aps_bind_src_table_size; i++)
        {
            zb_address_short_by_ref(&addr, gc_aps_bind_src_table[i].src_addr);
    		if(gc_aps_bind_src_table[i].src_end!=0){
            	LOG_INF("short addr: 0x%04x", addr);
            	LOG_INF("ep: %d cluster: 0x%04hx", 
                    	gc_aps_bind_src_table[i].src_end, gc_aps_bind_src_table[i].cluster_id);
    		}
        }
    }
    
    void get_dst_bind_table()
    {
        uint32_t i;
    	zb_uint16_t addr;
    	LOG_INF("Destination bind table");
        for (i = 0; i < gc_aps_bind_dst_table_size; i++)
        {
            zb_address_short_by_ref(&addr, gc_aps_bind_dst_table[i].u.long_addr.dst_addr);
    		if(gc_aps_bind_dst_table[i].u.long_addr.dst_end!=0){
    			LOG_INF("short addr: 0x%04x", addr);
            	LOG_INF("ep: %d cluster: 0x%04hx", 
                    	gc_aps_bind_dst_table[i].u.long_addr.dst_end, gc_aps_bind_src_table[i].cluster_id);
            	LOG_INF("group addr: 0x%04hx", gc_aps_bind_dst_table[i].u.group_addr);
    		}
        }
    }
    

    Calling these functions prints the following:

    On the light switch:

    Source bind table
    short addr: 0xc417
    ep: 1 cluster: 0x0006

    Destination bind table
    short addr: 0x7b8c
    ep: 10 cluster: 0x0006
    group addr: 0x0a09

    On the light bulb:

    Source bind table
    short addr: 0x7b8c
    ep: 10 cluster: 0x0006

    Destination bind table
    short addr: 0xc417
    ep: 1 cluster: 0x0006
    group addr: 0x0110

    When testing this I created the bindings between the switch and bulb from the coordinator (Zigbee Shell) in order for it to be as close to your use case as possible.

    Please note that in my functions I have a check on the endpoint. This is because the empty entries are filled up with 0x0000 and 0, so without this check you would get an output filled with the empty entries as well:

    Destination bind table
    short addr: 0x7b8c
    ep: 10 cluster: 0x0006
    group addr: 0x0a09
    short addr: 0x0000
    ep: 0 cluster: 0x0000
    group addr: 0x0000
    short addr: 0x0000
    ep: 0 cluster: 0x0000
    group addr: 0x0000

    Endpoint 0 is reserved for ZDO, so if the endpoint in the binding table is 0 you know what this means the entry is empty.

    Best regards,

    Marte

  • Hmm, seems a bit quirky for me.  On unbind, the entry isn't removed until the next power cycle, and unbinding a group doesn't even work at all.

    However this is good enough for my current purposes.  Thanks for the help!

Related