蓝牙消息传输_从机(GATT Server)与主机(GATT Cilent)
1.从机(GATT_Server)发送消息(通知)
GATT服务器的抽象
1.APP调用profile函数
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t),&charValue4);
2.profile调用GATTServAPP层函数
GATTServApp_ProcessCharCfg( simpleProfileChar4Config, &simpleProfileChar4, FALSE,
simpleProfileAttrTbl, GATT_NUM_ATTRS( simpleProfileAttrTbl ),
INVALID_TASK_ID, simpleProfile_ReadAttrCB );
3.GATTServApp_ProcessCharCfg调用GATT层函数
通过特征值Value地址找到特征值对应的属性
发送ATT通知/指示
pAttr = GATTServApp_FindAttr( attrTbl, numAttrs, pValue );
if ( pAttr != NULL )
{
if ( pItem->value & GATT_CLIENT_CFG_NOTIFY )
{
// 发送ATT通知/指示
status |= gattServApp_SendNotiInd( pItem->connHandle, GATT_CLIENT_CFG_NOTIFY,
authenticated, pAttr, taskId, pfnReadAttrCB );
}
if ( pItem->value & GATT_CLIENT_CFG_INDICATE )
{
status |= gattServApp_SendNotiInd( pItem->connHandle, GATT_CLIENT_CFG_INDICATE,
authenticated, pAttr, taskId, pfnReadAttrCB );
}
}
4.Readback回调将APP中缓存的要发送的信息(charValue4),同步到协议栈发送(noti.pValue)
status = (*pfnReadAttrCB)( connHandle, pAttr, noti.pValue, ¬i.len,
0, len, GATT_LOCAL_READ );
if ( status == SUCCESS )
{
noti.handle = pAttr->handle;
if ( cccValue & GATT_CLIENT_CFG_NOTIFY )
{
status = GATT_Notification( connHandle, ¬i, authenticated );
}
else // GATT_CLIENT_CFG_INDICATE
{
status = GATT_Indication( connHandle, (attHandleValueInd_t *)¬i,
authenticated, taskId );
}
}
5.GATT_Notification/GATT_Indication完成发送,这两个区别时 indicate 需要主机回应后才能继续发送,而 Notify 不需要。
2.从机(GATT_Server)接收消息
从机的消息接收是,通过回调实现的。
1.将APP的回调函数注册到Profiles层
SimpleProfile_RegisterAppCBs(&SimplePeripheral_simpleProfileCBs);
回调函数函数的作用是,发送SP_CHAR_CHANGE_EVT队列消息
static void SimplePeripheral_charValueChangeCB(uint8_t paramId)
{
uint8_t *pValue = ICall_malloc(sizeof(uint8_t));
if (pValue)
{
*pValue = paramId;
if (SimplePeripheral_enqueueMsg(SP_CHAR_CHANGE_EVT, pValue) != SUCCESS)
{
ICall_free(pValue);
}
}
}
2.将Profiles层的回调函数注册到 GATT Server App层
status = GATTServApp_RegisterService( simpleProfileAttrTbl,
GATT_NUM_ATTRS( simpleProfileAttrTbl ),
GATT_MAX_ENCRYPT_KEY_SIZE,
&simpleProfileCBs );
simpleProfile_WriteAttrCB回调函数的作用是1.将协议栈获得的信息,同步到APP层,2.通知APP层接收到消息
static bStatus_t simpleProfile_WriteAttrCB(uint16_t connHandle,
gattAttribute_t *pAttr,
uint8_t *pValue, uint16_t len,
uint16_t offset, uint8_t method)
{
bStatus_t status = SUCCESS;
uint8 notifyApp = 0xFF;
......
if ( pAttr->type.len == ATT_BT_UUID_SIZE )
{
// 16-bit UUID
uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
switch ( uuid )
{
// 改变特征值
if ( status == SUCCESS )
{
uint8 *pCurValue = (uint8 *)pAttr->pValue;
*pCurValue = pValue[0];
// 判断是哪个特征值
if( pAttr->pValue == &simpleProfileChar1 )
{
notifyApp = SIMPLEPROFILE_CHAR1;
}
else
{
notifyApp = SIMPLEPROFILE_CHAR3;
}
}
break;
case GATT_CLIENT_CHAR_CFG_UUID:
//处理客户端的客户端特征配置ccc写请求
status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
offset, GATT_CLIENT_CFG_NOTIFY );
break;
default:
// Should never get here! (characteristics 2 and 4 do not have write permissions)
status = ATT_ERR_ATTR_NOT_FOUND;
break;
}
}
// 将事件发送给APP层
if ( (notifyApp != 0xFF ) && simpleProfile_AppCBs && simpleProfile_AppCBs->pfnSimpleProfileChange )
{
simpleProfile_AppCBs->pfnSimpleProfileChange( notifyApp );
}
}
在这其中的将事件发送给APP层的回调函数pfnSimpleProfileChange就是SimplePeripheral_charValueChangeCB。
3.当从机(GATT服务器)收到消息是,协议栈回调GATT_Server_App层的回调函数simpleProfile_WriteAttrCB,simpleProfile_WriteAttrCB再回调APP层的函数SimplePeripheral_charValueChangeCB,在APP层SP_CHAR_CHANGE_EVT事件处理中,完成对消息的处理。
static void SimplePeripheral_processCharValueChangeEvt(uint8_t paramId)
{
uint8_t newValue;
switch(paramId)
{
case SIMPLEPROFILE_CHAR1:
SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue);
Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Char 1: %d", (uint16_t)newValue);
break;
case SIMPLEPROFILE_CHAR3:
SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &newValue);
Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Char 3: %d", (uint16_t)newValue);
break;
default:
// should not reach here!
break;
}
}
3.主机(GATT_Cilent)发送消息(ATT指示)
GATT客户端的抽象
GATT客户端通过 GATT_WriteCharValue()完成发送
GATT_WriteCharValue调用成功,在主机(GATT客户端)返回GATT层消息ATT_WRITE_RSP。
static void SimpleCentral_processGATTMsg(gattMsgEvent_t *pMsg)
{
if (linkDB_Up(pMsg->connHandle))
{
//ATT写响应 或 ATT写请求出错
//GATT_WriteCharValue中使用ATT_WriteReq,GATT_WriteCharValue成功则返回ATT_WRITE_RSP
//GATT_WriteCharValue向协议栈发送ATT_WriteReq请求,写成功后协议栈向APP层返回ATT_WRITE_RSP响应
else if ((pMsg->method == ATT_WRITE_RSP) ||
((pMsg->method == ATT_ERROR_RSP) &&
(pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ)))
{
if (pMsg->method == ATT_ERROR_RSP)
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"Write Error %d", pMsg->msg.errorRsp.errCode);
}
else
{
// After a successful write, display the value that was written and
// increment value
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"Write sent: 0x%02x", charVal);
}
tbm_goTo(&scMenuPerConn);
}
}
}
4.主机(GATT_Cilent)接收消息
当收到消息时,协议栈告诉GATT Cilent,有通知到达
1SimpleCentral_task阻塞,直到事件发生
2.判断为GATT协议栈事件
static void SimpleCentral_taskFxn(uintptr_t a0, uintptr_t a1)
{
......
if (pEvt->signature != 0xffff)
{
// Process inter-task message
safeToDealloc = SimpleCentral_processStackMsg((ICall_Hdr *)pMsg);
}
......
}
3.处理接收消息事件
static void SimpleCentral_processGATTMsg(gattMsgEvent_t *pMsg)
{
if (linkDB_Up(pMsg->connHandle))
{
......
//Cilent接收方式1:ATT读响应 或 ATT读请求出错
//GATT_ReadCharValue()函数成功返回ATT_READ_RSP
//对应从机 SimpleProfile_SetParameter()修改特征值的值
else if ((pMsg->method == ATT_READ_RSP) ||
((pMsg->method == ATT_ERROR_RSP) &&
(pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ)))
{
if (pMsg->method == ATT_ERROR_RSP)
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"Read Error %d", pMsg->msg.errorRsp.errCode);
}
else
{
// After a successful read, display the read value
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"Read rsp: 0x%02x", pMsg->msg.readRsp.pValue[0]);
}
}
//Cilent接收方式2.通知
//对应从机的 GATT_Notification()函数
else if ( ( pMsg->method == ATT_HANDLE_VALUE_NOTI ) )
{
}
// Needed only for ATT Protocol messages
GATT_bm_free(&pMsg->msg, pMsg->method);
}
}
因为SimpleProfile_SetParameter()内部也是调用GATT_Notification。
所以主机(GATT_Cilent)接收消息可以分为两种:
1.SimpleProfile_SetParameter可以使用GATT_ReadCharValue(),进而在ATT_READ_RSP消息处理,也可以在直接ATT_HANDLE_VALUE_NOTI 中使用。
2.GATT_Notification在ATT_HANDLE_VALUE_NOTI 中处理
从机GATT服务器发送:GATT_Notification,对应主机GATT客户端的ATT_HANDLE_VALUE_NOTI事件
主机CATT客户端发送:GATT_WriteCharValue,对应从机GATT服务器的SBP_EVEVT_CHANGE_EVENT事件
当特征值具有读、写属性时,GATT_Cilent可以通过 GATT_ReadCharValue、GATT_WriteCharValue 进行读、写 GATT_Server端的特征。
当特征具有CCC通知属性时,GATT_Server端可以主动将特征的值通知给 GATT_Cilent。