本文最后修改时间:2023年09月06日 12:19
一、本节简介
本节以simple_peripheral工程为例,介绍如何添加自定义服务。
二、实验平台
1)CC2640R2F平台
①协议栈版本:CC2640R2 SDK v1.40.00.45
②编译软件:CCS7.3.0.00019
③硬件平台:香瓜CC2640R2F开发板
④仿真器:香瓜XDS100V3下载器
2)手机平台
①手机型号:红米1S
②安卓版本:安卓4.3
③安卓app:BLE Reader
三、版权声明
1)作者:甜甜的大香瓜
2)声明:喝水不忘挖井人,转载请注明出处。
3)纠错/业务合作:897503845@qq.com
4)香瓜BLE之CC2640R2F群:557278427
5)本文出处:原创连载资料《简单粗暴学蓝牙5》
6)完整开源资料下载地址(电脑端打开):
7)香瓜CC2640R2F开发板购买链接:
https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4023-16963296339.8.21bfc58419sWKt&id=558653143169
四、实验前提
1、在进行本文步骤前,请先阅读以下章节:
1)《简单粗暴学蓝牙5》的“第一章至第四章”章节。
2、在进行本文步骤前,请先实现以下章节:
1)《简单粗暴学蓝牙5》的“第三章 软件的安装及使用”章节。
五、基础知识
1、服务、特征值、UUID、句柄是什么?
答:
1)服务(server)
一个工程可以有多个服务,比如按键服务、心率计服务、温度计服务。
2)特征值(characteristic )
一个服务可以有多个特征值,特征值是主从机传输数据的媒介,像运人渡河的小船。
3)UUID(通用唯一识别码/Universally Unique Identifier)
UUID 类似于服务、特征值的“身份证号码”,具有唯一性。
完整的UUID有128bit(16字节)的大小,而CC2640R2F的协议栈默认使用的UUID是简写版16bit(2字节)的UUID,如下文添加的香瓜服务的UUID为“0xFFFE”,实际上它最终会被填充为128bit(16字节)的UUID进行数据通信。
4)句柄(handle)
句柄类似于“学生的学号”,它是每次设备上电后蓝牙协议栈分配的一个号码,注意不要把它当做定值。最常见的是连接句柄(连接后分配的句柄,例如第一台连接上的设备连接句柄可能为0x0000、第二台连接上的设备连接句柄可能为0x0001)、特征值句柄(设备上电后每个特征值都有个句柄,例如某服务的特征值1句柄可能为0x0025、特征值4句柄可能为0x002e)。
举个例子,假设香瓜的蓝牙设备中有2个服务,温湿度服务(假设UUID为0x1110)和电量服务(假设UUID为0x2220)。
其中温湿度服务中包含了温度特征值(假设UUID为0x1111)、湿度特征值(假设UUID为0x1112)。
此时你想用另一个CC2640R2F作为主机读取温度值,那么CC2640R2F主机会做如下事情:
1)连接设备:扫描并连接香瓜的蓝牙设备。
2)发现服务:查找设备的服务中是否包含UUID为0x1110的服务(温湿度服务)。
3)发现特征值:查找UUID为0x1110的服务(温湿度服务)中是否包含UUID为0x1111的特征值(温度特征值)。
4)获得特征值句柄:查找到温度特征值后,获取问读特征值句柄,假设为0x0038。
5)利用句柄来读取温度值:使用0x0038的句柄发送读指令,则此时CC2640R2F主机可读取到CC2640R2F从机中的温度值。
2、simple_peripheral工程有哪些服务?
答:
在PROFILES文件夹下的文件,均属于服务。例如设备信息服务(包含设备基本信息)、从机服务(作为从机所需的服务,主机工程则有主机服务)等。
其中工程师要关心的是“simple_gatt_profile”服务,这个服务是用于蓝牙的数据通信的服务。
在此服务中,包含了5个不同类型的特征值。工程师可在此基础上增、删、减、改特征值。
3、为什么要添加自定义服务?
答:
1)新服务可以完全独立于项目中,不影响工程中已有服务。
2)添加新服务后的灵活性很高,你可以把它作为气压采集服务,也可以把它作为温度采集服务。
3)香瓜在此自定义服务中添加了一个可读、可写、可通知的特征值通道,直接给工程师一个最实用的特征值通道,简单易添加。
4)原有的“simple_gatt_profile”服务中的5个特征值往往在实际项目中难以全都用上,造成了资源占用的浪费。有了香瓜自定义服务后,可删除原有的无用服务,而不用在原服务中一个一个地删除无用特征值。
5)方便其他开发人员阅读你的代码,开发人员只需要关注你新写的服务,而不用到各个服务去查找你改动过的地方。
4、特征值的读、写、notify属性有什么区别?
答:
读、写、notify属性是特征值中最常用的属性。
特征值是一个变量或者一个数组,它被定义在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机。
5、简介notify通知的两种方式
答:
1)GATT_Notification
在从机代码中使用,由从机主动通知,且不需要主机发出请求和回应。
2)GATTServApp_ProcessCharCfg
在从机代码中使用,需要主机发送一次“通知请求”给从机,从机收到“通知请求”才发送通知。
注:实际上这个函数里依然会调用GATT_Notification这个函数。
6、什么是CCC?
答:
Client Characteristic Configuration,俗称CCC。
Notify属性的特征值,会比读、写属性的特征值多一个CCC。
从机要想使用notify函数时能正常发送出数据,就必须保证CCC是被打开的。
7、CCC如何打开?
答:
notify开关可由主机端或者从机端打开,但应尽量保证由主机来打开比较合适,毕竟它是“主机”,“主机“就该有主动权。
1)主机端打开(推荐)
先获取到CCC的特征值句柄,然后利用CCC的特征值句柄往CCC的特征值中写入0x0001。
2)从机端打开(不推荐)
GATTServApp_WriteCharCfg(connHandle, simpleProfileChar4Config, 0x0001); |
8、如何获取CCC的句柄?
答:
先获取到这个CCC所属的特征值的特征值句柄,然后将该特征值句柄+1。
例如,想要获取到char6的CCC的句柄,就必须先获取到char6的特征值句柄,假设获取到的值是0x0035,则CCC的特征值句柄就是0x0036。之所以加1,是因为char6的CCC所在属性表的位置,正好在char6的特征值后面。
9、是否可以直接在主机代码中使用0x0036当做char6的CCC句柄?
答:
可以,但是不推荐。
由于句柄是协议栈自动分配的,代码编译好之后,特征值句柄是固定的。但是一旦你在char6之前添加了一个特征值,那么char6的CCC句柄也会往后推算,这时候你的0x0036显然就没用了。
因此强烈推荐下文中使用的方法,自动获取句柄,详情自己看代码。
10、属性表是什么?
答:
它是一个服务中的数组,用来存放该服务下的服务信息、所有特征值信息。每个特征值一般为3个参数,而notify属性的特征值一般为4个参数(多一个notify开关的参数)。
六、硬件原理
暂无
七、实验步骤
1、编写并添加自定义的服务
1)写一个自定义的服务源文件GUA_Profile.c(存放在“……
\simplelink_cc2640r2_sdk_1_40_00_45\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\app\GUA“路径下)
//********************************************************************** //name: GUA_profile.c //introduce: 香瓜自定义的服务,内含一个可读、可写、可通知的特征值 //author: 甜甜的大香瓜 //email: 897503845@qq.com //QQ group: 香瓜BLE之CC2640R2F(557278427) //shop: //https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i //changetime: 2017.11.06 //********************************************************************** #include <string.h> #include “bcomdef.h” #include “OSAL.h” #include “linkdb.h” #include “att.h” #include “gatt.h” #include “gatt_uuid.h” #include “gattservapp.h” #include “gapbondmgr.h” #include “GUA_Profile.h” /********************************************************************* * MACROS */ /********************************************************************* * CONSTANTS */ //属性表的数据长度 #define SERVAPP_NUM_ATTR_SUPPORTED 5 //属性在属性表中的偏移值 #define ATTRTBL_GUA_CHAR1_IDX 2 //char1在属性表的偏移值 #define ATTRTBL_GUA_CHAR1_CCC_IDX 3 //char1的notify开关在属性表的偏移值 /********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ // GUA Service UUID: 0xFFE0 CONST uint8 GUAServUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(GUAPROFILE_SERV_UUID), HI_UINT16(GUAPROFILE_SERV_UUID) }; // GUA char1 UUID: 0xFFE1 CONST uint8 GUAChar1UUID[ATT_BT_UUID_SIZE] = { LO_UINT16(GUAPROFILE_CHAR1_UUID), HI_UINT16(GUAPROFILE_CHAR1_UUID) }; /********************************************************************* * EXTERNAL VARIABLES */ /********************************************************************* * EXTERNAL FUNCTIONS */ /********************************************************************* * LOCAL VARIABLES */ static GUAProfileCBs_t *GUAProfile_AppCBs = NULL; /********************************************************************* * Profile Attributes – variables */ // GUA Service attribute static CONST gattAttrType_t GUAProfile_Service = { ATT_BT_UUID_SIZE, GUAServUUID }; // GUA Characteristic 1 Properties static uint8 GUAProfile_Char1_Props = GATT_PROP_READ | GATT_PROP_WRITE | GATT_PROP_NOTIFY; // GUA Characteristic 1 Value static uint8 GUAProfile_Char1[GUAPROFILE_CHAR1_LEN] = {0}; // GUA Characteristic 1 Configs static gattCharCfg_t *GUAProfile_Char1_Config; // GUA Characteristic 1 User Description static uint8 GUAProfile_Char1_UserDesp[10] = “GUA Char1\0”; /********************************************************************* * Profile Attributes – Table */ static gattAttribute_t GUAProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] = { // GUA Service { { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */ GATT_PERMIT_READ, /* permissions */ 0, /* handle */ (uint8 *)&GUAProfile_Service /* pValue */ }, // GUA Characteristic 1 Declaration { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &GUAProfile_Char1_Props }, // GUA Characteristic 1 Value { { ATT_BT_UUID_SIZE, GUAChar1UUID }, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, GUAProfile_Char1 }, // GUA Characteristic 1 configuration { { ATT_BT_UUID_SIZE, clientCharCfgUUID }, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, (uint8 *)&GUAProfile_Char1_Config }, // GUA Characteristic 1 User Description { { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ, 0, GUAProfile_Char1_UserDesp }, }; /********************************************************************* * LOCAL FUNCTIONS */ static uint8 GUAProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint16 *pLen, uint16 offset, uint16 maxLen, uint8 method ); static bStatus_t GUAProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint16 len, uint16 offset, uint8 method ); /********************************************************************* * PROFILE CALLBACKS */ // GUAProfile Service Callbacks CONST gattServiceCBs_t GUAProfileCBs = { GUAProfile_ReadAttrCB, // Read callback function pointer GUAProfile_WriteAttrCB, // Write callback function pointer NULL // Authorization callback function pointer }; /********************************************************************* * PUBLIC FUNCTIONS */ /********************************************************************* * @fn GUAProfile_AddService * * @brief Initializes the GUA service by registering * GATT attributes with the GATT server. * * @param services – services to add. This is a bit map and can * contain more than one service. * * @return Success or Failure */ bStatus_t GUAProfile_AddService( uint32 services ) { uint8 status = SUCCESS; // Allocate Client Characteristic Configuration table GUAProfile_Char1_Config = (gattCharCfg_t *)Icall_malloc( sizeof(gattCharCfg_t) * linkDBNumConns ); if ( GUAProfile_Char1_Config == NULL ) { return ( bleMemAllocError ); } // Initialize Client Characteristic Configuration attributes GATTServApp_InitCharCfg( INVALID_CONNHANDLE, GUAProfile_Char1_Config ); if ( services & GUAPROFILE_SERVICE ) { // Register GATT attribute list and CBs with GATT Server App status = GATTServApp_RegisterService( GUAProfileAttrTbl, GATT_NUM_ATTRS( GUAProfileAttrTbl ), GATT_MAX_ENCRYPT_KEY_SIZE, &GUAProfileCBs ); } return ( status ); } /********************************************************************* * @fn GUAProfile_RegisterAppCBs * * @brief Registers the application callback function. Only call * this function once. * * @param callbacks – pointer to application callbacks. * * @return SUCCESS or bleAlreadyInRequestedMode */ bStatus_t GUAProfile_RegisterAppCBs( GUAProfileCBs_t *appCallbacks ) { if ( appCallbacks ) { GUAProfile_AppCBs = appCallbacks; return ( SUCCESS ); } else { return ( bleAlreadyInRequestedMode ); } } /********************************************************************* * @fn GUAProfile_SetParameter * * @brief Set a GUA Profile parameter. * * @param param – Profile parameter ID * @param len – length of data to right * @param pValue – pointer to data to write. This is dependent on * the parameter ID and WILL be cast to the appropriate * data type (example: data type of uint16 will be cast to * uint16 pointer). * * @return bStatus_t */ bStatus_t GUAProfile_SetParameter( uint8 param, uint8 len, void *pValue ) { bStatus_t ret = SUCCESS; switch ( param ) { case GUAPROFILE_CHAR1: if ( len == GUAPROFILE_CHAR1_LEN ) { VOID memcpy( GUAProfile_Char1, pValue, GUAPROFILE_CHAR1_LEN ); } else { ret = bleInvalidRange; } break; default: ret = INVALIDPARAMETER; break; } return ( ret ); } /********************************************************************* * @fn GUAProfile_GetParameter * * @brief Get a GUA Profile parameter. * * @param param – Profile parameter ID * @param pValue – pointer to data to put. This is dependent on * the parameter ID and WILL be cast to the appropriate * data type (example: data type of uint16 will be cast to * uint16 pointer). * * @return bStatus_t */ bStatus_t GUAProfile_GetParameter( uint8 param, void *pValue ) { bStatus_t ret = SUCCESS; switch ( param ) { case GUAPROFILE_CHAR1: VOID memcpy( pValue, GUAProfile_Char1, GUAPROFILE_CHAR1_LEN ); break; default: ret = INVALIDPARAMETER; break; } return ( ret ); } /********************************************************************* * @fn GUAProfile_ReadAttrCB * * @brief Read an attribute. * * @param connHandle – connection message was received on * @param pAttr – pointer to attribute * @param pValue – pointer to data to be read * @param pLen – length of data to be read * @param offset – offset of the first octet to be read * @param maxLen – maximum length of data to be read * * @return Success or Failure */ static uint8 GUAProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint16 *pLen, uint16 offset, uint16 maxLen, uint8_t method ) { bStatus_t status = SUCCESS; // If attribute permissions require authorization to read, return error if ( gattPermitAuthorRead( pAttr->permissions ) ) { // Insufficient authorization return ( ATT_ERR_INSUFFICIENT_AUTHOR ); } // Make sure it’s not a blob operation (no attributes in the profile are long if ( offset > 0 ) { return ( ATT_ERR_ATTR_NOT_LONG ); } if ( pAttr->type.len == ATT_BT_UUID_SIZE ) { // 16-bit UUID uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]); switch ( uuid ) { // No need for “GATT_SERVICE_UUID” or “GATT_CLIENT_CHAR_CFG_UUID” cases; // gattserverapp handles this type for reads // GUA characteristic does not have read permissions, but because it // can be sent as a notification, it must be included here case GUAPROFILE_CHAR1_UUID: *pLen = GUAPROFILE_CHAR1_LEN; VOID memcpy( pValue, pAttr->pValue, GUAPROFILE_CHAR1_LEN ); break; default: // Should never get here! *pLen = 0; status = ATT_ERR_ATTR_NOT_FOUND; break; } } else { // 128-bit UUID *pLen = 0; status = ATT_ERR_INVALID_HANDLE; } return ( status ); } /********************************************************************* * @fn GUAProfile_WriteAttrCB * * @brief Validate attribute data prior to a write operation * * @param connHandle – connection message was received on * @param pAttr – pointer to attribute * @param pValue – pointer to data to be written * @param len – length of data * @param offset – offset of the first octet to be written * * @return Success or Failure */ static bStatus_t GUAProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint16 len, uint16 offset, uint8_t method ) { bStatus_t status = SUCCESS; uint8 notifyApp = 0xFF; // If attribute permissions require authorization to write, return error if ( gattPermitAuthorWrite( pAttr->permissions ) ) { // Insufficient authorization return ( ATT_ERR_INSUFFICIENT_AUTHOR ); } if ( pAttr->type.len == ATT_BT_UUID_SIZE ) { // 16-bit UUID uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]); switch ( uuid ) { case GUAPROFILE_CHAR1_UUID: if ( offset == 0 ) { if ( len != GUAPROFILE_CHAR1_LEN ) { status = ATT_ERR_INVALID_VALUE_SIZE; } } else { status = ATT_ERR_ATTR_NOT_LONG; } //将接收到的数据写进特征值中,并且置标志位 if ( status == SUCCESS ) { VOID memcpy( pAttr->pValue, pValue, GUAPROFILE_CHAR1_LEN ); notifyApp = GUAPROFILE_CHAR1; } break; case GATT_CLIENT_CHAR_CFG_UUID: //char1通道,则打开notify开关 if ( pAttr->handle == GUAProfileAttrTbl[ATTRTBL_GUA_CHAR1_CCC_IDX].handle )//GUA CHAR1 NOTIFY { status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len, offset, GATT_CLIENT_CFG_NOTIFY ); } else { status = ATT_ERR_INVALID_HANDLE; } break; default: status = ATT_ERR_ATTR_NOT_FOUND; break; } } else { // 128-bit UUID status = ATT_ERR_INVALID_HANDLE; } // If a itrateristic value changed then callback function to notify application of change if ( (notifyApp != 0xFF ) && GUAProfile_AppCBs && GUAProfile_AppCBs->pfnGUAProfileChange ) { GUAProfile_AppCBs->pfnGUAProfileChange( notifyApp ); } return ( status ); } //********************************************************************** //name: GUA_Profile_Notify //introduce: 香瓜的notify发送函数 //parameter: nGUA_Param:特征值通道参数 // nGUA_ConnHandle:连接句柄 // pGUA_Value:要通知的数据 // nGUA_Len:要通知的数据长度,范围为0~SIMPLEPROFILE_CHAR6,最多20个字节 //return: none //author: 甜甜的大香瓜 //email: 897503845@qq.com //QQ group: 香瓜BLE之CC2640R2F(557278427) //shop: //https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i //changetime: 2017.11.06 //********************************************************************** void GUA_Profile_Notify(GUA_U8 nGUA_Param, GUA_U16 nGUA_ConnHandle, GUA_U8 *pGUA_Value, GUA_U8 nGUA_Len) { attHandleValueNoti_t stGUA_Noti; GUA_U16 nGUA_Value; switch(nGUA_Param) { //特征值1 case GUAPROFILE_CHAR1: { //读出CCC nGUA_Value = GATTServApp_ReadCharCfg(nGUA_ConnHandle, GUAProfile_Char1_Config); //判断CCC是否被打开 if(nGUA_Value & GATT_CLIENT_CFG_NOTIFY) { //分配发送数据缓冲区 stGUA_Noti.pValue = GATT_bm_alloc(nGUA_ConnHandle, ATT_HANDLE_VALUE_NOTI, GUAPROFILE_CHAR1_LEN, NULL); //分配成功,则发送数据 if(stGUA_Noti.pValue != NULL) { //填充数据 stGUA_Noti.handle = GUAProfileAttrTbl[ATTRTBL_GUA_CHAR1_IDX].handle; stGUA_Noti.len = nGUA_Len; memcpy( stGUA_Noti.pValue, pGUA_Value, nGUA_Len); //发送数据 if (GATT_Notification(nGUA_ConnHandle, &stGUA_Noti, FALSE) != SUCCESS) { GATT_bm_free((gattMsg_t *)&stGUA_Noti, ATT_HANDLE_VALUE_NOTI); } } } break; } default: break; } } |
2)写一个自定义的服务头文件GUA_Profile.h(存放在“……
\simplelink_cc2640r2_sdk_1_40_00_45\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\app\GUA“路径下)
/********************************************************************** //name: GUA_profile.h //introduce: 香瓜自定义的服务的头文件,内含一个可读、可写、可通知的特征值 //author: 甜甜的大香瓜 //email: 897503845@qq.com //QQ group: 香瓜BLE之CC2640R2F(557278427) //shop: //https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i //changetime: 2017.11.06 //********************************************************************** #ifndef _GUA_PROFILE_H_ #define _GUA_PROFILE_H_ #ifdef __cplusplus extern “C” { #endif /********************************************************************* * INCLUDES */ /********************************************************************* * CONSTANTS */ // Profile Parameters #define GUAPROFILE_CHAR1 0 // RW uint8 – Profile GUA Characteristic 1 value // GUA Service UUID #define GUAPROFILE_SERV_UUID 0xFFE0 // GUA CHAR1 UUID #define GUAPROFILE_CHAR1_UUID 0xFFE1 // GUA Profile Services bit fields #define GUAPROFILE_SERVICE 0x00000001 // Length of GUA Characteristic 1 in bytes #define GUAPROFILE_CHAR1_LEN 20 /********************************************************************* * TYPEDEFS */ /********************************************************************* * MACROS */ /********************************************************************* * Profile Callbacks */ // Callback when a characteristic value has changed typedef void (*GUAProfileChange_t)( uint8 paramID ); typedef struct { GUAProfileChange_t pfnGUAProfileChange; // Called when characteristic value changes } GUAProfileCBs_t; /********************************************************************* * API FUNCTIONS */ /* * GUAProfile_AddService- Initializes the GUA service by registering * GATT attributes with the GATT server. * * @param services – services to add. This is a bit map and can * contain more than one service. */ extern bStatus_t GUAProfile_AddService( uint32 services ); /* * GUAProfile_RegisterAppCBs – Registers the application callback function. * Only call this function once. * * appCallbacks – pointer to application callbacks. */ extern bStatus_t GUAProfile_RegisterAppCBs( GUAProfileCBs_t *appCallbacks ); /* * GUAProfile_SetParameter – Set a Simple Key Profile parameter. * * param – Profile parameter ID * len – length of data to right * pValue – pointer to data to write. This is dependent on * the parameter ID and WILL be cast to the appropriate * data type (example: data type of uint16 will be cast to * uint16 pointer). */ extern bStatus_t GUAProfile_SetParameter( uint8 param, uint8 len, void *pValue ); /* * GUA_GetParameter – Get a Simple Key Profile parameter. * * param – Profile parameter ID * pValue – pointer to data to write. This is dependent on * the parameter ID and WILL be cast to the appropriate * data type (example: data type of uint16 will be cast to * uint16 pointer). */ extern bStatus_t GUAProfile_GetParameter( uint8 param, void *pValue ); /*********************宏定义************************/ //类型宏 #ifndef GUA_C typedef char GUA_C; #endif #ifndef GUA_U8 typedef unsigned char GUA_U8; #endif #ifndef GUA_8 typedef signed char GUA_8; #endif #ifndef GUA_U16 typedef unsigned short GUA_U16; #endif #ifndef GUA_16 typedef signed short GUA_16; #endif #ifndef GUA_U32 typedef unsigned long GUA_U32; #endif #ifndef GUA_32 typedef signed long GUA_32; #endif #ifndef GUA_U64 typedef unsigned long long GUA_U64; #endif #ifndef GUA_64 typedef signed long long GUA_64; #endif /*********************函数声明************************/ extern void GUA_Profile_Notify(GUA_U8 nGUA_Param, GUA_U16 nGUA_ConnHandle, GUA_U8 *pGUA_Value, GUA_U8 nGUA_Len); /********************************************************************* *********************************************************************/ #ifdef __cplusplus } #endif #endif /* GUA_PROFILE_H */ |
2、工程中添加驱动文件GUA_Profile.c和GUA_Profile.h
注:拖拽至CCS工程的PROFILES文件夹下
添加文件过程中,选项选择如下
3、应用层调用
1)添加头文件(simple_peripheral.c中)
//GUA #include “GUA_Profile.h” //GUA |
2)添加初始化代码(simple_peripheral.c的SimpleBLEPeripheral_init函数末尾中)
//GUA //增加服务 GUAProfile_AddService(GATT_ALL_SERVICES);
//初始化特征值 uint8 GUAProfile_Char1Value[GUAPROFILE_CHAR1_LEN] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; GUAProfile_SetParameter(GUAPROFILE_CHAR1, GUAPROFILE_CHAR1_LEN, GUAProfile_Char1Value);
//添加回调函数 VOID GUAProfile_RegisterAppCBs(&simpleBLEPeripheral_GUAProfileCBs); //GUA |
注意这里GUAProfile_Char1Value因为是数组,所以作为参数传地址进去使用时,前面不需要“&”。
3)定义服务的回调函数及处理函数(simple_peripheral.c中)
/GUA //********************************************************************** //name: GUA_Profile_ChangeCB //introduce: 香瓜服务的应用层回调函数 //parameter: nGUA_ParamID:特征值ID //return: none //author: 甜甜的大香瓜 //email: 897503845@qq.com //QQ group: 香瓜BLE之CC2640R2F(557278427) //shop: //https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i //changetime: 2017.11.06 //********************************************************************** static void GUA_Profile_ChangeCB(GUA_U8 nGUA_ParamID) { SimpleBLEPeripheral_enqueueMsg(SBP_GUA_CHAR_CHANGE_EVT, nGUA_ParamID); } //********************************************************************** //name: GUA_Profile_CharValueChangeEvt //introduce: 香瓜服务的应用层处理函数 //parameter: nGUA_ParamID:特征值ID //return: none //author: 甜甜的大香瓜 //email: 897503845@qq.com //QQ group: 香瓜BLE之CC2640R2F(557278427) //shop: //https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i //changetime: 2017.11.06 //********************************************************************** static void GUA_Profile_CharValueChangeEvt(GUA_U8 nGUA_ParamID) { GUA_U16 nGUA_ConnHandle; GUA_U8 naGUA_Buf[20] = {0}; GUA_U8 *pGUA_Value = naGUA_Buf; //判断是哪个特征值 switch(nGUA_ParamID) { //特征值1 case GUAPROFILE_CHAR1: { //获取连接句柄 GAPRole_GetParameter(GAPROLE_CONNHANDLE, &nGUA_ConnHandle); //写一个20字节的测试缓冲区的数据 for(GUA_U8 I = 0; I < 20; i++) { *(pGUA_Value + i) = I; } //发送数据 GUA_Profile_Notify(GUAPROFILE_CHAR1, nGUA_ConnHandle, pGUA_Value, 20); break; } //其他 default: break; } } //GUA |
里面添加了测试代码,一旦接收到数据,就把0~19发给主机。
4)声明服务的回调函数和处理函数(simple_peripheral.c中)
//GUA static void GUA_Profile_ChangeCB(GUA_U8 nGUA_ParamID); static void GUA_Profile_CharValueChangeEvt(GUA_U8 nGUA_ParamID); //GUA |
5)注册回调函数(simple_peripheral.c中)
//GUA //GUA Profile Callbacks static GUAProfileCBs_t simpleBLEPeripheral_GUAProfileCBs = { GUA_Profile_ChangeCB // Charactersitic value change callback }; //GUA |
6)添加香瓜服务处理事件(simple_peripheral.c中)
①添加香瓜服务处理事件的宏定义
//GUA #define SBP_GUA_CHAR_CHANGE_EVT 0x0010 //GUA |
②添加香瓜服务处理事件的处理部分(替换simple_peripheral.c中的SimpleBLEPeripheral_processAppMsg函数)
//GUA static void SimpleBLEPeripheral_processAppMsg(sbpEvt_t *pMsg) { switch (pMsg->hdr.event) { case SBP_STATE_CHANGE_EVT: SimpleBLEPeripheral_processStateChangeEvt((gaprole_States_t)pMsg-> hdr.state); break;
case SBP_CHAR_CHANGE_EVT: SimpleBLEPeripheral_processCharValueChangeEvt(pMsg->hdr.state); break;
#if !defined(Display_DISABLE_ALL) case SBP_KEY_CHANGE_EVT: SimpleBLEPeripheral_handleKeys(pMsg->hdr.state); break; #endif // !Display_DISABLE_ALL
case SBP_GUA_CHAR_CHANGE_EVT: GUA_Profile_CharValueChangeEvt(pMsg->hdr.state); break;
default: // Do nothing. Break; } } //GUA |
八、注意事项
1、手机可能缓存了之前固件的数据(在更新过固件之后,都需要清除手机端的缓存!!!),因此要清除缓存,清除缓存的方法如下:
方法一:关闭app、关闭蓝牙总开关、打开蓝牙总开关、打开app。
方法二:手机重启。
九、实验结果
可见上图实现了使用香瓜服务中的特征值进行的收发。
注:一定要发20字节,其中“0011”为2个字节而不是4个字节,如果想发不定长数据请看第一章。
因此,实验成功。
标签:Profile,特征值,GUA,UUID,自定义,CC2640R2F,GUAProfile,香瓜,句柄 From: https://blog.csdn.net/feilusia/article/details/145170883