This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2674P10: Reporting Issues with Large Message Size and Fragmentation with Z-Stack

Part Number: CC2674P10
Other Parts Discussed in Thread: Z-STACK,

Tool/software:

Hello,

I am working with the CC2674P10 microcontroller using Z-Stack from the SimpleLink CC13xx/CC26xx SDK version 7.40.00.77.

In my project, I am trying to enable reporting from an end device to a coordinator. I have added the ZCL_CLUSTER_ID_MS_ELECTRICAL_MEASUREMENT = 0x0b04 cluster, with several reportable attributes. I created the binding between the end device and the coordinator, but I do not see any reporting happening on the coordinator.

When debugging in the bdb_event_loop, specifically in the bdb_RepReport function (from bdb_reporting.c), I noticed that I am periodically sending reporting commands via the function:

zcl_StackSendReportCmd(clusterEndpointItem->endpoint, &dstAddr, 
                       clusterEndpointItem->cluster, pReportCmd, 
                       ZCL_FRAME_SERVER_CLIENT_DIR, 
                       BDB_REPORTING_DISABLE_DEFAULT_RSP, 
                       zcl_getFrameCounter());


However, nothing is received on the coordinator side.

Digging further into the AF_DataRequest function (in af.c), I see the following code:

if (len > afDataReqMTU(&mtu)) {
  if (apsfSendFragmented) {
    stat = (*apsfSendFragmented)(&req);
  } else {
    stat = afStatus_INVALID_PARAMETER;
  }
} else {
  stat = APSDE_DataReq(&req);
}


When I calculate the reporting payload size for the Electrical Measurement cluster (ZCL_CLUSTER_ID_MS_ELECTRICAL_MEASUREMENT), (which is the  ∑(of all reportable attribute data types' lengths)  + (2 bytes for attribute ID + 1 byte for data type) * number of reportable attributes), it reaches about 90 bytes. The afDataReqMTU(&mtu) function returns a maximum size of 82 bytes, so it always calls the (*apsfSendFragmented)(&req) function (whose source code I cannot access), which returns stat = 0x02 (ZInvalidParameter).

I suspected the issue might be related to fragmentation. I tried adding the ZIGBEE_FRAGMENTATION compile option, but unfortunately, the issue persists.

When I reduce the number of reportable attributes to keep the size below afDataReqMTU(&mtu), it goes through the APSDE_DataReq(&req) function, and everything works as expected, with the coordinator successfully receiving the reporting message.

I am looking for a solution to enable reporting even with large message sizes. How can I resolve this fragmentation issue or allow larger reporting messages to be sent?

Any help or suggestions would be greatly appreciated!




  • Hi,

    Thank you for reaching out and providing all of this information.  I've looked into the apsfSendFragmented source code which points to APSF_SendFragmented.  This function only returns afStatus_INVALID_PARAMETER depending on the destination address mode:

    afStatus_t APSF_SendFragmented(APSDE_DataReq_t *pReq)
    {
      APSF_TxObj_t *pTx;
      afDataReqMTU_t mtu;
      uint8_t blockSize;
      uint16_t  dstAddr;
    
      if ( pReq->dstAddr.addrMode == AddrNotPresent ||
           pReq->dstAddr.addrMode == AddrGroup      ||
           NLME_IsAddressBroadcast(pReq->dstAddr.addr.shortAddr) != ADDR_NOT_BCAST )
      {
        return afStatus_INVALID_PARAMETER;
      }
    
      // Find out the destination short address
      if ( pReq->dstAddr.addrMode == Addr64Bit )
      {
        if ( APSME_LookupNwkAddr( pReq->dstAddr.addr.extAddr, &dstAddr ) == FALSE )
        {
          return afStatus_INVALID_PARAMETER;
        }
      }
      else
      {
        dstAddr = pReq->dstAddr.addr.shortAddr;
      }

    As is the case with bdb_RepReport, it will set dstAddr.addrMode = (afAddrMode_t)AddrNotPresent given indirect addressing is intended to use the binding table information stored.  This does not appear to be supported by fragmentation since it will return the fail status, and only short destination address are allowed.

    I will reach out to the Zigbee R&D Team to further understand why this condition is set inside of the APS fragmentation layer.  Meanwhile, are you able to determine the short address of your destination devices and modify bdb_RepReport accordingly to meet this requirement?  Or is it possible to spread the attributes across multiple report commands so that fragmentation is avoided?

    Regards,
    Ryan

  • Thanks for your quick response, Ryan.

    I’ve modified the bdb_RepReport function. When the payload size for the reporting cluster exceeds the MTU (Max Transport Unit) limit, I retrieve the destination device’s MAC address from the binding table, if a binding exists for the specific cluster, and switch the address from AddrNotPresent to Addr64Bit. This ensures proper transmission of the report and resolves the issue of fragmentation with large messages.


    Here's the bdb_RepReport function after modification:

    static void bdb_RepReport( uint8_t specificCLusterEndpointIndex )
    {
      afAddrType_t dstAddr;
      zclReportCmd_t *pReportCmd = NULL;
      uint8_t i;
      uint16_t dataLen = (1 + 1 + 1); // frame control + transaction seq num + cmd ID

      bdbReportAttrClusterEndpoint_t* clusterEndpointItem = NULL;
      if( specificCLusterEndpointIndex == BDBREPORTING_INVALIDINDEX )
      {
        if( bdb_reportingNextClusterEndpointIndex < bdb_reportingClusterEndpointArrayCount )
        {
          clusterEndpointItem = &(bdb_reportingClusterEndpointArray[bdb_reportingNextClusterEndpointIndex]);
        }
      }
      else
      {
        clusterEndpointItem = &(bdb_reportingClusterEndpointArray[specificCLusterEndpointIndex]);
      }

      // actually send the report
      if( (clusterEndpointItem != NULL) &&
          (clusterEndpointItem->consolidatedMaxReportInt != ZCL_REPORTING_OFF) &&
          (clusterEndpointItem->attrLinkedList.numItems)
        )
      {
        uint8_t *pAttrData = NULL;
        uint8_t *pAttrDataTemp = NULL;
        dstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
        dstAddr.addr.shortAddr = 0;
        dstAddr.endPoint = clusterEndpointItem->endpoint;
        dstAddr.panId = _NIB.nwkPanId;

        pReportCmd = OsalPort_malloc( sizeof( zclReportCmd_t ) + (clusterEndpointItem->attrLinkedList.numItems * sizeof( zclReport_t )) );
        pAttrData = OsalPort_malloc(clusterEndpointItem->attrLinkedList.numItems * BDBREPORTING_MAX_ANALOG_ATTR_SIZE);
        if ( (pReportCmd != NULL) && (pAttrData != NULL) )
        {
          pAttrDataTemp = pAttrData;
          pReportCmd->numAttr = clusterEndpointItem->attrLinkedList.numItems;
          for ( i = 0; i < clusterEndpointItem->attrLinkedList.numItems; ++ i )
          {
            pReportCmd->attrList[i].attrID   = 0xFFFF;
            pReportCmd->attrList[i].dataType = 0xFF;
            pReportCmd->attrList[i].attrData = NULL;

            bdbLinkedListAttrItem_t* attrListItem = bdb_linkedListAttrGetAtIndex( &clusterEndpointItem->attrLinkedList, i );
            if(attrListItem!=NULL)
            {
              zclAttribute_t attrRec;
              pReportCmd->attrList[i].attrID = attrListItem->data->attrID;
              uint8_t attrRes = bdb_RepFindAttrEntry( clusterEndpointItem->endpoint, clusterEndpointItem->cluster, attrListItem->data->attrID, &attrRec );
              if( attrRes == BDBREPORTING_TRUE )
              {
                dataLen += 2 + 1; // Attribute ID + data type
                pReportCmd->attrList[i].dataType = attrRec.dataType;
                pReportCmd->attrList[i].attrData = pAttrDataTemp;
                pAttrDataTemp = OsalPort_memcpy(pAttrDataTemp, attrRec.dataPtr, BDBREPORTING_MAX_ANALOG_ATTR_SIZE);
                dataLen += zclGetAttrDataLength( attrRec.dataType, pAttrDataTemp );
                //Update last value reported
                if( zclAnalogDataType( attrRec.dataType ) )
                {
                  //Only if the datatype is analog
                  memset( attrListItem->data->lastValueReported,0x00, BDBREPORTING_MAX_ANALOG_ATTR_SIZE );
                  OsalPort_memcpy( attrListItem->data->lastValueReported, attrRec.dataPtr, zclGetDataTypeLength( attrRec.dataType ) );
                }
              }
            }
          }

          if( dataLen > 82)
          {
              for( uint8_t  wIndxEntry = 0; wIndxEntry < NWK_MAX_BINDING_ENTRIES; wIndxEntry++)
              {
                  if(BindingTable[wIndxEntry].srcEP == clusterEndpointItem->endpoint)
                  {
                      for(uint8_t wIndxCluster = 0; wIndxCluster < BindingTable[wIndxEntry].numClusterIds; wIndxCluster++)
                      {
                          if(BindingTable[wIndxEntry].clusterIdList[wIndxCluster] == clusterEndpointItem->cluster)
                          {
                              zAddrType_t addrEx;
                              uint8_t status = bindingAddrMgsHelperConvert(BindingTable[wIndxEntry].dstIdx, &addrEx);
                              dstAddr.endPoint = BindingTable[wIndxEntry].dstEP;
                              if(status)
                              {
                                 dstAddr.addrMode = addrEx.addrMode;
                                 osal_cpyExtAddr(dstAddr.addr.extAddr, addrEx.addr.extAddr);
                              }
                          }
                      }
                  }
              }
          }

          zcl_StackSendReportCmd( clusterEndpointItem->endpoint, &dstAddr,
                             clusterEndpointItem->cluster, pReportCmd,
                             ZCL_FRAME_SERVER_CLIENT_DIR, BDB_REPORTING_DISABLE_DEFAULT_RSP, zcl_getFrameCounter() );
        }
        if( (pReportCmd != NULL ) )
        {
          OsalPort_free( pReportCmd );
        }
        if ( (pAttrData != NULL) )
        {
          OsalPort_free( pAttrData );
        }
      }
    }