【BLE】CC2541之添加特征值

本篇博文最后修改时间:2017年03月21日,15:32。


一、简介

本文以SimpleBLEPeripheral工程为例,介绍如何添加一个可读、可写、可通知、20字节长的特征值char6,并用app实现数据的收发。


二、实验平台

协议栈版本:BLE-CC254x-1.4.0

编译软件: IAR 8.20.2

硬件平台: Smart RF开发板(主芯片CC2541)

手机型号: 小米4S

安卓版本:安卓5.1

安卓app:TruthBlue2_1


版权声明

博主:甜甜的大香瓜

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

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

联系方式:897503845@qq.com

香瓜BLE之CC2541群:127442605

香瓜BLE之CC2640群:557278427

香瓜BLE之Android群:541462902

香瓜单片机之STM8/STM32群:164311667
香瓜单片机之Linux群:512598061
香瓜单片机之职场交流群:450154342
甜甜的大香瓜的小店(淘宝店):https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i

四、 实验前提
1、在进行本文步骤前,请先 阅读 以下博文:
暂无

2、在进行本文步骤前,请先 实现以下博文:
暂无


五、基础知识

1、特征值是什么?

答:特征值是一个变量或者一个数组,它被定义在server端,它是client端与server端之间传输数据的缓冲区。

比如添加一个char6[20],它的值初始化为1、2、3、4、5、6、7、8、9、10、11、12、13、14、15、16、17、18、19、20。

当char6具有读、写属性时,client端可以通过GATT_ReadCharValue、GATT_WriteCharValue进行读、写server端的char6。

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


六、实验步骤

1、增加char6的宏定义(替换simpleGATTprofile.h中CONSTANTS段部分

// Profile Parameters
#define SIMPLEPROFILE_CHAR1                   0  // RW uint8 - Profile Characteristic 1 value 
#define SIMPLEPROFILE_CHAR2                   1  // RW uint8 - Profile Characteristic 2 value
#define SIMPLEPROFILE_CHAR3                   2  // RW uint8 - Profile Characteristic 3 value
#define SIMPLEPROFILE_CHAR4                   3  // RW uint8 - Profile Characteristic 4 value
#define SIMPLEPROFILE_CHAR5                   4  // RW uint8 - Profile Characteristic 5 value
#define SIMPLEPROFILE_CHAR6                   5  // RW uint8 - Profile Characteristic 6 value //GUA 
  
// Simple Profile Service UUID
#define SIMPLEPROFILE_SERV_UUID               0xFFF0
    
// Key Pressed UUID
#define SIMPLEPROFILE_CHAR1_UUID            0xFFF1
#define SIMPLEPROFILE_CHAR2_UUID            0xFFF2
#define SIMPLEPROFILE_CHAR3_UUID            0xFFF3
#define SIMPLEPROFILE_CHAR4_UUID            0xFFF4
#define SIMPLEPROFILE_CHAR5_UUID            0xFFF5
#define SIMPLEPROFILE_CHAR6_UUID            0xFFF6      //GUA
  
// Simple Keys Profile Services bit fields
#define SIMPLEPROFILE_SERVICE               0x00000001

// Length of Characteristic 5 in bytes
#define SIMPLEPROFILE_CHAR5_LEN           5  

// Length of Characteristic 6 in bytes
#define SIMPLEPROFILE_CHAR6_LEN           20       //GUA

2、增加char6的UUID(simpleGATTprofile.c的GLOBAL VARIABLES段中

// Characteristic 6 UUID: 0xFFF6
CONST uint8 simpleProfilechar6UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(SIMPLEPROFILE_CHAR6_UUID), HI_UINT16(SIMPLEPROFILE_CHAR6_UUID)
};
将16位的UUID拆成2个字节放到数组里。


3、增加char6的配置属性simpleGATTprofile.c的Profile Attributes - variables段中

// Simple Profile Characteristic 6 Properties
static uint8 simpleProfileChar6Props = GATT_PROP_READ | GATT_PROP_WRITE | GATT_PROP_NOTIFY;

// Characteristic 6 Value
static uint8 simpleProfileChar6[SIMPLEPROFILE_CHAR6_LEN] = {0};

// Simple Profile Characteristic 6 Configuration Each client has its own
// instantiation of the Client Characteristic Configuration. Reads of the
// Client Characteristic Configuration only shows the configuration for
// that client and writes only affect the configuration of that client.
static gattCharCfg_t simpleProfileChar6Config[GATT_MAX_NUM_CONN];

// Simple Profile Characteristic 6 User Description
static uint8 simpleProfileChar6UserDesp[17] = "Characteristic 6\0";

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


4、修改属性表

1)修改属性表的大小simpleGATTprofile.c的CONSTANTS段中

#define SERVAPP_NUM_ATTR_SUPPORTED              21  
增加上面定义的char6的4个属性变量。


2)修改属性表simpleGATTprofile.c的simpleProfileAttrTbl数组中
      // Characteristic 6 Declaration
      { 
        { ATT_BT_UUID_SIZE, characterUUID },
        GATT_PERMIT_READ, 
        0,
        &simpleProfileChar6Props 
      },

      // Characteristic Value 6
      { 
        { ATT_BT_UUID_SIZE, simpleProfilechar6UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0, 
        simpleProfileChar6 
      },

      // Characteristic 6 configuration
      { 
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        (uint8 *)simpleProfileChar6Config 
      },
      
      // Characteristic 6 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        simpleProfileChar6UserDesp 
      },
此处注意两点:

第一点,

读、写属性的只有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)增加char6的数值可设置的处理simpleGATTprofile.c的SimpleProfile_SetParameter函数

    case SIMPLEPROFILE_CHAR6:  
      if ( len == SIMPLEPROFILE_CHAR6_LEN )   
      {  
        VOID osal_memcpy( simpleProfileChar6, value, SIMPLEPROFILE_CHAR6_LEN ); 
      }  
      else  
      {  
        ret = bleInvalidRange;  
      }  
      break;


2)增加char6的数值可获取的处理simpleGATTprofile.c的SimpleProfile_GetParameter函数中

    case SIMPLEPROFILE_CHAR6:  
      VOID osal_memcpy( value, simpleProfileChar6, SIMPLEPROFILE_CHAR6_LEN );  
      break; 

6、修改特征值的 读写 函数

1)增加char6的数值读取的处理simpleGATTprofile.c的simpleProfile_ReadAttrCB函数中

      case SIMPLEPROFILE_CHAR6_UUID:  
        *pLen = SIMPLEPROFILE_CHAR6_LEN;  
        VOID osal_memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR6_LEN );  
        break;  

2) 增加char6的数值写入的处理 simpleGATTprofile.c的simpleProfile_WriteAttrCB函数中

      case SIMPLEPROFILE_CHAR6_UUID:  
        
      //Validate the value  
      // Make sure it's not a blob oper  
      if ( offset == 0 )  
      {  
        if ( len != SIMPLEPROFILE_CHAR6_LEN )  
        {  
          status = ATT_ERR_INVALID_VALUE_SIZE;  
        }  
      }  
      else  
      {  
        status = ATT_ERR_ATTR_NOT_LONG;  
      }  
        
      //Write the value  
      if ( status == SUCCESS )  
      {  
        VOID osal_memcpy( pAttr->pValue, pValue, SIMPLEPROFILE_CHAR6_LEN );  
        notifyApp = SIMPLEPROFILE_CHAR6;  
      }  
             
      break;  

3)增加通知开关的处理(替换simpleGATTprofile.c的simpleProfile_WriteAttrCB函数中的GATT_CLIENT_CHAR_CFG_UUID部分)

      //通知开关管理
      case GATT_CLIENT_CHAR_CFG_UUID:  
        //CHAR4的通知开关
        if(pAttr->handle == simpleProfileAttrTbl[GUA_ATTRTBL_CHAR4_CCC_IDX].handle) 
        {   
          status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,  
                                                   offset, GATT_CLIENT_CFG_NOTIFY );  
        }  
        //CHAR6的通知开关        
        else if(pAttr->handle == simpleProfileAttrTbl[GUA_ATTRTBL_CHAR6_CCC_IDX].handle)   
        {   
          status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,  
                                                   offset, GATT_CLIENT_CFG_NOTIFY );  
        }    
        //其他情况不打开通知开关
        else  
        {  
          status = ATT_ERR_INVALID_HANDLE;  
        }          
        
        break;  
此处非常重要,如果没添加会导致通知开关打不开,以至于从机无法主动发送数据到主机。


4)添加两个通知开关的宏simpleGATTprofile.c中

#define GUA_ATTRTBL_CHAR4_VALUE_IDX             11     
#define GUA_ATTRTBL_CHAR4_CCC_IDX               12     
#define GUA_ATTRTBL_CHAR6_VALUE_IDX             18
#define GUA_ATTRTBL_CHAR6_CCC_IDX               19
char4与char6为通知属性,因此需要明确该特征值开关的位置。

char4的特征值数据在属性表simpleProfileAttrTbl中的位置为第11位(属性表是数组,从0位开始),因此GUA_ATTRTBL_CHAR4_VALUE_IDX宏定义为11。

char4的特征值开关在属性表simpleProfileAttrTbl中的位置为第12位(属性表是数组,从0位开始),因此GUA_ATTRTBL_CHAR4_CCC_IDX宏定义为12。

char6的特征值数据在属性表simpleProfileAttrTbl中的位置为第18位(属性表是数组,从0位开始),因此GUA_ATTRTBL_CHAR6_VALUE_IDX宏定义为18。

char6的特征值开关在属性表simpleProfileAttrTbl中的位置为第19位(属性表是数组,从0位开始),因此GUA_ATTRTBL_CHAR6_CCC_IDX宏定义为19。


7、增加char6的通知开关初始化(替换simpleGATTprofile.c的SimpleProfile_AddService函数

//******************************************************************************                
//name:             SimpleProfile_AddService               
//introduce:        通过注册GATT属性与GATT服务,从而初始化simple服务           
//parameter:        services: 服务号      
//return:           none             
//author:           甜甜的大香瓜                     
//email:            897503845@qq.com         
//QQ group          香瓜BLE之CC2541(127442605)                      
//changetime:       2016.12.08                     
//******************************************************************************  
bStatus_t SimpleProfile_AddService( uint32 services )
{
  uint8 status = SUCCESS;

  // Initialize Client Characteristic Configuration attributes
  GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar4Config );
  GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar6Config );      //GUA
  
  // Register with Link DB to receive link status change callback
  VOID linkDB_Register( simpleProfile_HandleConnStatusCB );  
  
  if ( services & SIMPLEPROFILE_SERVICE )
  {
    // Register GATT attribute list and CBs with GATT Server App
    status = GATTServApp_RegisterService( simpleProfileAttrTbl, 
                                          GATT_NUM_ATTRS( simpleProfileAttrTbl ),
                                          &simpleProfileCBs );
  }

  return ( status );
}
只有通知属性的才需要初始化通知开关属性。


8、增加char6的通知开关初始化的实时更新(替换simpleGATTprofile.c的simpleProfile_HandleConnStatusCB函数

//******************************************************************************                
//name:             simpleProfile_HandleConnStatusCB               
//introduce:        simple服务连接状态的改变函数           
//parameter:        connHandle: 连接句柄 
//                  changeType: 改变类型   
//return:           none             
//author:           甜甜的大香瓜                     
//email:            897503845@qq.com         
//QQ group          香瓜BLE之CC2541(127442605)                      
//changetime:       2016.12.08                     
//******************************************************************************  
static void simpleProfile_HandleConnStatusCB( uint16 connHandle, uint8 changeType )
{ 
  // Make sure this is not loopback connection
  if ( connHandle != LOOPBACK_CONNHANDLE )
  {
    // Reset Client Char Config if connection has dropped
    if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||
         ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) && 
           ( !linkDB_Up( connHandle ) ) ) )
    { 
      GATTServApp_InitCharCfg( connHandle, simpleProfileChar4Config );
      GATTServApp_InitCharCfg( connHandle, simpleProfileChar6Config );  //GUA      
    }
  }
}
会根据设备连接状态来改变特征值通知开关的状态。


9、增加char6的发送通知数据的函数

1)定义char6的发送通知数据的函数(simpleGATTprofile.c中)

//******************************************************************************                
//name:             GUA_SimpleGATTprofile_Char6_Notify               
//introduce:        发送char6通道的数据           
//parameter:        nGUA_ConnHandle: 连接句柄 
//                  npGUA_Value: 要通知的数据,范围为0~SIMPLEPROFILE_CHAR6,最多20个字节 
//                  nGUA_Len: 要通知的数据的长度  
//return:           none             
//author:           甜甜的大香瓜                     
//email:            897503845@qq.com         
//QQ group          香瓜BLE之CC2541(127442605)                      
//changetime:       2016.12.29                     
//******************************************************************************   
void GUA_SimpleGATTprofile_Char6_Notify(uint16 nGUA_ConnHandle, uint8 *pGUA_Value, uint8 nGUA_Len)  
{  
  attHandleValueNoti_t  stGUA_Noti;  
  uint16 nGUA_Return;  
  
  //读出CCC的值 
  nGUA_Return = GATTServApp_ReadCharCfg(nGUA_ConnHandle, simpleProfileChar6Config); 
  
  //判断是否打开通知开关,打开了则发送数据  
  if (nGUA_Return & GATT_CLIENT_CFG_NOTIFY) 
  {  
    //填充数据
    stGUA_Noti.handle = simpleProfileAttrTbl[GUA_ATTRTBL_CHAR6_VALUE_IDX].handle;  
    stGUA_Noti.len = nGUA_Len;  
    osal_memcpy(stGUA_Noti.value, pGUA_Value, nGUA_Len);
    
    //发送数据
    GATT_Notification(nGUA_ConnHandle, &stGUA_Noti, FALSE);  
  }  
} 
注意,本函数仅适用于协议栈1.3.2和1.4.0版本。

1.4.2版本的attHandleValueNoti_t 结构体发生变化,需要多一条分配发送数据缓冲区的代码。可以参考《CC2640之自定义服务》的notify代码(不一定完全一样):

//分配发送数据缓冲区  
stGUA_Noti.pValue = GATT_bm_alloc(nGUA_ConnHandle, ATT_HANDLE_VALUE_NOTI, GUAPROFILE_CHAR6_LEN, NULL);


2)声明 char6的发送通知数据的函数 (simpleGATTprofile.h中)

extern void GUA_SimpleGATTprofile_Char6_Notify(uint16 nGUA_ConnHandle, uint8 *pGUA_Value, uint8 nGUA_Len);

10、应用层修改

1)修改特征值初始化的数值simpleBLEPeripheral.c的SimpleBLEPeripheral_Init函数中

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

2)修改应用层的回调函数simpleBLEPeripheral.c的simpleProfileChangeCB函数中

//******************************************************************************                
//name:             simpleProfileChangeCB               
//introduce:        simple服务的回调函数
//parameter:        paramID: 特征值ID 
//return:           none             
//author:           甜甜的大香瓜                     
//email:            897503845@qq.com         
//QQ group          香瓜BLE之CC2541(127442605)                      
//changetime:       2016.12.08                     
//******************************************************************************  
static void simpleProfileChangeCB( uint8 paramID )
{
  uint16 nGUA_ConnHandle;
  uint8 nbGUA_Char6[20] = {0};  
  
  switch( paramID )
  {
    //char1
    case SIMPLEPROFILE_CHAR1:
    {
      break;      
    }

    //char3
    case SIMPLEPROFILE_CHAR3:
    {
      break;      
    }

    //char6
    case SIMPLEPROFILE_CHAR6:  
    {
      //获取连接句柄
      GAPRole_GetParameter(GAPROLE_CONNHANDLE, &nGUA_ConnHandle);
      
      //读取char6的数值
      SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR6, &nbGUA_Char6);

      //发送数据  
      GUA_SimpleGATTprofile_Char6_Notify(nGUA_ConnHandle, nbGUA_Char6, 20);
      
      break;      
    }        
      
    default:
    {
      break;
    }  
  }
}


七、注意事项

手机可能缓存了之前的代码(在更新过CC2541的代码之后,都需要清除手机端的缓存!!!),因此要清除缓存,清除缓存的方法如下:

方法一:关闭app、关闭蓝牙总开关、打开蓝牙总开关、打开app。
方法二:手机重启。


八、实验结果

1、仿真并全速运行工程。

2、用安卓手机的TruthBlue2_1扫描并连接设备,可发现新增的特征值char6



3、数据通信过程如下图


①红框为app主动读取到的数值,为默认的1~20(hex显示)。

②蓝框为app主动写入cc2541的数值。

③紫框为cc2541接收到app的数值后,再将char6的数值通过通知发送出来,可见当前char6的数值已被app改变。


因此,实验成功。




  • 16
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值