I created a simple light switch device that is supposed to work with zigbee2mqtt server as a coordinator. So far my device can join the network, and report on/off attribute change. Everything looks clear so far.
Currently I am trying to understand how binding works, and have a lot of questions. My use case is pretty straightforward: I want my device (which is basically a switch) to send On/Off commands to a bound device (another switch).
First off, I am not using BDB Find and Bind procedures. The coordinator (zigbee2mqtt) sends my device a very specific ZPS_EVENT_ZDO_BIND message, that explicitly requests 'please bind device endpoint #1 OnOff cluster to the other device 0x0123456789ABCDEF endpoint #2'. From what I see in BDB API there is no such API to bind 2 specific devices - instead it runs find and bind procedures on initiator and some target, without providing a possibility to specify which target and which cluster I would like to bind.
I tried to play with binding functions provided by a low level zigbee stack, such as using ZPS_eAplZdoBind(). My first confusion is that ZPS_EVENT_ZDO_BIND message does not deliver ClusterID to my code, while I clearly see clusterID in the Bind Request I am seeing in the sniffer.
Anyhow, My code currently looks like this (I hardcoded the cluster ID):
void handleZdoBindEvent(ZPS_tsAfZdoBindEvent * pEvent)
{
uint16 shortAddr = pEvent->u8DstAddrMode == ZPS_E_ADDR_MODE_IEEE ?
ZPS_u16AplZdoLookupAddr(pEvent->uDstAddr.u64Addr) :
pEvent->uDstAddr.u16Addr;
uint64 ieeeAddr = pEvent->u8DstAddrMode == ZPS_E_ADDR_MODE_IEEE ?
pEvent->uDstAddr.u64Addr :
ZPS_u64AplZdoLookupIeeeAddr(pEvent->uDstAddr.u16Addr);
ZPS_teStatus status = ZPS_eAplZdoBind(GENERAL_CLUSTER_ID_ONOFF,
pEvent->u8SrcEp,
shortAddr,
ieeeAddr,
pEvent->u8DstEp);
DBG_vPrintf(TRUE, "Binding SrcEP=%d to DstEP=%d Status=%d\n", pEvent->u8SrcEp, pEvent->u8DstEp, status);
}
void handleZdoEvents(ZPS_tsAfEvent* psStackEvent)
{
...
switch(psStackEvent->eType)
...
case ZPS_EVENT_ZDO_BIND:
handleZdoBindEvent(&psStackEvent->uEvent.sZdoBindEvent);
break;
The ZPS_eAplZdoBind() function returns success code. I see a record in binding table
Binding table (size=16)
AddrOrLkUp=0001 ClusterID=0006 addrMode=3 SrcEP=2 DstEP=1
Unfortunately, despite binding was ok, my device does not trigger any target device actions. Moreover the only thing I see in the sniffer is that device just reports its status change to the coordinator, but no other messages to any other devices
Just to be clear, my device reports switch state change as follows:
void reportStateChange()
{
// Destination address - 0x0000 (coordinator)
tsZCL_Address addr;
addr.uAddress.u16DestinationAddress = 0x0000;
addr.eAddressMode = E_ZCL_AM_SHORT;
DBG_vPrintf(TRUE, "Reporting attribute EP=%d value=%d... ", getEndpointId(), sSwitch.sOnOffServerCluster.bOnOff);
PDUM_thAPduInstance myPDUM_thAPduInstance = hZCL_AllocateAPduInstance();
teZCL_Status status = eZCL_ReportAttribute(&addr,
GENERAL_CLUSTER_ID_ONOFF,
E_CLD_ONOFF_ATTR_ID_ONOFF,
getEndpointId(),
1, myPDUM_thAPduInstance);
PDUM_eAPduFreeAPduInstance(myPDUM_thAPduInstance);
DBG_vPrintf(TRUE, "status: %02x\n", status);
}
What am I doing wrong here? Maybe I should explicitly report state change to all bound devices somehow? From what I see eZCL_ReportAttribute() just packs the report to a PDU instance and send to the coordinator - there is no code that is processing binding table and sending bound devices any other reports.
Any other recommendations?
My code is here, if this helps
Hi @grafalex,
I hope you are doing great.
I have been reading about the issue that you are facing.
Could you please provide the sniffer log that you have about this behavior?
What is the AN that you are working on?
Regards,
Mario
Hello, @mario_castaneda,
Thank you for your reply. I have some progress on this, and already solved a few issues in this code over the last couple weeks:
- My switch should send OnOff commands to control other devices, not reports. This is vaguely mentioned in the documentation, but without an accent.
- The switch should have a client OnOff cluster to be able to send commands to other devices
- Commands should be sent with E_ZCL_AM_BOUND addressing mode, and not E_ZCL_AM_SHORT
After these changes my device is now able to control other OnOff device on the network (smart switch, relay, or light).
I am using JN-UG-3113, JN-UG-3114, and JN-UG-3115 as a source of information, as well as Zigbee Specifications. Perhaps this is described in some AN document - I'll appreciate if you could point me to the one.
At the same time I am still confused with NXP Zigbee API. Here is a dump of Bind Request the coordinator is sending to my device
In this request you can clearly see Cluster ID field. And this ID should be eventually passed to ZPS_eAplZdoBind(). Unfortunately Zigbee Stack does not deliver this cluster ID to the ZDO handler. There is simply no such a field in ZPS_tsAfZdoBindEvent structure
typedef struct {
ZPS_tuAddress uDstAddr;
uint8 u8DstAddrMode;
uint8 u8SrcEp;
uint8 u8DstEp;
} ZPS_tsAfZdoBindEvent;
Is there a way to obtain ClusterID value included in the Bind Request message?
Hi @grafalex,
I am not sure if I am following your request.
You are looking for the Bind Request Cluster ID? Am I right? This will be 0x0021 by the spec.
Regards,
Mario
Hi @mario_castaneda !
Let me rephrase. I am talking about ID of the cluster to bind. E.g. My device may implement various clusters - On/Off, thermometer, OTA, but I would like to bind specifically On/Off one.
As per Zigbee specification Bind request message looks like this
As you clearly see, there is ClusterID field. In the sniffer screenshot you can see this field as well (set to 0x0006 On/Off). From what I see this low level zigbee message is converted to ZPS_tsAfZdoBindEvent structure, that has everything, except for Cluster ID.
I even disassembled the stack code, and looks like parsing low level Bind request message and converting it to ZPS_tsAfZdoBindEvent happens in bAplZdoBindUnbindServerProcessApdu(). Unfortunately this function comes without sources, so I cannot fix it myself.
So the question is: would it be possible to add ClusterID field to ZPS_tsAfZdoBindEvent? You have all needed information for this already.
Hi @grafalex,
Please look at the next event you could get the Cluster Id that you are looking for.
case BDB_EVENT_FB_CHECK_BEFORE_BINDING_CLUSTER_FOR_TARGET:
DBG_vPrintf(TRACE_APP_BDB,"APP-BDB: Check For Binding Cluster %04x\n",
psBdbEvent->uEventData.psFindAndBindEvent->uEvent.u16ClusterId);
break;
Regards,
Mario