参考瓜哥的文档,我这里搬运一下:

七、实验步骤
1、增加多个特征值状态的宏(sImpleBLECentral.c 中) 
// Discovery states
enum
{
 BLE_DISC_STATE_IDLE, // Idle
 BLE_DISC_STATE_SVC, // Service discovery
 BLE_DISC_STATE_CHAR1, // Characteristic discovery 1
 BLE_DISC_STATE_CHAR2, // Characteristic discovery 2
 BLE_DISC_STATE_CHAR3, // Characteristic discovery 3
 BLE_DISC_STATE_CHAR4, // Characteristic discovery 4
 BLE_DISC_STATE_CHAR5, // Characteristic discovery 5 
 BLE_DISC_STATE_CHAR6 // Characteristic discovery 6
};
默认的特征值状态的宏只有 BLE_DISC_STATE_CHAR,因此在这里修改为 6 个。(具体要看从
机有几个可读特征值)
2、定义一个自己存放特征句柄的数组(sImpleBLECentral.c 中) 
// Discovered characteristic handle
static uint16 simpleBLECharHdl = 0;
static uint16 GUA_charHdl[6] = {0}; //6 个特征值句柄保存位置
当 然 也 可以 直 接修 改 为 simpleBLECharHdl[6],但 这 样需 要 修改 代 码中 其 他用 到
simpleBLECharHdl 的地方。
为了省事,很懒的我自己定义一个 GUA_charHdl[6]。
简单粗暴学蓝牙 4.0(CC2541 篇)
3、修改发现事件的处理函数 simpleBLEGATTDiscoveryEvent(sImpleBLECentral.c 中) 
/*********************************************************************
* @fn simpleBLEGATTDiscoveryEvent
*
* @brief Process GATT discovery event
*
* @return none
*/
static void simpleBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg )
{
 attReadByTypeReq_t req;
 
 if ( simpleBLEDiscState == BLE_DISC_STATE_SVC )
 {
 // Service found, store handles
 if ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
 pMsg->msg.findByTypeValueRsp.numInfo > 0 )
 {
 simpleBLESvcStartHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].handle;
 simpleBLESvcEndHdl = 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 ( simpleBLESvcStartHdl != 0 )
 {
 // Discover characteristic
 simpleBLEDiscState = BLE_DISC_STATE_CHAR1;
 
 req.startHandle = simpleBLESvcStartHdl;
 req.endHandle = simpleBLESvcEndHdl;
 req.type.len = ATT_BT_UUID_SIZE;
 req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
 req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);
 GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId );
 }
 }
 }
 
第 249 页
简单粗暴学蓝牙 4.0(CC2541 篇)
 else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR1 ) //发现 char1
 {
 //读出 char1 的 handle 并保存到 GUA_charHdl
 if ( pMsg->method == ATT_READ_BY_TYPE_RSP && 
 pMsg->msg.readByTypeRsp.numPairs > 0 )
 {
 GUA_charHdl[0] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],
 pMsg->msg.readByTypeRsp.dataList[1] );
 simpleBLEProcedureInProgress = TRUE; //此时仍在进程中
 }
 //发送命令读取下一个特征值的句柄 
 else{ //注意这里一定要 else,当
numPairs=0 时才能再读下一个,下同 
 simpleBLEDiscState = BLE_DISC_STATE_CHAR2;
 
 req.startHandle = simpleBLESvcStartHdl;
 req.endHandle = simpleBLESvcEndHdl;
 req.type.len = ATT_BT_UUID_SIZE;
 req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR2_UUID);
 req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR2_UUID);
 GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId );
 }
 } 
 
 else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR2 ) //发现 char2
 {
 //读出 char2 的 handle 并保存到 GUA_charHdl
 if ( pMsg->method == ATT_READ_BY_TYPE_RSP && 
 pMsg->msg.readByTypeRsp.numPairs > 0 )
 {
 GUA_charHdl[1] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],
 pMsg->msg.readByTypeRsp.dataList[1] );
 simpleBLEProcedureInProgress = TRUE; //此时仍在进程中
 } 第 250 页
简单粗暴学蓝牙 4.0(CC2541 篇)
 //发送命令读取下一个特征值的句柄 
 else{ 
 simpleBLEDiscState = BLE_DISC_STATE_CHAR4;
 
 req.startHandle = simpleBLESvcStartHdl;
 req.endHandle = simpleBLESvcEndHdl;
 req.type.len = ATT_BT_UUID_SIZE;
 req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR4_UUID);
 req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR4_UUID);
 GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId ); 
 }
 } 
/* 
 else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR3 ) //发现 char3
 {
 //读出 char3 的 handle 并保存到 GUA_charHdl
 if ( pMsg->method == ATT_READ_BY_TYPE_RSP && 
 pMsg->msg.readByTypeRsp.numPairs > 0 )
 {
 GUA_charHdl[2] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],
 pMsg->msg.readByTypeRsp.dataList[1] );
 simpleBLEProcedureInProgress = TRUE; //此时仍在进程中
 }
 //发送命令读取下一个特征值的句柄 
 else{ 
 simpleBLEDiscState = BLE_DISC_STATE_CHAR4;
 
 req.startHandle = simpleBLESvcStartHdl;
 req.endHandle = simpleBLESvcEndHdl;
 req.type.len = ATT_BT_UUID_SIZE;
 req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR4_UUID);
 req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR4_UUID);
 GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId ); 
 }
 } 
*/
第 251 页
简单粗暴学蓝牙 4.0(CC2541 篇)
 else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR4 ) //发现 char4
 {
 //读出 char3 的 handle 并保存到 GUA_charHdl
 if ( pMsg->method == ATT_READ_BY_TYPE_RSP && 
 pMsg->msg.readByTypeRsp.numPairs > 0 )
 {
 GUA_charHdl[3] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],
 pMsg->msg.readByTypeRsp.dataList[1] );
 simpleBLEProcedureInProgress = TRUE; //此时仍在进程中
 }
 
 //发送命令读取下一个特征值的句柄 
 else{ 
 simpleBLEDiscState = BLE_DISC_STATE_CHAR6;
 
 req.startHandle = simpleBLESvcStartHdl;
 req.endHandle = simpleBLESvcEndHdl;
 req.type.len = ATT_BT_UUID_SIZE;
 req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR6_UUID);
 req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR6_UUID);
 GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId ); 
 }
 } 
/*
 else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR5 ) //发现 char5
 {
 //读出 char3 的 handle 并保存到 GUA_charHdl
 if ( pMsg->method == ATT_READ_BY_TYPE_RSP && 
 pMsg->msg.readByTypeRsp.numPairs > 0 )
 {
 GUA_charHdl[4] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],
 pMsg->msg.readByTypeRsp.dataList[1] );
 simpleBLEProcedureInProgress = TRUE; //此时仍在进程中
 }
 
 
 //发送命令读取下一个特征值的句柄 
 else{ 
第 252 页
简单粗暴学蓝牙 4.0(CC2541 篇)
第 253 页
 simpleBLEDiscState = BLE_DISC_STATE_CHAR6;
 
 req.startHandle = simpleBLESvcStartHdl;
 req.endHandle = simpleBLESvcEndHdl;
 req.type.len = ATT_BT_UUID_SIZE;
 req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR6_UUID);
 req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR6_UUID);
 GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId ); 
 } 
 } 
*/ 
 else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR6 ) //发现 char6
 {
 // Characteristic found, store handle
 if ( pMsg->method == ATT_READ_BY_TYPE_RSP && 
 pMsg->msg.readByTypeRsp.numPairs > 0 )
 {
 GUA_charHdl[5] = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],
 pMsg->msg.readByTypeRsp.dataList[1] );
 
 LCD_WRITE_STRING( "Char5 Found", HAL_LCD_LINE_6 );
 simpleBLEProcedureInProgress = FALSE; //注意最后一个特征值时需要赋
值
 }
 
 simpleBLEDiscState = BLE_DISC_STATE_IDLE; //读完最后的 char6,就可以返
回闲置模式了
 
 } 
}
注意:
1)主机端只能获得“特征值属性为读、通知,并且属性表中为可读”的特征值句柄。
说的通俗一点,就是特征值的属性要为 GATT_PROP_READ 或 GATT_PROP_NOTIFY,且属性表中
对应的值的属性要为 GATT_PERMIT_READ,主机端才能获取到它的特征值句柄。
例子一:
SimpleBLEPeripheral 工程的 char1 的属性是可读可写(满足条件)
// Simple Profile Characteristic 1 Properties
static uint8 simpleProfileChar1Props = GATT_PROP_READ | GATT_PROP_WRITE;
简单粗暴学蓝牙 4.0(CC2541 篇)
属性表中的属性是 GATT_PERMIT_READ(满足条件)
 // Characteristic Value 1
 { 
 { ATT_BT_UUID_SIZE, simpleProfilechar1UUID },
 GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
 0, 
 &simpleProfileChar1 
 },
因此,主机端可获取到同时满足两个条件的 char1 的特征值句柄。
例子二:
SimpleBLEPeripheral 工程的 char3 的属性是可写,不可读(不满足条件)
// Simple Profile Characteristic 3 Properties
static uint8 simpleProfileChar3Props = GATT_PROP_WRITE;
并且属性表中的属性也是 GATT_PERMIT_WRITE(不满足条件)
 // Characteristic Value 3
 { 
 { ATT_BT_UUID_SIZE, simpleProfilechar3UUID },
 GATT_PERMIT_WRITE, 
 0, 
 &simpleProfileChar3 
 },
因此,主机端不能获取 char3 的特征值句柄。(如果想获取,需要 char3 修改为
GATT_PROP_READ 和 GATT_PERMIT_READ)
例子三:
SimpleBLEPeripheral 工程的 char4 的属性是通知(满足条件)
// Simple Profile Characteristic 4 Properties
static uint8 simpleProfileChar4Props = GATT_PROP_NOTIFY;
但是属性表中的属性是 0,即不可读不可写(不满足条件)
 // Characteristic Value 4
 { 
 { ATT_BT_UUID_SIZE, simpleProfilechar4UUID },
 0, 
 0, 
 &simpleProfileChar4 
 },
因此,主机端不能获取 char4 的特征值句柄。(如果想获取,需要 char4 的属性表修改为
GATT_PERMIT_READ) 2)连续读取特征值句柄时,如果中间某个特征值句柄读取失败,则会导致后续的特征值也
读取不到。
3)char5 的属性表的值的属性是 GATT_PERMIT_AUTHEN_READ,是加密的,需要主机端输入密
第 254 页
简单粗暴学蓝牙 4.0(CC2541 篇)
第 255 页
码后方可读取。