Zigbee协议分析(三)接收数据处理过程

3 篇文章 1 订阅

书接上回:

在用户事件处理函数uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )中,

通过 switch ( MSGpkt->hdr.event )语句, 首先区分了以下三类事件:

1. 按键KEY_CHANGE ,处理入口

SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t*)MSGpkt)->keys );

2. AF_INCOMING_MSG_CMD 数据接收

入口函数SampleApp_MessageMSGCB( MSGpkt ); 

3. ZDO_STATE_CHANGE 网络状态改变

处置操作SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status);

所以,

从上面看到,接收数据的入口函数是SampleApp_MessageMSGCB( MSGpkt ); 

下面来用例程1来观察接收数据的处理:

上述3中有个判断不同网络状态的处理,也就是完成网络初始化,确定该设备类型后的操作应该在这里进行。

case ZDO_STATE_CHANGE:
          //只要网络状态发生改变,就通过ZDO_STATE_CHANGE事件通知所有的任务。
          //同时完成对协调器,路由器,终端的设置
          SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
          //if ( (SampleApp_NwkState == DEV_ZB_COORD)//实验中协调器只接收数据所以取消发送事件
          if ( (SampleApp_NwkState == DEV_ROUTER) || (SampleApp_NwkState == DEV_END_DEVICE) )
          {
            // Start sending the periodic message in a regular interval.
            //这个定时器只是为发送周期信息开启的,设备启动初始化后从这里开始
            //触发第一个周期信息的发送,然后周而复始下去
            osal_start_timerEx( SampleApp_TaskID,
                              SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
                              SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );
          }
          else
          {
            // Device is no longer in the network
          }
          break;

观察osal_start_timerEX启动了周期性执行任务,看看这个函数的定义OSAL_Timers.c:

/*********************************************************************
 * @fn      osal_start_timerEx
 *
 * @brief
 *
 *   This function is called to start a timer to expire in n mSecs.
 *   When the timer expires, the calling task will get the specified event.
 *
 * @param   uint8 taskID - task id to set timer for
 * @param   uint16 event_id - event to be notified with
 * @param   UNINT16 timeout_value - in milliseconds.
 *
 * @return  SUCCESS, or NO_TIMER_AVAIL.
 */
uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value )
{
  halIntState_t intState;
  osalTimerRec_t *newTimer;

  HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.

  // Add timer
  newTimer = osalAddTimer( taskID, event_id, timeout_value );

  HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.

  return ( (newTimer != NULL) ? SUCCESS : NO_TIMER_AVAIL );
}

启动了一个间隔n ms触发的事件

看看调用的变量定义SampleApp.h:

// Send Message Timeout
#define SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT   5000     // Every 5 seconds

// Application Events (OSAL) - These are bit weighted definitions.
#define SAMPLEAPP_SEND_PERIODIC_MSG_EVT       0x0001

也就是5s会触发一次动作(END_DEVICE或者Router)

那么这个周期性动作会在哪里触发呢? 

恰恰是在用户处理事件的两个入口之一(另一个是系统事件消息SYS_EVENT_MSG),这一个就是周期性事件,这样就串起来了。

 if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT )
  {
    // Send the periodic message 处理周期性事件,
    //利用SampleApp_SendPeriodicMessage()处理完当前的周期性事件,然后启动定时器
    //开启下一个周期性事情,这样一种循环下去,也即是上面说的周期性事件了,
    //可以做为传感器定时采集、上传任务
    SampleApp_SendPeriodicMessage();

    // Setup to send message again in normal period (+ a little jitter)
    osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
        (SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) );

    // return unprocessed events 返回未处理的事件
    return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
  }

关键入口一:周期发送函数SampleApp_SendPeriodicMessage();

查看定义SampleApp.c


/*********************************************************************
 * @fn      SampleApp_SendPeriodicMessage
 *
 * @brief   Send the periodic message.
 *
 * @param   none
 *
 * @return  none
 */
//分析发送周期信息
void SampleApp_SendPeriodicMessage( void )
{
  byte SendData[3]="D1";

  // 调用AF_DataRequest将数据无线广播出去
  if( AF_DataRequest( &SampleApp_Periodic_DstAddr,//发送目的地址+端点地址和传送模式
                       &SampleApp_epDesc,//源(答复或确认)终端的描述(比如操作系统中任务ID等)源EP
                       SAMPLEAPP_PERIODIC_CLUSTERID, //被Profile指定的有效的集群号
                       2,       // 发送数据长度
                       SendData,// 发送数据缓冲区
                       &SampleApp_TransID,     // 任务ID号
                       AF_DISCV_ROUTE,      // 有效位掩码的发送选项
                       AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )  //传送跳数,通常设置为AF_DEFAULT_RADIUS
  {
  }
  else
  {
    HalLedSet(HAL_LED_1, HAL_LED_MODE_ON);
    // Error occurred in request to send.
  }
}

主要是调用ZSTACK的数据发送API,顺便看看地址的数据结构:

afAddrType_t SampleApp_Periodic_DstAddr;

typedef struct
{
  union
  {
    uint16      shortAddr;
    ZLongAddr_t extAddr;
  } addr;
  afAddrMode_t addrMode;
  byte endPoint;
  uint16 panId;  // used for the INTER_PAN feature
} afAddrType_t;

那么其中的数据在哪儿定义的呢?跟踪到了SampleApp_Init(),真相大白:)

 // Setup for the periodic message's destination address 设置发送数据的方式和目的地址寻址模式
  // Broadcast to everyone 发送模式:广播发送
  SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;//广播
  SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号
  SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF;//指定目的网络地址为广播地址
 

端口号的定义:

// These constants are only for example and should be changed to the
// device's needs
#define SAMPLEAPP_ENDPOINT           20   //16进制不就是0x14吗

#define SAMPLEAPP_PROFID             0x0F08

#define SAMPLEAPP_DEVICEID           0x0001
#define SAMPLEAPP_DEVICE_VERSION     0
#define SAMPLEAPP_FLAGS              0

这些数据在抓包的时候都会最终看到。敬请留意

好,回顾完了,来看下接收到的数据处理过程:

/*********************************************************************
 * @fn      SampleApp_MessageMSGCB
 *
 * @brief   Data message processor callback.  This function processes
 *          any incoming data - probably from other devices.  So, based
 *          on cluster ID, perform the intended action.
 *
 * @param   none
 *
 * @return  none
 */
//接收数据,参数为接收到的数据
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
  uint16 flashTime;
  byte buf[3]; 

  switch ( pkt->clusterId ) //判断簇ID
  {
    case SAMPLEAPP_PERIODIC_CLUSTERID: //收到广播数据
      osal_memset(buf, 0 , 3);
      osal_memcpy(buf, pkt->cmd.Data, 2); //复制数据到缓冲区中
      
      if(buf[0]=='D' && buf[1]=='1')      //判断收到的数据是否为"D1"         
      {
          HalLedBlink(HAL_LED_1, 0, 50, 500);//如果是则Led1间隔500ms闪烁
#if defined(ZDO_COORDINATOR) //协调器收到"D1"后,返回"D1"给终端,让终端Led1也闪烁
          SampleApp_SendPeriodicMessage();
#endif
      }
      else
      {
          HalLedSet(HAL_LED_1, HAL_LED_MODE_ON);                   
      }
      break;

    case SAMPLEAPP_FLASH_CLUSTERID: //收到组播数据
      flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
      HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );
      break;
  }
}
 

1)对比簇ID

查看宏定义SampleApp.h中:


#define SAMPLEAPP_MAX_CLUSTERS       2
#define SAMPLEAPP_PERIODIC_CLUSTERID 1
#define SAMPLEAPP_FLASH_CLUSTERID     2

抓包来看看,结合前面协议栈的分析,有几个发现:

a) 按程序设计,是EP先发送数据“D1”,间隔应该是5秒,观察发送时隙:

间隔基本是5秒

b)观察MAC层数据

EP发出:

EC发回针对收到这一帧的ACK:

跟着在发出广播数据:

c)发送接口

AF_DataRequest( &SampleApp_Periodic_DstAddr,//发送目的地址+端点地址和传送模式
                       &SampleApp_epDesc,//源(答复或确认)终端的描述(比如操作系统中任务ID等)源EP
                       SAMPLEAPP_PERIODIC_CLUSTERID, //被Profile指定的有效的集群号
                       2,       // 发送数据长度
                       SendData,// 发送数据缓冲区
                       &SampleApp_TransID,     // 任务ID号
                       AF_DISCV_ROUTE,      // 有效位掩码的发送选项
                       AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )  //传送跳数,通常设置为AF_DEFAULT_RADIUS

都在NWK层和APS层得到充分体现:

先看NWK层:

DestAddress为广播方式0xFFFF,源地址为EP的16位短地址0x5F76

RADIUS就是跳数

对AF_DEFAULT_RADIUS顺藤摸瓜AF.h

// Default Radius Count value
#define AF_DEFAULT_RADIUS                  DEF_NWK_RADIUS

再摸nwk.h:

// the default network radius set twice the value of <nwkMaxDepth>
#define DEF_NWK_RADIUS           ( 2 * BEACON_MAX_DEPTH )

再摸nwk.h:

#define BEACON_MAX_DEPTH                0x0F

从抓包数据看是0x1E,恰恰是0x0F*2=30 =0x1E!

再看APS层:

DestEndPoint就是刚才定义的端口号

#define SAMPLEAPP_ENDPOINT           20   //16进制不就是0x14吗

端和源一样都是0x14

因为初始化时定义了:

  // Setup for the flash command's destination address - Group 1 组播发送
  SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)afAddrGroup; //组寻址
  SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号
  SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP;//组号0x0001

  // Fill out the endpoint description. 定义本设备用来通信的APS层端点描述符
  SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号
  SampleApp_epDesc.task_id = &SampleApp_TaskID;   //SampleApp 描述符的任务ID
  SampleApp_epDesc.simpleDesc
            = (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;//SampleApp简单描述符
  SampleApp_epDesc.latencyReq = noLatencyReqs;    //延时策略

ClusterID和刚才发送函数里指定的ID对应上0x0001,注意是四位(对接其他服务要靠它了)

#define SAMPLEAPP_PERIODIC_CLUSTERID 1

APS counter是字节计数?暂且这么理解

最后是有效载荷APS payload  “44 31”,就是D1的ASCII码

看看完整包的RAW DATA:

Packet index: 1
Length: 27
Raw data (hex): 61 88 74 F1 FF 00 00 76 5F 08 00 FF FF 76 5F 1E 37 08 14 01 00 08 0F 14 05 44 31
RSSI [dBm]: -69
Correlation value: 108
CRC OK: 1

对应数据帧格式描述:

抓包工具看到的29长度包括了“净长”27+RSSI+校验值

前两个字节61 88为帧控制域,查定义:

其中,帧类型:

低位在前:

61对应:

bit0-3为“0x01”: 0-2为值为001, bit3=0,保护使能关闭

bit4-7 值为“0x60”:bit5=1,bit6=1,代表ACK使能,内部PAN(该子域1表示发送到同一PAN网络,如果为0,则发送值不同PAN网络)

88对应:

bit8-11值为0x08, bit10-11=10

bit12-15 值为0x80,bit14-15=10

均表示采用16位短地址模式

74  :帧序号

F1FF: PANID

0000:目标短地址

765F:(高位在前)  源短地址

接下来从08 00开始 是NWK 帧,查NWK帧格式定义:

所以08 00值帧控制域,定义:

bit0-1=00 ,所以此帧为数据帧

协议版本bit2-bit5=0100 ,协议版本号为0x02, zigbee2006以后的版本

bit6-7=00 ,发现路由

本应用里禁止路由(程序在哪儿定义? )

bit9=0 ,安全使能关闭

接下来是NWK payload, FFFF广播地址,76 5F 源地址(低位在前),1E是跳转深度,37是广播帧序列号

从08 14开始进入APS层(协议第二章详细描述),APS层 的帧格式:

所以0x14是目标端点的定义,上面提过了。

08代表帧控制域,来看帧控制域格式:

b1b0=00,帧类型,代表数据帧, 01命令,10确认ACK,11保留

b3b2=10 , 传送模式为广播,其他00为单播,01为间接寻址,11为组寻址

ACK请求关闭

进入APS数据域:

0x14: 目标端点

01 00: 0x0001 APS簇 ID,低位在前

08 0F:  0x0F08  ProfileID 

在SampleApp.h中定义:

// These constants are only for example and should be changed to the
// device's needs
#define SAMPLEAPP_ENDPOINT           20

#define SAMPLEAPP_PROFID             0x0F08
#define SAMPLEAPP_DEVICEID           0x0001
#define SAMPLEAPP_DEVICE_VERSION     0
#define SAMPLEAPP_FLAGS              0

#define SAMPLEAPP_MAX_CLUSTERS       2
#define SAMPLEAPP_PERIODIC_CLUSTERID 1
#define SAMPLEAPP_FLASH_CLUSTERID     2

0x14 : 源端口

0x05 : counter计数器

最后 0x44 0x31  :净数据 ASCII码对应“D1”

代码对应抓包数据解析完毕!

这应该是理解应用层的重要一课,主要理解了应用层的工作流程,下一节应该是网络层。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海里的鱼2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值