4.6.3 Sending ZDP Requests

Zigbee Device Profile (ZDP) requests offer a set of functionalities to manage the network, discover devices and services, and more.

The ZDO_ZdpReq() function sends out a ZDP request. The reqCluster field within a ZDO_ZdpReq_t type instance, which represents the request parameters, determines the specific action the function performs. The function accepts additional request parameters in the req.reqPayload field. This field is a union, allowing the same memory space to accommodate parameters for different types of requests.

The user must define the request parameter’s instance in a global or file scope (using the static keyword) because the function operates asynchronously.
Note: The user must not reuse an instance of request parameters until the confirmation callback executes for the current request. This is because the callback function requires the pointer to point to the response parameters that reside in the request parameter’s instance.

The following sections provide some examples of issuing ZDP requests of various types. For additional sample application code examples that come with the SDK, go to wireless_apps_pic32cxbz2_wbz45.

Device Discovery Requests

In Zigbee application development, two common tasks include retrieving the extended address of a remote device using a known short address and finding the short address of a device with a known extended address. The user accomplishes these tasks using ZDP requests of the IEEE_ADDR_CLID and NWK_ADDR_CLID types, respectively. For instance, to retrieve the extended address, the user can employ the following code:

ZDO_IeeeAddrReq_t *ieeeAddrReq = &zdpReq.req.reqPayload.ieeeAddrReq;
zdpReq.ZDO_ZdpResp = zdpIeeeAddrResp; //confirmation callback function
zdpReq.reqCluster = IEEE_ADDR_CLID; //type of request
ieeeAddrReq->nwkAddrOfInterest = nwkAddr; //the destination short address
ieeeAddrReq->reqType = SINGLE_RESPONSE_REQUESTTYPE; //indicate that response is expected from//one node only - and not from its children
ieeeAddrReq->startIndex = 0;
ZDO_ZdpReq()(&zdpReq); //send the request
The implementation of the callback function for this request appears as follows:
static void zdpIeeeAddrResp(ZDO_ZdpResp_t *resp)
{
  //Convert the response to the appropriate type
  ZDO_IeeeAddrResp_t *ieeeAddrResp = (ZDO_IeeeAddrResp_t *) &resp->respPayload.ieeeAddrResp;
  //Check whether the operation is a success
  if (ZDO_SUCCESS_STATUS == resp->respPayload.status)
  {
    //The desired extended address is contained in ieeeAddrResp->ieeeAddrRemote field
    ... //Perform actions that required obtaining the address
     //Post a task to notify the stack that for an appropriate task to be executed
     SYS_PostTask(...);
  }
  else
  {
    //Execute required logic, e.g. perform IEEE request again
  }
}
Note: The application must convert the response payload to a type that conforms to the type of the issued request. To do this, select the appropriate field from the union within the respPayload field.

Service Discovery Requests

Service discovery stands for the procedures of collecting information about the network devices and functionality (clusters) they support. With service discovery, the application on the current node discovers short addresses of devices that support certain input or output clusters and accomplish some other similar tasks.

A crucial ZDP request for implementing service discovery, commonly required in applications, is the MATCH_DESCRIPTOR_CLID type. This request initiates a search for devices with registered endpoints that support the designated clusters. The application sends the request to a particular node using its short address or to a broadcast address. While the application can list multiple input and output clusters, it typically proves unnecessary to mention more than one cluster. This is because the target node replies with a list of all its endpoints that support any of the input or output clusters mentioned in the request.

The following code issues a match descriptor request:

//define global variables
static ZDO_ZdpReq_t zdpReq;
...
ZDO_MatchDescReq_t *matchDescReq = &zdpReq.req.reqPayload.matchDescReq;

zdpReq.ZDO_ZdpResp = zdpMatchDescResp; //confirmation callback
zdpReq.reqCluster = MATCH_DESCRIPTOR_CLID; //set request type

matchDescReq->nwkAddrOfInterest = CPU_TO_LE16(BROADCAST_SHORT_ADDRESS);
matchDescReq->profileId = CPU_TO_LE16(APP_PROFILE); //set profile ID
matchDescReq->numInClusters = 1; //number of clusters
matchDescReq->inClusterList[0] = APP_CLUSTER; //set cluster ID
ZDO_ZdpReq(&zdpReq); //send the request

Each reply from a discovered device received by the request's originator causes the confirmation callback to be called. As soon as the request is issued, the stack starts a timer and waits for replies during a period of time specified in the CS_ZDP_RESPONSE_TIMEOUT parameter. When the timer elapses, the stack fires the callback with the ZDO_CMD_COMPLETED_STATUS status code. The stack calculates the default CS_ZDP_RESPONSE_TIMEOUT value in such a way that the devices sleeping at the moment the request is sent are not expected to have time to awake and answer the request. The request reaches only active devices.

Each response contains the short address, the number of matching endpoints and the list of matching endpoints. The confirmation callback’s implementation can be like this:

When the request’s originator receives a reply from a discovered device, it triggers the confirmation callback. The stack initiates a timer immediately after issuing the request and waits for responses within the timeframe set by the CS_ZDP_RESPONSE_TIMEOUT parameter. After the timeframe elapses, the stack activates the callback with the ZDO_CMD_COMPLETED_STATUS status code. The stack calculates the default value of CS_ZDP_RESPONSE_TIMEOUT to exclude devices that are asleep at the time of the request as they are unlikely to wake up and respond in time. Therefore, the request typically reaches only active devices.

Each response includes the short address, the number of matching endpoints and the list of those endpoints. The implementation of the confirmation callback looks as follows:

//A callback function for the ZDP match descriptor request
static void zdpMatchDescResp(ZDO_ZdpResp_t *resp)
{
  ZDO_MatchDescResp_t *matchResp = &resp->respPayload.matchDescResp;
  if (ZDO_CMD_COMPLETED_STATUS == resp->respPayload.status) 
  {
    //timeout has expired; this is the last time the callback is called
    //for the current request
  }
  else if (ZDO_SUCCESS_STATUS == resp->respPayload.status)
 {
    //process another response from the network: for example, the application
    //can immediately send a ZDP request to obtain the extended address of the device
    doIeeeAddrReq();
  }
  else {
    //process failure statuses
  }
}

Leaving the Network

To force a device to exit the network, send a ZDP request of the MGMT_LEAVE_CLID type. This request also serves to make a remote node leave the network. A node that exits the network becomes unreachable by remote data requests and cannot send data to remote nodes from the application layer. To rejoin the network, the node must initiate a network start request. To increase the likelihood of selecting the same network during network scan procedures, save the PANID value of the previous network before exiting and specify it as predefined during the rejoining process.

The following example shows how to configure parameters for the MGMT_LEAVE_CLID request to force the current node to leave the network:

static ZDO_ZdpReq_t zdpLeaveReq; //globally defined variable
...
//set corresponding cluster ID 
zdpLeaveReq.reqCluster = MGMT_LEAVE_CLID; 
zdpLeaveReq.dstAddrMode = EXT_ADDR_MODE;
zdpLeaveReq.dstExtAddr = 0; // for own node address shall be 0
zdpLeaveReq.ZDO_ZdpResp = ZDO_ZdpLeaveResp; // callback//for own node address shall be 0 
zdpLeaveReq.req.reqPayload.mgmtLeaveReq.deviceAddr = 0;
//specify whether to force children leave or not 
zdpLeaveReq.req.reqPayload.mgmtLeaveReq.removeChildren = 0;

//specify whether to perform rejoin procedure after network leave 
zdpLeaveReq.req.reqPayload.mgmtLeaveReq.rejoin = 0;
ZDO_ZdpReq(&zdpLeaveReq); // request for network leave
The request can also be used to ask a remote node to leave the network. In addition, the user can configure it to compel all of its direct children to exit as well. For this purpose, the parameters must be set differently:
static ZDO_ZdpReq_t zdpLeaveReq; //globally defined variable
...
//set corresponding cluster ID 
zdpLeaveReq.reqCluster = MGMT_LEAVE_CLID; 
zdpLeaveReq.dstAddrMode = EXT_ADDR_MODE;
zdpLeaveReq.dstExtAddr = DST_EXT_ADDRESS; //for own node address shall be 0
zdpLeaveReq.ZDO_ZdpResp = ZDO_ZdpLeaveResp; //confirmation callback//for own node should be 0 (or equal own address)
zdpLeaveReq.req.reqPayload.mgmtLeaveReq.deviceAddr = DST_EXT_ADDRESS;
//specify whether to force children leave or not 
zdpLeaveReq.req.reqPayload.mgmtLeaveReq.removeChildren = 1;

//specify whether to perform rejoin procedure after network leave
zdpLeaveReq.req.reqPayload.mgmtLeaveReq.rejoin = 0;
ZDO_ZdpReq(&zdpLeaveReq); // request for network leave
Note: If the rejoin field is set to 1, then a destination node attempts to join the network again after the completing the network leave process. Use this to force a device to change its parent or to join with a different short address.