首页 > 其他分享 >【CC2640R2F】香瓜CC2640R2F之自定义服务

【CC2640R2F】香瓜CC2640R2F之自定义服务

时间:2025-01-16 09:32:22浏览次数:3  
标签:Profile 特征值 GUA UUID 自定义 CC2640R2F GUAProfile 香瓜 句柄

本文最后修改时间: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)完整开源资料下载地址(电脑端打开):

opengua.taobao.com

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

相关文章

  • Windows自定义变量打开文件或文件夹
    前言全局说明Windows系统自带很多变量,方便使用。参照系统设置,我们可以自定义一些变量,快速打开文件或文件夹。例如:在运行或文件夹地址栏输入%TEMP%就能打开对应文件夹一、说明1.1环境:Windows11家庭版23H222631.37371.2环境变量位置用户变量:自定义的文件(夹)......
  • Tauri教程-进阶篇-第一节 自定义启动画面
    “如果结果不如你所愿,就在尘埃落定前奋力一搏。”——《夏目友人帐》“有些事不是看到了希望才去坚持,而是因为坚持才会看到希望。”——《十宗罪》“维持现状意味着空耗你的努力和生命。”——纪伯伦Tauri技术教程*第五章Tauri的进阶教程第一节自定义启动画面......
  • 如何自定义设置伪静态规则?
    关于您提到的如何自定义设置伪静态规则的问题,我们将为您提供详细的解决方案。伪静态(RewriteRules)是一种将动态URL转换为静态URL的技术,可以提高网站的SEO友好性并改善用户体验。不同类型的Web服务器有不同的配置方法,以下是针对几种常见服务器的详细说明。ApacheWeb服务器编辑......
  • Oracle自定义函数:生成汉字首字母拼音码的函数、MD5
    1 生成汉字拼音码的函数使用方法:select用户名.函数名(需要获取首字母拼音码的字段名)from用户名.表名;selectoracle_user1.fgetpy(t.name)fromoracle_user1.studentt;函数定义:createorreplacefunctionfgetpy(v_strvarchar2)returnvarchar2asv_strleni......
  • Origin 自定义公式拟合
    非线性拟合选中数据-绘图-分析-拟合-非线性曲线拟合-打开对话框-新建函数-函数命名-输入函数表达式,如y=a*x^2,即可。若公式中涉及到复数,则使用ImReal()取实部,Imaginary()取虚部,Imsqrt()取开方。如色散方程取实部,即色散部分进行拟合,则相应的Origin拟合......
  • 【树莓派5】香瓜树莓派5之Home Assistant(ZHA+ Zigbee2MQTT)控制zigbee开关(有线+无线)
    本文最后修改时间:2024年07月04日一、本节简介本节以树莓派5为例,安装HomeAssistant系统并且进行配置,通过两种协议方式(ZHA+Zigbee2MQTT)控制zigbee网关,连接zigbee开关(有线+无线)来控制灯的亮灭。二、实验平台1、硬件平台1)树莓派5开发板套件①树莓派5开发板②SD卡(32G)③mir......
  • 鸿蒙开发 - 自定义组件 和 组件通信的方法
    自定义组件的基本结构@Entry@ComponentstructMyComponent{build(){//...}}build()函数build()函数用于描述组件的UI界面,自定义组件必须定义build()函数build(){Column(){Text('测试')Button('点击')}}struct关键字strcut用来......
  • 界面控件 DevExpress v24.2 新版亮点 - 自定义和扩展 AI 驱动的扩展
    DevExpress拥有.NET开发需要的所有平台控件,包含600多个UI控件、报表平台、DevExpressDashboardeXpressApp框架、适用于VisualStudio的CodeRush等一系列辅助工具。屡获大奖的软件开发平台DevExpress今年第一个重要版本v23.1正式发布,该版本拥有众多新产品和数十个具有高影响力......
  • Winform自定义控件与案例 - 打造炫酷的自定义开关按钮控件
    文章目录1、控件效果2、开关控件需求说明3、案例实现1、属性扩展代码实现2、控件外观绘制3、事件实现4、拓展方法4、案例演示5、总结1、控件效果2、开关控件需求说明在开发Winform应用程序时,拥有一个美观且功能强大的UI控件库是非常重要的。本文将详细介绍如......
  • Django Admin 自定义操作封装
    1.为什么需要封装?在Django开发中,我们经常需要在Admin界面添加自定义操作按钮,以便管理员执行特定的任务。通过封装,我们可以:减少重复代码统一管理自定义操作的逻辑提高代码的可维护性和可扩展性©ivwdcwso(ID:u012172506)2.CustomActionMixin的实现让我们看看C......