admin管理员组

文章数量:1529449

一、简介

本篇以SimpleBLEPeripheral为例,讲述如何在工程中使用indicate指示。


二、实验平台

协议栈版本:BLE-CC254x-1.4.0

编译软件:IAR 8.20.2

硬件平台:Smart RF开发板


三、版权声明

博主:甜甜的大香瓜

声明:喝水不忘挖井人,转载请注明出处。

原文地址:http://blog.csdn/feilusia

联系方式:897503845@qq

技术交流QQ群:127442605


四、简介indicate

1、什么是indicate?

indicate译为“指示”,它是服务器给客户端发送数据的方式。


2、它与notify有什么不同?

它在使用上比notify多一个应答的步骤,如下图:


注:有应答的通信方式“indicate”,更适用于可靠的通信方式。


3、indicate的指示开关如何打开、关闭?


也就是0x0002写到属性为“indicate”的特征值的CCC位,则开指示;如果写0x0000,则关指示。

注:如果写0x0001到属性为“indicate”的特征值的CCC位,无用。


五、实验目的

通过按五项按键的“center”键发送indicate数据给btool,用PC的串口工具打印出“indicate”的“发数据”和“应答”的过程。


六、添加属性为indicate的特征值char7(simpleGATTprofile.c中

1、修改simpleGATTprofile.h 的宏定义



2、添加char7的UUID

[cpp] view plain copy
  1. // Characteristic 7 UUID: 0xFFF7  
  2. CONST uint8 simpleProfilechar7UUID[ATT_BT_UUID_SIZE] =  
  3. {   
  4.   LO_UINT16(SIMPLEPROFILE_CHAR7_UUID), HI_UINT16(SIMPLEPROFILE_CHAR7_UUID)  
  5. };  

3、添加char7的设置属性 [cpp] view plain copy
  1. // Simple Profile Characteristic 7 Properties    
  2. static uint8 simpleProfileChar7Props = GATT_PROP_INDICATE;    
  3.     
  4. // Characteristic 7 Value    
  5. static uint8 simpleProfileChar7[SIMPLEPROFILE_CHAR7_LEN] = {0};    
  6.     
  7. // Simple Profile Characteristic 7 Configuration Each client has its own    
  8. // instantiation of the Client Characteristic Configuration. Reads of the    
  9. // Client Characteristic Configuration only shows the configuration for    
  10. // that client and writes only affect the configuration of that client.    
  11. static gattCharCfg_t simpleProfileChar7Config[GATT_MAX_NUM_CONN];    
  12.     
  13. // Simple Profile Characteristic 7 User Description    
  14. static uint8 simpleProfileChar7UserDesp[17] = "Characteristic 7\0";  

4、属性表修改

1)修改属性表的大小

[cpp] view plain copy
  1. #define SERVAPP_NUM_ATTR_SUPPORTED        25  
具体要看属性表有多少个属性


2)修改属性表

[cpp] view plain copy
  1. // Characteristic 7 Declaration    
  2. {     
  3.   { ATT_BT_UUID_SIZE, characterUUID },    
  4.   GATT_PERMIT_READ,     
  5.   0,    
  6.   &simpleProfileChar7Props     
  7. },    
  8.   
  9.   
  10. // Characteristic Value 7    
  11. {     
  12.   { ATT_BT_UUID_SIZE, simpleProfilechar7UUID },    
  13.   0,    
  14.   0,     
  15.   simpleProfileChar7     
  16. },    
  17.   
  18.   
  19. // Characteristic 7 configuration    
  20. {     
  21.   { ATT_BT_UUID_SIZE, clientCharCfgUUID },    
  22.   GATT_PERMIT_READ | GATT_PERMIT_WRITE,     
  23.   0,     
  24.   (uint8 *)simpleProfileChar7Config     
  25. },    
  26.     
  27. // Characteristic 7 User Description    
  28. {     
  29.   { ATT_BT_UUID_SIZE, charUserDescUUID },    
  30.   GATT_PERMIT_READ,     
  31.   0,     
  32.   simpleProfileChar7UserDesp     
  33. },   

5、修改参数函数

1)在SimpleProfile_SetParameter中添加

[cpp] view plain copy
  1. case SIMPLEPROFILE_CHAR7:      
  2.   if ( len == SIMPLEPROFILE_CHAR7_LEN )       
  3.   {      
  4.     VOID osal_memcpy( simpleProfileChar7, value, SIMPLEPROFILE_CHAR7_LEN );      
  5.   }      
  6.   else      
  7.   {      
  8.     ret = bleInvalidRange;      
  9.   }      
  10.   break;  

2)在SimpleProfile_GetParameter中添加 [cpp] view plain copy
  1. case SIMPLEPROFILE_CHAR7:    
  2.   VOID osal_memcpy( value, simpleProfileChar7, SIMPLEPROFILE_CHAR7_LEN );    
  3.   break;    


6、修改读写特征值函数

1)在simpleProfile_ReadAttrCB中添加

[cpp] view plain copy
  1. case SIMPLEPROFILE_CHAR7_UUID:    
  2.   *pLen = SIMPLEPROFILE_CHAR7_LEN;    
  3.   VOID osal_memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR7_LEN );    
  4.   break;  
以上的添加实际上没什么用,但是跟char4一样统一。

2)在simpleProfile_WriteAttrCB中修改 [cpp] view plain copy
  1. case GATT_CLIENT_CHAR_CFG_UUID:  
  2.   if ( pAttr->handle == simpleProfileAttrTbl[ATTRTBL_CHAR4_CCC_IDX].handle )//CHAR4 NOTIFY  
  3.   {  
  4.     // BloodPressure Notifications  
  5.     status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,  
  6.                                              offset, GATT_CLIENT_CFG_NOTIFY );  
  7.   }  
  8.   else if ( pAttr->handle == simpleProfileAttrTbl[ATTRTBL_CHAR6_CCC_IDX].handle )//CHAR6 NOTIFY   
  9.   {  
  10.     // BloodPressure Notifications  
  11.     status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,  
  12.                                              offset, GATT_CLIENT_CFG_NOTIFY );  
  13.   }  
  14.   else if ( pAttr->handle == simpleProfileAttrTbl[ATTRTBL_CHAR7_CCC_IDX].handle )//CHAR7 INDICATE  
  15.   {  
  16.     // BloodPressure Indications  
  17.     status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,  
  18.                                              offset, GATT_CLIENT_CFG_INDICATE );  
  19.   }          
  20.   else  
  21.   {  
  22.     status = ATT_ERR_INVALID_HANDLE;  
  23.   }          
  24.   
  25.   break;  
注意,这里每个具备notify和indicate属性的都需要添加一个对应的代码,否则CCC开关开不起来。


3)添加char7的CCC在属性表中偏移值的宏

[cpp] view plain copy
  1. #define ATTRTBL_CHAR7_CCC_IDX               23  
注:char4和char6等其他notify、indicate属性的自行添加。这里只添加char7的。


7、修改SimpleProfile_AddService

[cpp] view plain copy
  1. bStatus_t SimpleProfile_AddService( uint32 services )  
  2. {  
  3.   uint8 status = SUCCESS;  
  4.   
  5.   // Initialize Client Characteristic Configuration attributes  
  6.   GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar4Config );  
  7.   GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar6Config );   
  8.   GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar7Config );   
  9.   
  10.   // Register with Link DB to receive link status change callback  
  11.   VOID linkDB_Register( simpleProfile_HandleConnStatusCB );    
  12.     
  13.   if ( services & SIMPLEPROFILE_SERVICE )  
  14.   {  
  15.     // Register GATT attribute list and CBs with GATT Server App  
  16.     status = GATTServApp_RegisterService( simpleProfileAttrTbl,   
  17.                                           GATT_NUM_ATTRS( simpleProfileAttrTbl ),  
  18.                                           &simpleProfileCBs );  
  19.   }  
  20.   
  21.   return ( status );  
  22. }  


8、修改simpleProfile_HandleConnStatusCB [cpp] view plain copy
  1. static void simpleProfile_HandleConnStatusCB( uint16 connHandle, uint8 changeType )  
  2. {   
  3.   // Make sure this is not loopback connection  
  4.   if ( connHandle != LOOPBACK_CONNHANDLE )  
  5.   {  
  6.     // Reset Client Char Config if connection has dropped  
  7.     if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||  
  8.          ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) &&   
  9.            ( !linkDB_Up( connHandle ) ) ) )  
  10.     {   
  11.       GATTServApp_InitCharCfg( connHandle, simpleProfileChar4Config );  
  12.       GATTServApp_InitCharCfg( connHandle, simpleProfileChar6Config );  
  13.       GATTServApp_InitCharCfg( connHandle, simpleProfileChar7Config );        
  14.     }  
  15.   }  
  16. }  

9、修改应用层时初始化特征值的部分 [cpp] view plain copy
  1. {    
  2.   uint8 charValue1 = 1;    
  3.   uint8 charValue2 = 2;    
  4.   uint8 charValue3 = 3;    
  5.   uint8 charValue4 = 4;    
  6.   uint8 charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };    
  7.   uint8 charValue6[SIMPLEPROFILE_CHAR6_LEN] = { 1, 2, 3, 4, 5 };    
  8.   uint8 charValue7[SIMPLEPROFILE_CHAR7_LEN] = { 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, };   
  9.     
  10.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR1, sizeof ( uint8 ), &charValue1 );    
  11.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR2, sizeof ( uint8 ), &charValue2 );    
  12.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR3, sizeof ( uint8 ), &charValue3 );    
  13.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR4, sizeof ( uint8 ), &charValue4 );    
  14.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5 );    
  15.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR6, SIMPLEPROFILE_CHAR6_LEN, charValue6 );        
  16.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR7, SIMPLEPROFILE_CHAR7_LEN, charValue7 );  
  17. }  

七、写indicate函数

1、添加char7的value在属性表中的偏移值的宏(simpleGATTprofile.c)

[cpp] view plain copy
  1. #define ATTRTBL_CHAR7_VALUE_IDX               22   


2、添加indicate指示函数(simpleGATTprofile.c) [cpp] view plain copy
  1. //******************************************************************************    
  2. //name:         SimpleGATTprofile_Char7_Indicate    
  3. //introduce:    指示len长度的数据   
  4. //parameter:    connHandle:连接句柄    
  5. //              pValue:要通知的数据,范围为0~SIMPLEPROFILE_CHAR7_LEN,最多20个字节    
  6. //              len:要通知的数据的长度   
  7. //              taskId:应答时要返回到的那个任务的id  
  8. //return:       none    
  9. //******************************************************************************   
  10. bStatus_t SimpleGATTprofile_Char7_Indicate( uint16 connHandle, uint8 *pValue, uint8 len, uint8 taskId)  
  11. {  
  12.   attHandleValueInd_t  indi;  
  13.   uint16 value;  
  14.   
  15.   value  = GATTServApp_ReadCharCfg( connHandle, simpleProfileChar7Config );//读出CCC的值  
  16.   
  17.   if ( value & GATT_CLIENT_CFG_INDICATE ) //判断是否打开通知开关,打开了则发送数据  
  18.   {  
  19.     indi.handle = simpleProfileAttrTbl[ATTRTBL_CHAR7_VALUE_IDX].handle;  
  20.     indi.len = len;  
  21.     osal_memcpy( indi.value, pValue, len);       //数据  
  22.     return(GATT_Indication( connHandle, &indi, FALSE, taskId ));  
  23.   }  
  24.     
  25.   return(FAILURE);  
  26. }  

3、声明函数(simpleGATTprofile.h) [cpp] view plain copy
  1. //******************************************************************************    
  2. //name:         SimpleGATTprofile_Char7_Indicate    
  3. //introduce:    指示len长度的数据   
  4. //parameter:    connHandle:连接句柄    
  5. //              pValue:要通知的数据,范围为0~SIMPLEPROFILE_CHAR7_LEN,最多20个字节    
  6. //              len:要通知的数据的长度   
  7. //              taskId:应答时要返回到的那个任务的id  
  8. //return:       none    
  9. //******************************************************************************   
  10. bStatus_t SimpleGATTprofile_Char7_Indicate( uint16 connHandle, uint8 *pValue, uint8 len, uint8 taskId);  


八、在应用层使用indicate发送数据(simpleBLEPeripheral.c中)

1、按键处理处simpleBLEPeripheral_HandleKeys添加

[cpp] view plain copy
  1. if ( keys & HAL_KEY_SW_5 )  
  2. {     
  3.   uint16 notify_Handle;   
  4.   uint8 *p = buf[20];   
  5.   uint8 status;  
  6.     
  7.   GAPRole_GetParameter( GAPROLE_CONNHANDLE, &notify_Handle);                //获取Connection Handle   
  8.     
  9.   for(uint8 i = 0; i < 20; i++)       //写一个20字节的测试缓冲区的数据  
  10.   {  
  11.     *(p+i) = i;  
  12.   }  
  13.   
  14.   status = SimpleGATTprofile_Char7_Indicate(notify_Handle, p, 20, simpleBLEPeripheral_TaskID);    
  15.     
  16.   if(status == SUCCESS)  
  17.   {  
  18.      NPI_PrintString("indicate is seccess to send!\r\n");  
  19.   }  
  20.   else  
  21.   {  
  22.      NPI_PrintString("indicate is fail to send!\r\n");      
  23.   }  
  24. }  
status为SUCCESS时,说明主机已经写了0x0002到char7的CCC中,打开了指示开关。没连接、没打开指示开关等原因,则status不为SUCCESS。

调用SimpleGATTprofile_Char7_Indicate函数时,最后一个参数simpleBLEPeripheral_TaskID就是indicate应答会返回的任务ID处,所以indicate应答才会返回应用层。

GATT_Indication函数说明有以下一段注释:

[cpp] view plain copy
  1. *          If the return status from this function is SUCCESS, the calling  
  2. *          application task will receive an OSAL GATT_MSG_EVENT message.  
  3. *          The type of the message will be ATT_HANDLE_VALUE_CFM.  
说明indicate的事件是GATT_MSG_EVENT(下面会用到),消息类型是ATT_HANDLE_VALUE_CFM。

2、应用层事件处理函数添加GATT_MSG_EVENT事件

[cpp] view plain copy
  1. /********************************************************************* 
  2.  * @fn      simpleBLEPeripheral_ProcessOSALMsg 
  3.  * 
  4.  * @brief   Process an incoming task message. 
  5.  * 
  6.  * @param   pMsg - message to process 
  7.  * 
  8.  * @return  none 
  9.  */  
  10. static void simpleBLEPeripheral_ProcessOSALMsg( osal_event_hdr_t *pMsg )  
  11. {  
  12.   switch ( pMsg->event )  
  13.   {  
  14.   //#if defined( CC2540_MINIDK )  
  15.     case KEY_CHANGE:  
  16.       simpleBLEPeripheral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );  
  17.       break;  
  18.   //#endif // #if defined( CC2540_MINIDK )  
  19.   
  20.   case GATT_MSG_EVENT:  
  21.       Indication_ProcessGattMsg( (gattMsgEvent_t *) pMsg );  
  22.       break;  
  23.         
  24.   default:  
  25.     // do nothing  
  26.     break;  
  27.   }  
  28. }  
由于这个事件是底层传上来的,所以会传到系统消息处理的地方。

因此在这里添加一个indicate的消息处理函数。


3、定义一个indicate的消息处理函数

[cpp] view plain copy
  1. /********************************************************************* 
  2.  * @fn      Indication_ProcessGattMsg 
  3.  * 
  4.  * @brief   Process GATT messages 
  5.  * 
  6.  * @return  none 
  7.  */  
  8. static void Indication_ProcessGattMsg( gattMsgEvent_t *pMsg )  
  9. {  
  10.   NPI_PrintString("Indication_ProcessGattMsg\r\n");  
  11. }  

4、声明indicate的消息处理函数 [cpp] view plain copy
  1. static void Indication_ProcessGattMsg( gattMsgEvent_t *pMsg );  

九、实验结果

1、btool与Smart RF连接后,按五向按键的“center”键


显示错误是正常,因为此时指示开关还没有打开。


2、打开指示开关

方法一(主机端打开指示开关):


0x003A是char7的CCC的特征值句柄,往里写0x0002打开指示开关。


方法二(从机端打开指示开关):

[cpp] view plain copy
  1. GATTServApp_WriteCharCfg(connHandle, simpleProfileChar7Config, 0x0002);  
不建议从机端自己打开此开关,应尽量保证主机来控制从机。


3、再按五向按键的“center”键发送indicate数据


看串口工具:

“indicate is seccess to send!”代表Smart RF发送indicate数据发送成功。

“Indication_ProcessGattMsg”则代表btool返回的应答消息到达了应用层。

看btool:

收到了Smart RF发出的20个字节的数据。


所以,实验成功~

本文标签: 指示