14.2.3.6.3 Sending ZDP requests

Zigbee Device Profile (ZDP) requests provide a set of functionality for managing the network, discovering devices and services, etc. The

ZDO_ZdpReq() function issues a ZDP request. A particular action performed by the function is detemined by the reqCluster field in an instance of the ZDO_ZdpReq_t type representing request parameters. Additional request parameters are given in the req.reqPayload field, which is actually a union, so that the same memory is used for parameters specific for different request types.

Since the function is executed asynchronously, request parameters' instance shall be defined in a global scope (or file scope - using the static keyword). Note that an instance of request parameters must not be reused until the confirmation callback is called for the current request, because the callback function uses the pointer to response parameters residing in the instance of the request parameters, as an argument.

The following sections provide some examples of issuing ZDP requests of various types. For more examples explore the code of sample applications provided with the SDK.

Device Discovery requests

Two typical tasks in Zigbee application development are obtaining the extended address of a remote device corresponding to a given short address and the reverse task of discovering the short address of the device with a given extended address. These tasks are accomplished with a help of ZDP requests of IEEE_ADDR_CLID and NWK_ADDR_CLID types, respectively. For example, to obtain the extended address the following code may be used:

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

Implementation of the callback function for this request might look like the following:

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 that the response payload should be converted to the type conforming to the type of the issued request. For that, select appropriate field from the union inside the respPayload field.

Service Discovery requests

Service Discovery stands for procedures of collecting information about network devices and functionality (clusters) they support. Using Service Discovery, the application on the current node is able to find out short addresses of devices that support certain input or output clusters and accomplish some other similar tasks.

An essential ZDP request implementing Service Discovery that is frequently needed in applications is of type MATCH_DESCRIPTOR_CLID. It launches a search for devices that have registered endpoints supporting the specified clusters. The request can be sent either to a specific node identified with the short address or to a broadcast address. The application can specify a list of input and a list of output clusters, although it is rarely useful to point out more than one cluster, because a destination node responds with a list of all its endpoints that support at least one input or output cluster specified in the request. The following code sends 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 default CS_ZDP_RESPONSE_TIMEOUT's value is calculated by the stack 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. So the request will normally reach 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:

//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 leave the network issue a ZDP request of the MGMT_LEAVE_CLID type. The request can also be used to force a remote node to leave the network. A node that has left the network cannot be reached by remote data requests and can not sent data to remote nodes from the application layer. To return to the network the node should issue network start request. To ensure that the same network will be chosen during network scan procedures, the PANID value of the previous network shall be saved before network leave and indicated as predefined.

The following examples illustrates parameters configuration for the MGMT_LEAVE_CLID request which will 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 for asking a remote node to leave. Moreover, it is possible to force all its direct children to leave. For that, parameters shall be configured in a different way as it is shown below:

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

If the rejoin field is set to 1, then a destination node will attempt to join the network again after the network leave is complete. It may be used to force a device to change its parent or join with a different short address.