CC2541 寻找ANCS服务

CC2541 寻找ANCS服务

转载:http://www.lofter.com/tag/cc2541

BLE设备想要获取iOS通知,寻找ANCS服务是其中非常关键的一步。而寻找ANCS服务也是代码实现最复杂的部分。就来讲讲如何代码实现寻找ANCS服务。

1、寻找状态

在寻找ANCS的过程中,会存在7种状态:空闲状态、开始寻找状态、寻找服务状态、寻找特征状态、寻找通知源特征CCCD状态、寻找数据源特征CCCD状态、寻找失败状态。写成代码如下:

enum/*NCS Discoverty State*/

{

  DISC_IDLE = 0x00,

  DISC_ANCS_START = 0x10,

  DISC_ANCS_SVC,

  DISC_ANCS_CHAR,

  DISC_ANCS_NTF_CCCD,

  DISC_ANCS_DATA_CCCD,

  DISC_FAILED = 0xFF

};

 

2、特征的句柄

对于BLE设备来说,它能够ANCS服务中的不同特征进行数据通讯,靠的并不是UUID,而是句柄。句柄就类似于网络中客户端服务器“端口”(Port)一样的存在。在寻找ANCS服务过程中,要保存获取的各类句柄,如通知源特征的起始句柄、通知源特征结束句柄、通知源特征CCCD的句柄等等。如下所示:

enum/*ANCS Handle*/

{

  HDL_ANCS_NTF_SOURCE_START, /*ANCS Notification Source start handle*/

  HDL_ANCS_NTF_SOURCE_END,   /*ANCD Notification Source end handle*/

  HDL_ANCS_NTF_SOURCE_CCCD,  /*ANCD Notification Source CCCD handle*/    

  HDL_ANCS_DATA_SOURCE_START, /*ANCS Data Source start handle */

  HDL_ANCS_DATA_SOURCE_END,   /*NCS Data Source end handle*/

  HDL_ANCS_DATA_SOURCE_CCCD,  /*ANCS Data source CCCD handle*/

  HDL_ANCS_CTL_POINT_CTRL,    /*ANCS Control Point handle*/

  HDL_ANCS_CASCE_LEN         /*Number of ANCS handle*/

};

再定义一个数组专门用于保存上面各类句柄的值,如下:

uint16 ANCSHdlCache[HDL_ANCS_CASCE_LEN];

 

3、ANCS的UUID

在寻找ANCS过程中,服务与特征的UUID是需要的。ANCS存在下面几种UUID。

· 主服务UUID:  7905F431-B5CE-4E99-A40F-4B1E122D00D0

· 通知源特征UUID:9FBF120D-6301-42D9-8C58-25E699A21DBD

· 控制点特征UUID:69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9

· 数据源特征UUID:22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB

主服务的UUID可以用来寻找特征的句柄范围,所以必须要是完整,如下:

#define ANCS_SVC_UUID {0xd0,0x00,0x2d,0x12,0x1e,0x4b,0x0f,0xa4,0x99,0x4e,0xce,0xb5,0x31,0xf4,0x05,0x79}

找到特征的句柄范围后,就可以轻易获得个特征的句柄,这是要依靠UUID来区分是哪种特征,判断时并不需要完整UUID,而只要一小部分就可以了,这里选择最前的16位UUID,如下:

#define ANCS_NTF_SOURCE_CHAR_UUID      0x1DBD

#define ANSC_DATA_SOURCE_CHAR_UUID     0x7BFB

#define ANCS_CTL_POIONT_CHAR_UUID      0xD9D9

 

4、寻找ANCD服务

接着编写函数ANCSDiscovery()函数用于寻找ANCS服务。该函数包含了寻找ANCS的5个状态:开始寻找状态、寻找服务状态、寻找特征状态、寻找通知源特征CCCD状态、寻找数据源特征CCCD状态。如下:

uint8 ANCSDiscovery(uint8 state, gattMsgEvent_t *pMsg)

{

switch(state)

{

case DISC_ANCS_START:

......

break;

case DISC_ANCS_SVC:

......

break;

case DISC_ANCS_CHAR:

......

break;

case DISC_ANCS_NTF_CCCD:

.......

break;

case DISC_ANCS_DATA_CCCD:

.......

break;

default:

break;

}

return newState;

}

(1)寻找主服务

首先在DISC_ANCS_START下,开始寻找ANCS服务。根据ACCS服务的UUID找到ANCS的主服务,调用GATT_DiscPrimaryServiceByUUID()就可以实现,代码如下:

case DISC_ANCS_START:  

{

  uint8 uuid[ATT_UUID_SIZE] = ANCS_SVC_UUID;

  /* Initialize service discovery variables*/

  svcStartHdl = svcEndHdl = 0;

  endHdlIdx = 0;

 

  /* Discover service by UUID*/

  GATT_DiscPrimaryServiceByUUID( AppleANCSConnHandle, uuid, 

                  ATT_UUID_SIZE, AppleANCSTaskId );            

  newState = DISC_ANCS_SVC;

break;

函数DiscPrimaryServiceByUUID()会向服务器发送获取主服务的请求,并进入下一状态DISC_ANCS_SVC。

 

(2)寻找特征

当收到服务器的响应时,可以获取到ANCS服务的起始句柄和结束句柄两个参数,再根据这另个参数,就可以调用函数GATT_DiscAllChars()寻找之间的特征,代码如下:

if ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&

   pMsg->msg.findByTypeValueRsp.numInfo > 0 )

{

  /*store service handle and end handle */

  svcStartHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].handle;

  svcEndHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].grpEndHandle; 

}  

/* If procedure complete */

if ( ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP  && 

    pMsg->hdr.status == bleProcedureComplete ) ||

   ( pMsg->method == ATT_ERROR_RSP ) )

{

  /* If service found*/

  if ( svcStartHdl != 0 )

  {

    /* Discover all characteristics */

    GATT_DiscAllChars( AppleANCSConnHandle, svcStartHdl,

                      svcEndHdl, AppleANCSTaskId );

    newState = DISC_ANCS_CHAR;

  }

  else

  {

    /* Service not found*/

    newState = DISC_FAILED;

  }

}    

break;

函数GATT_DiscAllChars()会向服务器发出获取各特征的请求,并进入下一状态DISC_ANCS_CHAR。

 

(3)寻找通知源特征的CCCD

收到服务的响应后,响应数据携带每个特征的UUID与句柄等信息,并将句柄提取出来。对于控制点特征来说值存在一个句柄,而对于通知源特征和数据源特征来说就不止一个特征了,因为他们还存在CCCD,所以需要保存它们的句柄范围,然后调用函数GATT_DiscAllCharDescs()最先开始需找通知源特征的CCCD,如下所示。

case DISC_ANCS_CHAR:

{

  uint8   i;

  uint8   *p;

  uint16  handle;

  uint16  uuid;

      

  /* Characteristics found*/

  if ( pMsg->method == ATT_READ_BY_TYPE_RSP &&

     pMsg->msg.readByTypeRsp.numPairs > 0 && 

     pMsg->msg.readByTypeRsp.len == CHAR_DESC_HDL_UUID128_LEN )

  {

    /* For each characteristic declaration*/

    p = pMsg->msg.readByTypeRsp.dataList;

    for ( i = pMsg->msg.readByTypeRsp.numPairs; i > 0; i-- )

    {

      /* Parse characteristic declaration*/

      handle = BUILD_UINT16(p[3], p[4]);

      uuid = BUILD_UINT16(p[5], p[6]);

      /* If looking for end handle*/

      if ( endHdlIdx != 0 )

      {

        /* End handle is one less than handle of characteristic declaration*/

        ANCSHdlCache[endHdlIdx] = BUILD_UINT16(p[0], p[1]) - 1;

        endHdlIdx = 0;

      }

 

      /* If UUID is of interest, store handle*/

      switch ( uuid )

      {

        /*Store ANCS Notification Source handle*/

        case ANCS_NTF_SOURCE_CHAR_UUID:

          ANCSHdlCache[HDL_ANCS_NTF_SOURCE_START] = handle;

          endHdlIdx = HDL_ANCS_NTF_SOURCE_END;

          break;      

        /*Store ANCS Data Source characteristic handle    */   

        case ANSC_DATA_SOURCE_CHAR_UUID:

          ANCSHdlCache[HDL_ANCS_DATA_SOURCE_START] = handle;

          endHdlIdx = HDL_ANCS_DATA_SOURCE_END;

          break;

        /*Store ANCS Control Point characteristic handle*/

        case ANCS_CTL_POIONT_CHAR_UUID:

          ANCSHdlCache[HDL_ANCS_CTL_POINT_CTRL] = handle;

          break;

        default:

          break;

      }   

      p += CHAR_DESC_HDL_UUID128_LEN;

    }     

  }     

  /* If procedure complete*/

  if ( ( pMsg->method == ATT_READ_BY_TYPE_RSP  && 

      pMsg->hdr.status == bleProcedureComplete ) ||

     ( pMsg->method == ATT_ERROR_RSP ) )

  {

    /* Special case of end handle at end of service*/

    if ( endHdlIdx != 0 )

    {

      ANCSHdlCache[endHdlIdx] = svcEndHdl;

      endHdlIdx = 0;

    }

    /* If didn't find mandatory characteristic*/

    if ( ANCSHdlCache[HDL_ANCS_NTF_SOURCE_START] == 0 )

    {

      newState = DISC_FAILED;

    }

    else if ( ANCSHdlCache[HDL_ANCS_NTF_SOURCE_START] <

           ANCSHdlCache[HDL_ANCS_NTF_SOURCE_END] )

    {

     /* Discover Notification Source client configuration characteristic descriptors*/

      GATT_DiscAllCharDescs( AppleANCSConnHandle,

                   ANCSHdlCache[HDL_ANCS_NTF_SOURCE_START] + 1,

                   ANCSHdlCache[HDL_ANCS_NTF_SOURCE_END],

                   AppleANCSTaskId );

      newState = DISC_ANCS_NTF_CCCD;

    }

    else

    {

      newState = DISC_IDLE;

    }

  }

}      

break;

函数DiscAllCharDescs()会向服务器发起寻找指定的CCCD请求,并进入DISC_ANCS_NTF_CCCD状态。

 

(4)寻找数据源CCCD

服务器收到寻找CCCD请求后,就会返回携带CCCD句柄的响应数据,将句柄保存下来,就得到了通知源特征CCCD句柄。然后,再调用一次函数DiscAllCharDescs()来寻找数据源特征的CCCD,代码如下:

case DISC_ANCS_NTF_CCCD:

{

  uint8 i;

 

  /* Notification Source client configuration characteristic descriptors found*/

  if (pMsg->method == ATT_FIND_INFO_RSP &&

    pMsg->msg.findInfoRsp.numInfo > 0 &&

    pMsg->msg.findInfoRsp.format == ATT_HANDLE_BT_UUID_TYPE )

  {

    /*For each handle/uuid pair*/

    for (i = 0; i < pMsg->msg.findInfoRsp.numInfo; i++)

    {

      /* Look for CCCD*/

        if((pMsg->msg.findInfoRsp.info.btPair[i].uuid[0] == 

          LO_UINT16(GATT_CLIENT_CHAR_CFG_UUID)) &&

          (pMsg->msg.findInfoRsp.info.btPair[i].uuid[1] == 

          HI_UINT16(GATT_CLIENT_CHAR_CFG_UUID)))

      {

        /* CCCD found, Store Notification source CCCD handle*/

          ANCSHdlCache[HDL_ANCS_NTF_SOURCE_CCCD]=pMsg->msg.findInfoRsp.info.btPair[i].handle;

        break;

      }

    }

  }

  /* If procedure complete*/

  if ((pMsg->method == ATT_FIND_INFO_RSP  &&

     pMsg->hdr.status == bleProcedureComplete ) ||

    (pMsg->method == ATT_ERROR_RSP))

  {

    /* Discover Data Source client configuration characteristic descriptors*/

    GATT_DiscAllCharDescs(AppleANCSConnHandle,

                    ANCSHdlCache[HDL_ANCS_DATA_SOURCE_START] + 1,

                    ANCSHdlCache[HDL_ANCS_DATA_SOURCE_END],

                    AppleANCSTaskId );

    newState = DISC_ANCS_DATA_CCCD;

  }

}

break;

 

(5)寻找完成

当收到服务器的响应时,就可得到数据源特征CCCD的句柄,至此ANCS寻找完毕,进入空闲状态DISC_IDLE,并开始接收通知。

case DISC_ANCS_DATA_CCCD:

{

  uint8 i;

      

  /* Data source client configuration characteristic descriptors found*/

  if (pMsg->method == ATT_FIND_INFO_RSP &&

    pMsg->msg.findInfoRsp.numInfo > 0 &&

    pMsg->msg.findInfoRsp.format == ATT_HANDLE_BT_UUID_TYPE )

  {

    /*For each handle/uuid pair*/

    for (i = 0; i < pMsg->msg.findInfoRsp.numInfo; i++)

    {

      /* Look for CCCD*/

      if ((pMsg->msg.findInfoRsp.info.btPair[i].uuid[0] == 

           LO_UINT16(GATT_CLIENT_CHAR_CFG_UUID)) &&

           (pMsg->msg.findInfoRsp.info.btPair[i].uuid[1] == 

           HI_UINT16(GATT_CLIENT_CHAR_CFG_UUID)))

      {

        /* CCCD found, store Data Source CCCD handle*/

      ANCSHdlCache[HDL_ANCS_DATA_SOURCE_CCCD] = pMsg->msg.findInfoRsp.info.btPair[i].handle;

        break;

      }

    }

  }

  /* If procedure complete*/

  if ((pMsg->method == ATT_FIND_INFO_RSP  &&

     pMsg->hdr.status == bleProcedureComplete ) ||

    (pMsg->method == ATT_ERROR_RSP))

  {

    /*ANCS Discovery complete, then listen for notification*/

    osal_set_event( AppleANCSTaskId,START_LISENNTF_EVT);/*Listen for Notification Source right away*/

    osal_start_timerEx( AppleANCSTaskId,START_LISENNTF_EVT,1000);/*Listen for Data Source 1s later*/

        

    newState = DISC_IDLE;

  }

}

break;

ANCS服务寻找完毕后,就可以打开监听通知功能了,这里需要注意的是,不能同一时间打开通知源特征的通知和数据源特征的通知功能,所以这里开启了一个定时任务,让数据源特征在1s后再打来通知功能。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值