GATT服务的发现由主机执行,一共三个阶段
1.处理交换 MTU 请求和响应,启动对 Simple Service 服务的发现。
if (discState == BLE_DISC_STATE_MTU)
{
// MTU size response received, discover simple service
if (pMsg->method == ATT_EXCHANGE_MTU_RSP)
{
//获取主服务的UUID
uint8_t uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_SERV_UUID),
HI_UINT16(SIMPLEPROFILE_SERV_UUID) };
discState = BLE_DISC_STATE_SVC;
// 通过UUID寻找主服务----simple service
VOID GATT_DiscPrimaryServiceByUUID(pMsg->connHandle, uuid,
ATT_BT_UUID_SIZE, selfEntity);
}
}
2.处理发现服务的响应,并存储服务的开始和结束句柄。服务发现过程完成后,启动对特定特征(Characteristic)的发现。
else if (discState == BLE_DISC_STATE_SVC)
{
// Service found, store handles
if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
pMsg->msg.findByTypeValueRsp.numInfo > 0)
{
//存储服务的开始和结束句柄
svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
}
// If procedure complete
if (((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP) &&
(pMsg->hdr.status == bleProcedureComplete)) ||
(pMsg->method == ATT_ERROR_RSP))
{
if (svcStartHdl != 0)
{
attReadByTypeReq_t req;
// Discover characteristic
discState = BLE_DISC_STATE_CHAR;
req.startHandle = svcStartHdl;
req.endHandle = svcEndHdl;
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);
//根据服务句柄范围和特征UUID,客户端发现服务器上的服务特征
VOID GATT_DiscCharsByUUID(pMsg->connHandle, &req, selfEntity);
}
}
}
3.处理发现特征的响应,并存储特征值的句柄。如果特征发现过程完成,触发相应的操作,比如启动通知。
else if (discState == BLE_DISC_STATE_CHAR)
{
// Characteristic found, store handle
if ((pMsg->method == ATT_READ_BY_TYPE_RSP) &&
(pMsg->msg.readByTypeRsp.numPairs > 0))
{
//有多个连接时,获取连接句柄
uint8_t connIndex = SimpleCentral_getConnIndex(scConnHandle);
// connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
// Store the handle of the simpleprofile characteristic 1 value
//获取特征句柄
connList[connIndex].charHandle
= BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[3],
pMsg->msg.readByTypeRsp.pDataList[4]);
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Simple Svc Found");
// Now we can use GATT Read/Write
tbm_setItemStatus(&scMenuPerConn,
SC_ITEM_GATTREAD | SC_ITEM_GATTWRITE, SC_ITEM_NONE);
}
discState = BLE_DISC_STATE_IDLE;
}
通过GATT_Cilent调用三次SimpleCentral_processGATTDiscEvent(gattMsgEvent_t *pMsg)实现。
因为Handle是由协议栈分配的连续的,所以获取其中一个的Handle就能知道其它的。
区分特征与特征值,特征由特征值、特征声明、用户特征描述、客户特征配置组成,每个组成部分有各自的Handle值
比如与一个连接的两个通知类型的特征值进行数据发送时,获取了第一个特征值的句柄,存储在connList[i].charHandle中。
第一个特征的CCC为charHandle+1
第二个特征的CCC为charHandle+5
if(index ==SIMPLEPROFILE_CHAR1)
{
req.handle = connList[i].charHandle+1;
}
else if(index ==SIMPLEPROFILE_CHAR2)
{
req.handle = connList[i].charHandle+5;
}
主从机发送消息时,需要将APP的信息发送给协议栈来发送,所以APP的信息要和协议栈的信息在发送和接受时完成同步,APP中的消息类似于缓存。
主从消息传输可以通过通知的方式,Notification有两种使用情况,都是由从机发起的
1)GATT_Notification
用在通信时,由从机主动通知,且不需要主机发出请求和回应。
2)GATTServApp_ProcessCharCfg
用在通知启动时,需要主机发送一次“通知请求”给从机,从机收到“通知请求”才发送通知。
1.从机完成通知的初始化
// 分配客户端特征配置表
simpleProfileChar4Config = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) *
MAX_NUM_BLE_CONNS );
if ( simpleProfileChar4Config == NULL )
{
return ( bleMemAllocError );
}
// Initialize Client Characteristic Configuration attributes
//初始化客户端特征配置表CCC
GATTServApp_InitCharCfg( CONNHANDLE_INVALID, simpleProfileChar4Config );
if ( services & SIMPLEPROFILE_SERVICE )
{
// Register GATT attribute list and CBs with GATT Server App
//注册GATT属性列表和CBs与 GATT Server App回调函数
status = GATTServApp_RegisterService( simpleProfileAttrTbl,
GATT_NUM_ATTRS( simpleProfileAttrTbl ),
GATT_MAX_ENCRYPT_KEY_SIZE,
&simpleProfileCBs );
}
在GATTServApp_InitCharCfg中,使用给定的 connHandle 在 charCfgTbl 中搜索,以找到与连接相关的特征配置项,并将该CCC初始化为GATT_CFG_NO_OPERATION0.
gattCharCfg_t *pItem = gattServApp_FindCharCfgItem( connHandle, charCfgTbl );
if ( pItem != NULL )
{
pItem->connHandle = CONNHANDLE_INVALID;
pItem->value = GATT_CFG_NO_OPERATION;
}
2.从机/主机发送一次通知请求,从机开始发送通知,CCC设置为通知:GATT_CLIENT_CFG_NOTIFY
status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
offset, GATT_CLIENT_CFG_NOTIFY );