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