一、简介

本篇在《CC2541的SimpleBLECentral发现服务与特征值过程》的基础上,进一步讲述主机端如何发现从机的多个特征值句柄。

二、实验平台

协议栈版本:BLE-CC254x-1.3.2

编译软件:IAR 8.20.2

三、思路

主机通过“从机服务的UUID”发现从机相对应的服务,再通过分别发送“特征值的UUID”依次获取到特征值句柄。

由于有些特征值句柄是获取不到的,所以本篇只获取char1、char2、char4(需要修改从机)和char6(从机要有char6)的特征值句柄。

四、实现过程

1、增加多个特征值状态的宏(sImpleBLECentral.c中)


1. // Discovery states  
2. enum  
3. {  
4. // Idle  
5. // Service discovery  
6. // Characteristic discovery 1  
7. // Characteristic discovery 2  
8. // Characteristic discovery 3  
9. // Characteristic discovery 4  
10. // Characteristic discovery 5    
11. // Characteristic discovery 6  
12. };


默认的特征值状态的宏只有BLE_DISC_STATE_CHAR,因此在这里修改为6个。(具体要看从机有几个可读特征值)

2、定义一个自己存放特征句柄的数组(sImpleBLECentral.c中)


    1. // Discovered characteristic handle  
    2. static uint16 simpleBLECharHdl = 0;  
    3. static uint16 GUA_charHdl[6] = {0};     //6个特征值句柄保存位置


    当然也可以直接修改为simpleBLECharHdl[6],但这样需要修改代码中其他用到simpleBLECharHdl的地方。

    为了省事,很懒的我自己定义一个GUA_charHdl[6]。

    3、修改发现事件的处理函数simpleBLEGATTDiscoveryEvent(sImpleBLECentral.c中)

    1. /*********************************************************************
    2.  * @fn      simpleBLEGATTDiscoveryEvent
    3.  *
    4.  * @brief   Process GATT discovery event
    5.  *
    6.  * @return  none
    7.  */  
    8. static void simpleBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg )  
    9. {  
    10.   attReadByTypeReq_t req;  
    11.     
    12. if ( simpleBLEDiscState == BLE_DISC_STATE_SVC )  
    13.   {  
    14. // Service found, store handles  
    15. if ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&  
    16.          pMsg->msg.findByTypeValueRsp.numInfo > 0 )  
    17.     {  
    18.       simpleBLESvcStartHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].handle;  
    19.       simpleBLESvcEndHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].grpEndHandle;  
    20.     }  
    21.       
    22. // If procedure complete  
    23. if ( ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP  &&   
    24.            pMsg->hdr.status == bleProcedureComplete ) ||  
    25.          ( pMsg->method == ATT_ERROR_RSP ) )  
    26.     {  
    27. if ( simpleBLESvcStartHdl != 0 )  
    28.       {  
    29. // Discover characteristic  
    30.         simpleBLEDiscState = BLE_DISC_STATE_CHAR1;  
    31.           
    32.         req.startHandle = simpleBLESvcStartHdl;  
    33.         req.endHandle = simpleBLESvcEndHdl;  
    34.         req.type.len = ATT_BT_UUID_SIZE;  
    35. uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);  
    36. uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);  
    37.   
    38.   
    39.         GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId );  
    40.       }  
    41.     }  
    42.   }  
    43.     
    44. else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR1 )        //发现char1  
    45.   {  
    46. //读出char1的handle并保存到GUA_charHdl  
    47. if ( pMsg->method == ATT_READ_BY_TYPE_RSP &&                  
    48.          pMsg->msg.readByTypeRsp.numPairs > 0 )  
    49.     {  
    50.       GUA_charHdl[0] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],  
    51.                                        pMsg->msg.readByTypeRsp.dataList[1] );  
    52.   
    53.   
    54. //此时仍在进程中  
    55.     }  
    56.   
    57.   
    58. //发送命令读取下一个特征值的句柄        
    59. else{                                                      //注意这里一定要else,当numPairs=0时才能再读下一个,下同      
    60.       simpleBLEDiscState = BLE_DISC_STATE_CHAR2;  
    61.             
    62.       req.startHandle = simpleBLESvcStartHdl;  
    63.       req.endHandle = simpleBLESvcEndHdl;  
    64.       req.type.len = ATT_BT_UUID_SIZE;  
    65. uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR2_UUID);  
    66. uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR2_UUID);  
    67.   
    68.   
    69.       GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId );  
    70.     }  
    71.   }    
    72.     
    73. else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR2 )        //发现char2  
    74.   {  
    75. //读出char2的handle并保存到GUA_charHdl  
    76. if ( pMsg->method == ATT_READ_BY_TYPE_RSP &&                  
    77.          pMsg->msg.readByTypeRsp.numPairs > 0 )  
    78.     {  
    79.       GUA_charHdl[1] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],  
    80.                                        pMsg->msg.readByTypeRsp.dataList[1] );  
    81.   
    82.   
    83. //此时仍在进程中  
    84.     }  
    85.   
    86.   
    87. //发送命令读取下一个特征值的句柄        
    88. else{                                                             
    89.       simpleBLEDiscState = BLE_DISC_STATE_CHAR4;  
    90.             
    91.       req.startHandle = simpleBLESvcStartHdl;  
    92.       req.endHandle = simpleBLESvcEndHdl;  
    93.       req.type.len = ATT_BT_UUID_SIZE;  
    94. uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR4_UUID);  
    95. uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR4_UUID);  
    96.   
    97.   
    98.       GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId );      
    99.     }  
    100.   }    
    101. /*    
    102.   else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR3 )        //发现char3
    103.   {
    104.     //读出char3的handle并保存到GUA_charHdl
    105.     if ( pMsg->method == ATT_READ_BY_TYPE_RSP &&                
    106.          pMsg->msg.readByTypeRsp.numPairs > 0 )
    107.     {
    108.       GUA_charHdl[2] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],
    109.                                        pMsg->msg.readByTypeRsp.dataList[1] );
    110. 
    111. 
    112.       simpleBLEProcedureInProgress = TRUE;                     //此时仍在进程中
    113.     }
    114. 
    115. 
    116.     //发送命令读取下一个特征值的句柄    
    117.     else{                                                          
    118.       simpleBLEDiscState = BLE_DISC_STATE_CHAR4;
    119.           
    120.       req.startHandle = simpleBLESvcStartHdl;
    121.       req.endHandle = simpleBLESvcEndHdl;
    122.       req.type.len = ATT_BT_UUID_SIZE;
    123.       req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR4_UUID);
    124.       req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR4_UUID);
    125. 
    126. 
    127.       GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId ); 
    128.     }
    129.   }    
    130. */  
    131. else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR4 )        //发现char4  
    132.   {  
    133. //读出char3的handle并保存到GUA_charHdl  
    134. if ( pMsg->method == ATT_READ_BY_TYPE_RSP &&                  
    135.          pMsg->msg.readByTypeRsp.numPairs > 0 )  
    136.     {  
    137.       GUA_charHdl[3] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],  
    138.                                        pMsg->msg.readByTypeRsp.dataList[1] );  
    139.   
    140.   
    141. //此时仍在进程中  
    142.     }  
    143.       
    144. //发送命令读取下一个特征值的句柄      
    145. else{                                                           
    146.       simpleBLEDiscState = BLE_DISC_STATE_CHAR6;  
    147.             
    148.       req.startHandle = simpleBLESvcStartHdl;  
    149.       req.endHandle = simpleBLESvcEndHdl;  
    150.       req.type.len = ATT_BT_UUID_SIZE;  
    151. uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR6_UUID);  
    152. uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR6_UUID);  
    153.   
    154.   
    155.       GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId );   
    156.     }  
    157.   }   
    158. /*
    159.   else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR5 )        //发现char5
    160.   {
    161.     //读出char3的handle并保存到GUA_charHdl
    162.     if ( pMsg->method == ATT_READ_BY_TYPE_RSP &&                
    163.          pMsg->msg.readByTypeRsp.numPairs > 0 )
    164.     {
    165.       GUA_charHdl[4] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],
    166.                                        pMsg->msg.readByTypeRsp.dataList[1] );
    167. 
    168. 
    169.       simpleBLEProcedureInProgress = TRUE;                     //此时仍在进程中
    170.     }
    171.     
    172.     
    173.     //发送命令读取下一个特征值的句柄    
    174.     else{                                                          
    175.       simpleBLEDiscState = BLE_DISC_STATE_CHAR6;
    176.           
    177.       req.startHandle = simpleBLESvcStartHdl;
    178.       req.endHandle = simpleBLESvcEndHdl;
    179.       req.type.len = ATT_BT_UUID_SIZE;
    180.       req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR6_UUID);
    181.       req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR6_UUID);
    182. 
    183. 
    184.       GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId ); 
    185.     }   
    186.   } 
    187. */    
    188. else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR6 )        //发现char6  
    189.   {  
    190. // Characteristic found, store handle  
    191. if ( pMsg->method == ATT_READ_BY_TYPE_RSP &&   
    192.          pMsg->msg.readByTypeRsp.numPairs > 0 )  
    193.     {  
    194.       GUA_charHdl[5] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],  
    195.                                        pMsg->msg.readByTypeRsp.dataList[1] );  
    196.         
    197. "Char5 Found", HAL_LCD_LINE_6 );  
    198. //注意最后一个特征值时需要赋值  
    199.     }  
    200.       
    201. //读完最后的char6,就可以返回闲置模式了  
    202.   
    203.   
    204.       
    205.   }      
    206. }

    注意:

    1)主机端只能获得“特征值属性为读、通知,并且属性表中为可读”的特征值句柄。

    说的通俗一点,就是特征值的属性要为GATT_PROP_READ或GATT_PROP_NOTIFY,且属性表中对应的值的属性要为GATT_PERMIT_READ,主机端才能获取到它的特征值句柄。

    例子一:

    SimpleBLEPeripheral工程的char1的属性是可读可写(满足条件)


    1. // Simple Profile Characteristic 1 Properties  
    2. static uint8 simpleProfileChar1Props = GATT_PROP_READ | GATT_PROP_WRITE;


    属性表中的属性是GATT_PERMIT_READ(满足条件)


    1. // Characteristic Value 1  
    2. {   
    3.   { ATT_BT_UUID_SIZE, simpleProfilechar1UUID },  
    4.   GATT_PERMIT_READ | GATT_PERMIT_WRITE,   
    5.   0,   
    6.   &simpleProfileChar1   
    7. },

    因此,主机端可获取到同时满足两个条件的char1的特征值句柄。

    例子二:

    SimpleBLEPeripheral工程的char3的属性是可写,不可读(不满足条件)


    并且属性表中的属性也是GATT_PERMIT_WRITE(不满足条件)

    1. // Simple Profile Characteristic 3 Properties  
    2. static uint8 simpleProfileChar3Props = GATT_PROP_WRITE;


    1. // Characteristic Value 3  
    2. {   
    3.   { ATT_BT_UUID_SIZE, simpleProfilechar3UUID },  
    4.   GATT_PERMIT_WRITE,   
    5.   0,   
    6.   &simpleProfileChar3   
    7. },


    因此,主机端不能获取char3的特征值句柄。(如果想获取,需要char3修改为GATT_PROP_READ和GATT_PERMIT_READ)

    例子三:

    SimpleBLEPeripheral工程的char4的属性是通知(满足条件)


    1. // Simple Profile Characteristic 4 Properties  
    2. static uint8 simpleProfileChar4Props = GATT_PROP_NOTIFY;


    但是属性表中的属性是0,即不可读不可写(不满足条件)



      1. // Characteristic Value 4  
      2. {   
      3.   { ATT_BT_UUID_SIZE, simpleProfilechar4UUID },  
      4.   0,   
      5.   0,   
      6.   &simpleProfileChar4   
      7. },


      因此,主机端不能获取char4的特征值句柄。(如果想获取,需要char4的属性表修改为GATT_PERMIT_READ)



      2)连续读取特征值句柄时,如果中间某个特征值句柄读取失败,则会导致后续的特征值也读取不到。

      3)char5的属性表的值的属性是GATT_PERMIT_AUTHEN_READ,似乎是加密的,我不熟,就暂时不考虑啦。会用的朋友可以告诉我。

      五、实验结果


      android 蓝牙连接 读取特征值_句柄

      注:char3和char5的特征值我没有去读,而char4则是修改了从机的属性表为GATT_PERMIT_READ才可读到。

      六、此方法存在的问题

      实验中发现:从char1一直读到char6时,由于char3不能读,导致后面几个也读不到了。而屏蔽了char3,后面的就可以读了。

      目前的解决办法:屏蔽不能读的char3和char5,跳过问题。

      待验证的解决方案:用GATT_DiscCharsByUUID函数获取特征值句柄。