2013年1月4日 星期五

[ZigBee][CC2530] [Z-Stack] Combining SerialApp and TransmitApp - Multiple Endpoints in One ZigBee Device

    After playing around with Z-Stack samples, SerialApp and TransmitApp, I tried to combine these two sample applications to setup a template for my project.

    In the beginning, I thought it's an easy work which can be done within one or two days. However, it actually took me almost TWO WEEKS to work, because the original samples doesn't fit in multiple endpoints scheme.

    I used SerialApp as the basis and added TransmitApp (in fact, only TransmitApp.c and TransmitApp.h) in.

1. Follow "Create New Application For SmartRF05 + CC2530"(SWRA231) to create a new project based on SerialApp. In my case, the project name is OH6A.

2. Changes in  OSAL_OH6A.c are:

  • adding TransmitApp to the task array "tasksArr"


const pTaskEventHandlerFn tasksArr[] = {
  macEventLoop,
  nwk_event_loop,
  Hal_ProcessEvent,
#if defined( MT_TASK )
  MT_ProcessEvent,
#endif
  APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_ProcessEvent,
#endif
  ZDApp_event_loop,
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_event_loop,
#endif
  SerialApp_ProcessEvent,
  TransmitApp_ProcessEvent
};



  • Invoking TransmitApp_Init() in the function "void osalInitTasks( void )"


3. I wish to use the same binding behavior as samples, thus, the key handlers has to be extend to accommodate multiple tasks

in onboard.c

  • change registeredKeysTaskID to uint8 array
uint8 registeredKeysTaskID[REGISTER_KEY_TASK_NUM_MAX];
  • modify RegisterForKeys() to allow multiple tasks to register to
uint8 RegisterForKeys( uint8 task_id )
{
char i;
for(i = 0; i < REGISTER_KEY_TASK_NUM_MAX; i++)
{
if(registeredKeysTaskID[i] == NO_TASK_ID)
{ registeredKeysTaskID[i] = task_id;
break;
}
}
if(i < REGISTER_KEY_TASK_NUM_MAX)
return (TRUE);
else
return (FALSE);
}
  • modify OnBoard_SendKeys to send the key press event to all registered tasks
uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
{
        keyChange_t *msgPtr;

char i;
for(i = 0; (i < REGISTER_KEY_TASK_NUM_MAX) && (registeredKeysTaskID[i] != NO_TASK_ID); i++)
{
// Send the address to the task
msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );
if ( msgPtr )
{
msgPtr->hdr.event = KEY_CHANGE;
msgPtr->state = state;
msgPtr->keys = keys;
osal_msg_send( registeredKeysTaskID[i], (uint8 *)msgPtr );
}
else
{
return ( ZFailure );
}
}
return ( ZSuccess );

}  

  • (OPTIONAL) I disabled SW2 (End_Device_Bind) in both SerialApp_HandleKeys()@SerialApp.c and TransmitApp_HandleKeys()@TransmitApp.c, since "One of the things I don’t like about this command is that if it returns success, the caller has no idea if the targets were bound or unbound. It’s a toggle!", quoted from Zigbee Wireless Networking, CH, Drew Gislason. (Don't know why "toggle" though but it's not the point in this experiment)
  • (OPTIONAL) compile option "RFD_RCVC_ALWAYS_ON=TRUE" is added to preprocessor.

    OK, it only took me a while to finish those steps above and I *THOUGHT* this experiment can be done by end of that day. It's NOT. TransmitApp seems working but apparently SerialApp doesn't.

    After spending many day and night debugging, I found that the root cause is when processing Match_Desc_rsp @SerialApp_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg )@SerialApp.c, the SerialApp_TxAddr.endPoint was assigned incorrect (actually, overwritten by the  endpoint of TransmitApp).

    In SerialApp_Init( uint8 task_id )Match_Desc_rsp event was registered by   ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp ). However, the callback/dispatch function,   handled = ZDO_SendMsgCBs( &inMsg )@ZDProfile.c is blindly dispatch ALL Match_Desc_rsp message to ALL registered task. Z-Stack should have dealt with matching Match_Desc_req with corresponding Match_desc_rsp, before sending back to registered task.

4. To quickly fix this problem (not graceful, but I don't want to modify Z-Stack anyway), in both SerialApp.c and TransmitApp.c, I tried to keep track of APS "Transaction sequence number" when sending Match_Desc_req and check the stored data when receiving Match_Desc_rsp. Fortunately OSAL is not preemptive so it can work.

Take TransmitApp as example, in TransmitApp.c:


  • adding global "byte TransmitApp_ZDP_TransID;" to store TransID
  • in function TransmitApp_HandleKeys(), saving global variable ZDP_TransID which is used by 


      HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF );
      // Initiate a Match Description Request (Service Discovery)
      dstAddr.addrMode = AddrBroadcast;
      dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR;
      TransmitApp_ZDP_TransID = ZDP_TransID;      ZDP_MatchDescReq( &dstAddr, NWK_BROADCAST_SHORTADDR,
                        TRANSMITAPP_PROFID,
                        TRANSMITAPP_MAX_CLUSTERS, (cId_t *)TransmitApp_ClusterList,
                        TRANSMITAPP_MAX_CLUSTERS, (cId_t *)TransmitApp_ClusterList,
                        FALSE );


  • in function TransmitApp_ProcessZDOMsgs(), checking the TransSeq within incoming msg with the stored TransID.
    case Match_Desc_rsp:
      {
        ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg );
        if ( pRsp )
        {

 if(TransmitApp_ZDP_TransID == inMsg->TransSeq)
          if ( pRsp->status == ZSuccess && pRsp->cnt )
          {
            TransmitApp_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
            TransmitApp_DstAddr.addr.shortAddr = pRsp->nwkAddr;
            // Take the first endpoint, Can be changed to search through endpoints
            TransmitApp_DstAddr.endPoint = pRsp->epList[0];
            // Light LED
            HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );
          }
          osal_mem_free( pRsp );
        }
      }
      break;


After those change, I can finally get two endpoints work on the same ZigBee device, based on IT's Z-stack.









沒有留言:

張貼留言