【BLE】CC2541之添加特征值
转载:http://blog.csdn.net/feilusia/article/details/48235691
版权声明:喝水不忘挖井人,转载请注明出处,897503845@qq.com。
本篇博文最后修改时间: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、特征值是什么?
答:特征值是一个变量或者一个数组,它被定义在从机端,它是主从机之间传输数据的缓冲区。
比如添加一个char6[20],它的值初始化为1、2、3、4、5、6、7、8、9、10、11、12、13、14、15、16、17、18、19、20。
当char6具有读、写属性时,主机可以通过GATT_ReadCharValue、GATT_WriteCharValue进行读、写从机的char6。
当char6具有notify通知属性时,从机可以将char6的值通知给主机。(通知的两种方式可参见本博客的《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改变。
因此,实验成功。
还没有评论,来说两句吧...