<蓝牙BLE>cc2540如何添加特征值

声明,本文转载自“甜甜的大香瓜”的博客,原文地址如下:

http://blog.csdn.net/feilusia/article/details/48314165


一、简介

本篇介绍从机如何添加一个可读、可写、可通知的特征值char6。

(为了方便copy代码,就不大量使用截图了)


二、特征值有什么用?

它是一个变量或者一个数组,主从机之间传输应用层数据,都是通过特征值。

比如添加一个char6[5],它的值初始化为1、2、3、4、5,

当char6具有读、写属性时,主机可以通过GATT_ReadCharValue和GATT_WriteCharValue读写从机的这个char6。

当char6具有notify通知属性时,从机可以将char6的值通知给主机。(通知的两种方式可参见本博客的《CC2541的notify通知》)


三、添加步骤

1、修改simpleGATTprofile.h 的宏定义

<蓝牙BLE>cc2540如何添加特征值_第1张图片


2、添加char6的UUID

<蓝牙BLE>cc2540如何添加特征值_第2张图片

将16位的UUID拆成2个字节放到数组里。


3、添加char6的设置属性

[cpp]  view plain copy
  1. // Simple Profile Characteristic 6 Properties  
  2. static uint8 simpleProfileChar6Props = GATT_PROP_READ | GATT_PROP_WRITE | GATT_PROP_NOTIFY;  
  3.   
  4. // Characteristic 6 Value  
  5. static uint8 simpleProfileChar6[SIMPLEPROFILE_CHAR6_LEN] = {0};  
  6.   
  7. // Simple Profile Characteristic 6 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 simpleProfileChar6Config[GATT_MAX_NUM_CONN];  
  12.   
  13. // Simple Profile Characteristic 6 User Description  
  14. static uint8 simpleProfileChar6UserDesp[17] = "Characteristic 6\0";  

由于属性包含GATT_PROP_NOTIFY方式,所以必须要有个通知开关simpleProfileChar6Config。


4、属性表修改

1)修改属性表的大小

在simpleGATTprofile.c中将属性表大小由17改成21(增加上面定义的char6的4个属性变量):

[cpp]  view plain copy
  1. #define SERVAPP_NUM_ATTR_SUPPORTED        21  


2)修改属性表
[cpp]  view plain copy
  1.       // Characteristic 6 Declaration  
  2.       {   
  3.         { ATT_BT_UUID_SIZE, characterUUID },  
  4.         GATT_PERMIT_READ,   
  5.         0,  
  6.         &simpleProfileChar6Props   
  7.       },  
  8.   
  9.   
  10.       // Characteristic Value 6  
  11.       {   
  12.         { ATT_BT_UUID_SIZE, simpleProfilechar6UUID },  
  13.         GATT_PERMIT_READ | GATT_PERMIT_WRITE,  
  14.         0,   
  15.         simpleProfileChar6   
  16.       },  
  17.   
  18.   
  19.       // Characteristic 6 configuration  
  20.       {   
  21.         { ATT_BT_UUID_SIZE, clientCharCfgUUID },  
  22.         GATT_PERMIT_READ | GATT_PERMIT_WRITE,   
  23.         0,   
  24.         (uint8 *)simpleProfileChar6Config   
  25.       },  
  26.         
  27.       // Characteristic 6 User Description  
  28.       {   
  29.         { ATT_BT_UUID_SIZE, charUserDescUUID },  
  30.         GATT_PERMIT_READ,   
  31.         0,   
  32.         simpleProfileChar6UserDesp   
  33.       },  
此处注意两点:

第一点,

读、写属性的只有3个变量,而含有notify属性的特征值会多一个开关config,所以是4个变量。


第二点,

大多数新手搞不清楚特征值属性的“GATT_PROP_READ”与属性表的“GATT_PERMIT_READ”的区别:

打个比方说明,

属性表是一列火车,它有SERVAPP_NUM_ATTR_SUPPORTED这么多节车厢,GATT_PERMIT_READ是每节车厢的钥匙。

此时第18节~21节车厢装的是宝箱char6,GATT_PROP_READ是宝箱char6的钥匙。

虽然两把都是钥匙,但是作用的对象不一样。

实际上GATT_PERMIT_READ是针对属性表使用的,而GATT_PROP_READ是针对特征值使用的。


5、修改参数函数

1)在SimpleProfile_SetParameter中添加

[cpp]  view plain copy
  1.     case SIMPLEPROFILE_CHAR6:    
  2.       if ( len == SIMPLEPROFILE_CHAR6_LEN )     
  3.       {    
  4.         VOID osal_memcpy( simpleProfileChar6, value, SIMPLEPROFILE_CHAR6_LEN );   
  5.   
  6.   
  7.         // See if Notification has been enabled  
  8.         GATTServApp_ProcessCharCfg( simpleProfileChar6Config, simpleProfileChar6, FALSE,  
  9.                                     simpleProfileAttrTbl, GATT_NUM_ATTRS( simpleProfileAttrTbl ),  
  10.                                     INVALID_TASK_ID );   
  11.       }    
  12.       else    
  13.       {    
  14.         ret = bleInvalidRange;    
  15.       }    
  16.       break;  

从机可通过此函数写数值到char6中。

每次写char6的值时都会通过GATTServApp_ProcessCharCfg函数把数据notify出来。

PS:此函数使用请参看博文《CC2541的notify通知》。


2)在SimpleProfile_GetParameter中添加

[cpp]  view plain copy
  1. case SIMPLEPROFILE_CHAR6:  
  2.   VOID osal_memcpy( value, simpleProfileChar6, SIMPLEPROFILE_CHAR6_LEN );  
  3.   break;  
从机可通过此函数读出char6的值。

6、修改读写特征值函数

1)在simpleProfile_ReadAttrCB中添加

[cpp]  view plain copy
  1. case SIMPLEPROFILE_CHAR6_UUID:  
  2.   *pLen = SIMPLEPROFILE_CHAR6_LEN;  
  3.   VOID osal_memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR6_LEN );  
  4.   break;  

特征值被主机读取时,从机会自动调用此回调函数。


2)在simpleProfile_WriteAttrCB中添加

[cpp]  view plain copy
  1. case SIMPLEPROFILE_CHAR6_UUID:  
  2.   
  3. //Validate the value  
  4. // Make sure it's not a blob oper  
  5. if ( offset == 0 )  
  6. {  
  7.   if ( len != SIMPLEPROFILE_CHAR6_LEN )  
  8.   {  
  9.     status = ATT_ERR_INVALID_VALUE_SIZE;  
  10.   }  
  11. }  
  12. else  
  13. {  
  14.   status = ATT_ERR_ATTR_NOT_LONG;  
  15. }  
  16.   
  17. //Write the value  
  18. if ( status == SUCCESS )  
  19. {  
  20.   VOID osal_memcpy( pAttr->pValue, pValue, SIMPLEPROFILE_CHAR6_LEN );  
  21.   notifyApp = SIMPLEPROFILE_CHAR6;  
  22. }  
  23.        
  24. break;  

特征值被主机写入时,从机会自动调用此回调函数。


3)在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.     
  15.   else  
  16.   {  
  17.     status = ATT_ERR_INVALID_HANDLE;  
  18.   }          
  19.   
  20.   break;  

再添加两个CCC的宏

[cpp]  view plain copy
  1. #define ATTRTBL_CHAR4_CCC_IDX               12       
  2. #define ATTRTBL_CHAR6_CCC_IDX               19  



7、修改回调函数simpleProfileChangeCB

[cpp]  view plain copy
  1. static void simpleProfileChangeCB( uint8 paramID )  
  2. {  
  3.   uint8 newValue;  
  4.   uint8 Char6_Value[5];  
  5.     
  6.   switch( paramID )  
  7.   {  
  8.     case SIMPLEPROFILE_CHAR1:  
  9.       SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR1, &newValue );  
  10.   
  11.       #if (defined HAL_LCD) && (HAL_LCD == TRUE)  
  12.         HalLcdWriteStringValue( "Char 1:", (uint16)(newValue), 10,  HAL_LCD_LINE_3 );  
  13.       #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)  
  14.   
  15.       break;  
  16.   
  17.     case SIMPLEPROFILE_CHAR3:  
  18.       SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR3, &newValue );  
  19.   
  20.       #if (defined HAL_LCD) && (HAL_LCD == TRUE)  
  21.         HalLcdWriteStringValue( "Char 3:", (uint16)(newValue), 10,  HAL_LCD_LINE_3 );  
  22.       #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)  
  23.   
  24.       break;  
  25.   
  26.     case SIMPLEPROFILE_CHAR6:  
  27.       SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR6, &Char6_Value );  
  28.   
  29.       #if (defined HAL_LCD) && (HAL_LCD == TRUE)  
  30.         HalLcdWriteStringValue( "Char 6:", (uint16)(Char6_Value), 10,  HAL_LCD_LINE_3 );  
  31.       #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)  
  32.   
  33.       break;  
  34.         
  35.     default:  
  36.       // should not reach here!  
  37.       break;  
  38.   }  
  39. }  

实际上在特征值被主机修改后,先调用了simpleProfile_WriteAttrCB,并且在函数末尾再调用了simpleProfileChangeCB。

simpleProfileChangeCB函数才是真正做“特征值改变后的处理”的地方,simpleProfile_WriteAttrCB只是作为中介。

在此处我只做了个简单处理,一旦char6的值改变了,则把char6的值显示在LCD上(前提当然是LCD能用)。


8、修改周期函数performPeriodicTask

[cpp]  view plain copy
  1. static void performPeriodicTask( void )  
  2. {  
  3.   uint8 valueToCopy;  
  4.   uint8 stat;  
  5.   uint8 char6_value[SIMPLEPROFILE_CHAR6_LEN]={0};  
  6.   
  7.   SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR6, char6_value);                               //读出char6的值  
  8.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR6, SIMPLEPROFILE_CHAR6_LEN, char6_value);      //将char6的值notify给主机  
  9.     
  10.   // Call to retrieve the value of the third characteristic in the profile  
  11.   stat = SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR3, &valueToCopy);  
  12.   
  13.   if( stat == SUCCESS )  
  14.   {  
  15.     /* 
  16.      * Call to set that value of the fourth characteristic in the profile. Note 
  17.      * that if notifications of the fourth characteristic have been enabled by 
  18.      * a GATT client device, then a notification will be sent every time this 
  19.      * function is called. 
  20.      */  
  21.     SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR4, sizeof(uint8), &valueToCopy);  
  22.   }  
  23. }  
每5秒钟就会把char6的值通知一次。(需要BTOOL中打开通知开关)


9、修改应用层初始化SimpleBLEPeripheral_Init

[cpp]  view plain copy
  1. // Setup the SimpleProfile Characteristic Values  
  2. {  
  3.   uint8 charValue1 = 1;  
  4.   uint8 charValue2 = 2;  
  5.   uint8 charValue3 = 3;  
  6.   uint8 charValue4 = 4;  
  7.   uint8 charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };  
  8.   uint8 charValue6[SIMPLEPROFILE_CHAR6_LEN] = { 1, 2, 3, 4, 5 };      
  9.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR1, sizeof ( uint8 ), &charValue1 );  
  10.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR2, sizeof ( uint8 ), &charValue2 );  
  11.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR3, sizeof ( uint8 ), &charValue3 );  
  12.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR4, sizeof ( uint8 ), &charValue4 );  
  13.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5 );  
  14.   SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR6, SIMPLEPROFILE_CHAR6_LEN, charValue6 );      
  15. }  

特征值在定义时初始化的值都是0,在此处才是正式初始化数值。


10、修改SimpleProfile_AddService(有notify属性才需要此步骤)

[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.     
  9.   // Register with Link DB to receive link status change callback  
  10.   VOID linkDB_Register( simpleProfile_HandleConnStatusCB );    
  11.     
  12.   if ( services & SIMPLEPROFILE_SERVICE )  
  13.   {  
  14.     // Register GATT attribute list and CBs with GATT Server App  
  15.     status = GATTServApp_RegisterService( simpleProfileAttrTbl,   
  16.                                           GATT_NUM_ATTRS( simpleProfileAttrTbl ),  
  17.                                           &simpleProfileCBs );  
  18.   }  
  19.   
  20.   return ( status );  
  21. }  

初始化特征值配置时用的是INVALID_CONNHANDLE,此时任何连接的主机都不能开关从机的通知功能。(此处待定!!!!)

(后续验证:发现此处不修改也可以开从机的通知功能。)


10、修改simpleProfile_HandleConnStatusCB(有notify属性才需要此步骤)

[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.     }  
  14.   }  
  15. }  
当连接改变时,重新将新的连接句柄和通知开关对应起来,此时只有当前连接机子才能开关通知。(此处待定!!!!)

(后续验证:发现此处不修改也可以开从机的通知功能。)


此时就可以用BTOOL、手机进行测试读、写、通知功能了。


你可能感兴趣的:(蓝牙,特征值,BLE)