【BLE】CC2541之添加特征值
来源:互联网 发布:华为4 a数据分区破坏 编辑:程序博客网 时间:2024/05/21 11:20
本篇博文最后修改时间:2017年03月06日,23:51。
一、简介
本文以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
五、基础知识
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: 0xFFF6CONST 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 Propertiesstatic uint8 simpleProfileChar6Props = GATT_PROP_READ | GATT_PROP_WRITE | GATT_PROP_NOTIFY;// Characteristic 6 Valuestatic 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 Descriptionstatic uint8 simpleProfileChar6UserDesp[17] = "Characteristic 6\0";
由于属性包含GATT_PROP_NOTIFY方式,所以必须要有个通知开关simpleProfileChar6Config。
4、修改属性表
1)修改属性表的大小(simpleGATTprofile.c的CONSTANTS段中)
#define SERVAPP_NUM_ATTR_SUPPORTED 21增加上面定义的char6的4个属性变量。
// 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的simpleProfile_WriteAttrCB函数中)
#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 19char4与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);
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改变。
因此,实验成功。
- 【BLE】CC2541之添加特征值
- 【BLE】CC2541之添加特征值
- CC2541之添加特征值
- 【BLE】CC2541之发现服务与特征值
- 【BLE】CC2541之主机端读取特征值
- 【BLE】CC2541之发现多个特征值句柄
- 【BLE】CC2541之添加自定义服务
- 【BLE】CC2541之添加自定义任务
- <蓝牙BLE>cc2541发现服务与特征值
- 【BLE】CC2541之notify
- 【BLE】CC2541之按键
- 【BLE】CC2541之RSSI
- 【BLE】CC2541之OLED
- 【BLE】CC2541之indicate
- 【BLE】CC2541之timer3
- 【BLE】CC2541之看门狗
- 【BLE】CC2541之SNV
- 【BLE】CC2541之OAD
- Codeforces Round #274 (Div. 2)
- clfeak与matlab的匹配
- Android json的一些学习
- grep 使用的一个问题
- android json解析及简单例子
- 【BLE】CC2541之添加特征值
- hadoop 自学指南三之WordCount解析(2)
- codeforces-549F-Yura and Developers
- HDU 5428
- shearphoto2.0头像截取插件
- 嵌入式博大精深,从零开始,活到老学到老
- 从零开始学习嵌入式 - 什么是IP core?
- OpenGL-----Spatial Convolution
- Processing 练习(4) - 鼠标控制颜色