一、背景
1.1 Profile(规范)
profile 可以理解为一种规范,一个标准的通信协议,它存在于蓝牙从机中(服务端);
蓝牙组织规定了一些标准的 profile,例如 HID OVER GATT,防丢器,心率计等;
每个 profile 中会包含多个 service,每个 service 代表从机的一种能力。
1.2 Service(服务)
service 可以理解为一个服务,在 BLE 从机中有多个服务,例如:电量信息服务、系统信息服务等;
每个 service 中又包含多个 characteristic 特征值;
每个具体的 characteristic 特征值才是 BLE 通信的主题,比如当前的电量是 80%,电量的 characteristic 特征值存在从机的 profile 里,这样主机就可以通过这个 characteristic 来读取 80% 这个数据。
1.3 Characteristic(特征)
characteristic 特征,BLE 主从机的通信均是通过 characteristic 来实现,可以理解为一个标签,通过这个标签可以获取或者写入想要的内容。
1.4 UUID(通用唯一识别码)
uuid 通用唯一识别码,我们刚才提到的 service 和 characteristic 都需要一个唯一的 uuid 来标识;
每个从机都会有一个 profile,不管是自定义的 simpleprofile,还是标准的防丢器 profile,他们都是由一些 service 组成,每个 service 又包含了多个 characteristic,主机和从机之间的通信,均是通过characteristic来实现。
1.5 CCC(Client Characteristic Configuration)
Notify 属性的特征值,会比读、写属性的特征值多一个 CCC。
从机要想使用 Notify 函数时能正常发送出数据,就必须保证 CCC 是被打开的。
1.6 AttrTbl(属性表)
它是一个服务中的数组,用来存放该服务下的服务信息、所有特征值信息。每个特征值一般
为 3 个参数,而 Notify 属性的特征值一般为 4 个参数(多一个 Notify 开关的参数)。
1.7 应用场景
举个例子,假设蓝牙设备中有 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 从机中的温度值。
二、移植文件
链接:https://pan.baidu.com/s/19_VC_ifUScEXSIGHERjsCQ 提取码:8t9u
将 alm_gatt_profile.c 和 alm_gatt_profile.h 两个文件拖拽至CCS工程的PROFILES文件夹下
添加文件过程中,选项选择如下
2.1 alm_gatt_profile.c
/*********************************************************************
* INCLUDES
*/
#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 "alm_gatt_profile.h"
/*********************************************************************
* DEFINITIONS
*/
//属性表的数据长度
#define SERVAPP_NUM_ATTR_SUPPORTED 5 // 每新增一个特征要加3(带Notify的特征要加4)
//属性在属性表中的偏移值
#define ATTRTBL_ALM_CHAR1_IDX 2 // 特征1在属性表的偏移值
#define ATTRTBL_ALM_CHAR1_CCC_IDX 3 // 特征1的notify开关在属性表的偏移值
/*********************************************************************
* GLOBAL VARIABLES
*/
/*--------------------- 特征相关 -------------------------*/
uint8 g_ALMProfile_Char1Value[ALMPROFILE_CHAR1_LEN] = {0}; // 特征1值
uint8 g_ALMProfile_Char2Value[ALMPROFILE_CHAR2_LEN] = {0}; // 特征2值
uint8 g_ALMProfile_Char3Value[ALMPROFILE_CHAR3_LEN] = {0}; // 特征3值
// ALM GATT Profile 服务 UUID: 0xFFE0
CONST uint8 g_ALMProfileServUUID[ATT_BT_UUID_SIZE] =
{
LO_UINT16(ALMPROFILE_SERV_UUID), HI_UINT16(ALMPROFILE_SERV_UUID)
};
// 特征1 UUID: 0xFFE1
CONST uint8 g_ALMProfileChar1UUID[ATT_BT_UUID_SIZE] =
{
LO_UINT16(ALMPROFILE_CHAR1_UUID), HI_UINT16(ALMPROFILE_CHAR1_UUID)
};
/*********************************************************************
* LOCAL VARIABLES
*/
static almProfileCBs_t *s_pALMProfile_AppCBs = NULL;
/*********************************************************************
* Profile Attributes - variables
*/
// ALM Profile 服务属性
static CONST gattAttrType_t s_ALMProfileService = { ATT_BT_UUID_SIZE, g_ALMProfileServUUID };
// 特征1的权限
static uint8 s_ALMProfileChar1Props = GATT_PROP_READ | GATT_PROP_WRITE | GATT_PROP_NOTIFY;
// 特征1的值
static uint8 s_ALMProfileChar1[ALMPROFILE_CHAR1_LEN] = {0};
// 特征1的配置
static gattCharCfg_t *s_pALMProfileChar1Config;
// 特征1的用户描述
static uint8 s_ALMProfileChar1UserDesp[10] = "ALM Char1\0";
/*********************************************************************
* Profile Attributes - Table
*/
static gattAttribute_t s_ALMProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] =
{
// ALM Profile 服务
{
{ ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
GATT_PERMIT_READ, /* permissions */
0, /* handle */
(uint8 *)&s_ALMProfileService /* pValue */
},
// 特征1的权限
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&s_ALMProfileChar1Props
},
// 特征1的值
{
{ ATT_BT_UUID_SIZE, g_ALMProfileChar1UUID },
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
0,
s_ALMProfileChar1
},
// 特征1的配置
{
{ ATT_BT_UUID_SIZE, clientCharCfgUUID },
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
0,
(uint8 *)&s_pALMProfileChar1Config
},
// 特征1的用户描述
{
{ ATT_BT_UUID_SIZE, charUserDescUUID },
GATT_PERMIT_READ,
0,
s_ALMProfileChar1UserDesp
},
};
/*********************************************************************
* LOCAL FUNCTIONS
*/
/**
@brief 读属性
@param connHandle 连接句柄
@param pAttr 指向属性
@param pValue 指向读出的数据
@param pLen 被读出的数据长度
@param offset 偏移量
@param maxLen 被读出的数据最大长度
@return SUCCESS - 成功;FAILURE - 失败
*/
static uint8 almProfile_ReadAttrCB(uint16 connHandle,
gattAttribute_t *pAttr,
uint8 *pValue, uint16 *pLen,
uint16 offset, uint16 maxLen,
uint8_t method)
{
bStatus_t status = SUCCESS;
// 如果属性没有读的权限,返回错误
if(gattPermitAuthorRead(pAttr->permissions))
{
// 权限不足
return (ATT_ERR_INSUFFICIENT_AUTHOR);
}
// 确保不是错误操作 (没有长属性)
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 those reads
// 特征1有读权限
case ALMPROFILE_CHAR1_UUID:
*pLen = ALMPROFILE_CHAR1_LEN;
VOID memcpy(pValue, pAttr->pValue, ALMPROFILE_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);
}
/**
@brief 写属性,在写入之前验证属性数据
@param connHandle 连接句柄
@param pAttr 指向属性
@param pValue 指向写入的数据
@param len 写入的数据长度
@param offset 偏移量
@return SUCCESS - 成功;FAILURE - 失败
*/
static bStatus_t almProfile_WriteAttrCB(uint16 connHandle,
gattAttribute_t *pAttr,
uint8 *pValue, uint16 len,
uint16 offset, uint8_t method)
{
bStatus_t status = SUCCESS;
uint8 notifyApp = 0xFF;
// 如果属性没有写的权限,返回错误
if(gattPermitAuthorWrite(pAttr->permissions))
{
// 权限不足
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 ALMPROFILE_CHAR1_UUID:
if(offset == 0)
{
if(len != ALMPROFILE_CHAR1_LEN)
{
status = ATT_ERR_INVALID_VALUE_SIZE;
}
}
else
{
status = ATT_ERR_ATTR_NOT_LONG;
}
// 将接收到的数据写进特征值中,并且置标志位
if(status == SUCCESS)
{
VOID memcpy(pAttr->pValue, pValue, ALMPROFILE_CHAR1_LEN);
notifyApp = ALMPROFILE_CHAR1;
}
break;
case GATT_CLIENT_CHAR_CFG_UUID:
// 特征1通道,则打开notify开关
if(pAttr->handle == s_ALMProfileAttrTbl[ATTRTBL_ALM_CHAR1_CCC_IDX].handle)
{
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((notifyApp != 0xFF) && s_pALMProfile_AppCBs
&& s_pALMProfile_AppCBs->pfnALMProfileChange)
{
s_pALMProfile_AppCBs->pfnALMProfileChange(notifyApp);
}
return (status);
}
/*********************************************************************
* PROFILE CALLBACKS
*/
// ALM Profile 服务回调
CONST gattServiceCBs_t almProfileCBs =
{
almProfile_ReadAttrCB, // Read callback function pointer
almProfile_WriteAttrCB, // Write callback function pointer
NULL // Authorization callback function pointer
};
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/**
@brief 通过向GATT服务端注册GATT属性来初始化ALM Profile服务
@param services 被添加的服务
@return SUCCESS - 成功;FAILURE - 失败
*/
bStatus_t ALMProfile_AddService(uint32 services)
{
uint8 status = SUCCESS;
// 为客户端特征配置表分配内存
s_pALMProfileChar1Config = (gattCharCfg_t *)ICall_malloc(sizeof(gattCharCfg_t) *
linkDBNumConns);
if(s_pALMProfileChar1Config == NULL)
{
return (bleMemAllocError);
}
// 初始化客户端特征配置属性
GATTServApp_InitCharCfg(INVALID_CONNHANDLE, s_pALMProfileChar1Config);
if(services & ALMPROFILE_SERVICE)
{
// 向GATT服务端应用注册GATT属性列表和回调
status = GATTServApp_RegisterService(s_ALMProfileAttrTbl,
GATT_NUM_ATTRS(s_ALMProfileAttrTbl),
GATT_MAX_ENCRYPT_KEY_SIZE,
&almProfileCBs);
}
return (status);
}
/**
@brief 注册应用程序回调函数。只调用这个函数一次
@param callbacks 指向应用程序的回调
@return SUCCESS - 成功;bleAlreadyInRequestedMode - 失败
*/
bStatus_t ALMProfile_RegisterAppCBs(almProfileCBs_t *appCallbacks)
{
if(appCallbacks)
{
s_pALMProfile_AppCBs = appCallbacks;
return (SUCCESS);
}
else
{
return (bleAlreadyInRequestedMode);
}
}
/**
@brief 设置ALM Profile参数
@param param Profile参数ID
@param len 写入的数据长度
@param pValue 指向写入的数据
@return SUCCESS - 成功;FAILURE - 失败
*/
bStatus_t ALMProfile_SetParameter(uint8 param, uint8 len, void *pValue)
{
bStatus_t ret = SUCCESS;
switch(param)
{
case ALMPROFILE_CHAR1:
if(len == ALMPROFILE_CHAR1_LEN)
{
VOID memcpy(s_ALMProfileChar1, pValue, ALMPROFILE_CHAR1_LEN);
}
else
{
ret = bleInvalidRange;
}
break;
default:
ret = INVALIDPARAMETER;
break;
}
return (ret);
}
/**
@brief 获取ALM Profile参数
@param param Profile参数ID
@param pValue 指向读出的数据
@return SUCCESS - 成功;FAILURE - 失败
*/
bStatus_t ALMProfile_GetParameter(uint8 param, void *pValue)
{
bStatus_t ret = SUCCESS;
switch(param)
{
case ALMPROFILE_CHAR1:
VOID memcpy(pValue, s_ALMProfileChar1, ALMPROFILE_CHAR1_LEN);
break;
default:
ret = INVALIDPARAMETER;
break;
}
return (ret);
}
/**
@brief Notify发送函数
@param param 特征值通道参数
@param connHandle 连接句柄
@param pValue 指向要通知的数据
@param len 要通知的数据长度,范围为0~SIMPLEPROFILE_CHAR6,最多20个字节
@return SUCCESS - 成功;FAILURE - 失败
*/
bStatus_t ALMProfile_Notify(uint8 param, uint16 connHandle, uint8 *pValue, uint8 len)
{
attHandleValueNoti_t attHandleValueNoti;
uint16 value;
bStatus_t ret = SUCCESS;
switch(param)
{
// 特征1
case ALMPROFILE_CHAR1:
// 读出CCC
value = GATTServApp_ReadCharCfg(connHandle, s_pALMProfileChar1Config);
// 判断CCC是否被打开
if(value & GATT_CLIENT_CFG_NOTIFY)
{
// 分配发送数据缓冲区
attHandleValueNoti.pValue = GATT_bm_alloc(connHandle,
ATT_HANDLE_VALUE_NOTI,
ALMPROFILE_CHAR1_LEN, NULL);
// 分配成功,则发送数据
if(attHandleValueNoti.pValue != NULL)
{
// 填充数据
attHandleValueNoti.handle = s_ALMProfileAttrTbl[ATTRTBL_ALM_CHAR1_IDX].handle;
attHandleValueNoti.len = len;
memcpy(attHandleValueNoti.pValue, value, len);
// 发送数据
if(GATT_Notification(connHandle, &attHandleValueNoti, FALSE) != SUCCESS)
{
GATT_bm_free((gattMsg_t *)&attHandleValueNoti, ATT_HANDLE_VALUE_NOTI);
}
}
else
ret = FAILURE;
}
else
ret = FAILURE;
break;
default:
ret = FAILURE;
break;
}
return (ret);
}
/*************************************END OF FILE*************************************/
2.2 alm_gatt_profile.h
#ifndef _ALM_GATT_PROFILE_H_
#define _ALM_GATT_PROFILE_H_
#ifdef __cplusplus
extern "C"
{
#endif
/*********************************************************************
* DEFINITIONS
*/
// Profile Parameters
#define ALMPROFILE_CHAR1 0 // RW uint8 - Profile Characteristic 1 value
// ALM Profile Service UUID
#define ALMPROFILE_SERV_UUID 0xFFE0
// CHAR1 UUID
#define ALMPROFILE_CHAR1_UUID 0xFFE1
// ALM Profile Services bit fields
#define ALMPROFILE_SERVICE 0x00000001
// Length of Characteristic in bytes
#define ALMPROFILE_CHAR1_LEN 8 // CHAR1 LEN
#define ALMPROFILE_CHAR2_LEN 1 // CHAR2 LEN
#define ALMPROFILE_CHAR3_LEN 1 // CHAR3 LEN
/*********************************************************************
* TYPEDEFS
*/
/*********************************************************************
* Profile Callbacks
*/
// Callback when a characteristic value has changed
typedef void (*almProfileChange_t)(uint8 paramID);
typedef struct
{
almProfileChange_t pfnALMProfileChange; // Called when characteristic value changes
} almProfileCBs_t;
/*********************************************************************
* API FUNCTIONS
*/
extern bStatus_t ALMProfile_AddService(uint32 services);
extern bStatus_t ALMProfile_RegisterAppCBs(almProfileCBs_t *appCallbacks);
extern bStatus_t ALMProfile_SetParameter(uint8 param, uint8 len, void *pValue);
extern bStatus_t ALMProfile_GetParameter(uint8 param, void *pValue);
extern bStatus_t ALMProfile_Notify(uint8 param, uint16 connHandle, uint8 *pValue, uint8 len);
#ifdef __cplusplus
}
#endif
#endif /* _ALM_GATT_PROFILE_H_ */
三、API调用
需包含头文件 alm_gatt_profile.h
ALMProfile_AddService
功能 | 通过向GATT服务端注册GATT属性来初始化ALM Profile服务 |
---|---|
函数定义 | bStatus_t ALMProfile_AddService(uint32 services) |
参数 | services:被添加的服务 |
返回 | SUCCESS - 成功;FAILURE - 失败 |
ALMProfile_RegisterAppCBs
功能 | 注册应用程序回调函数。只调用这个函数一次 |
---|---|
函数定义 | bStatus_t ALMProfile_RegisterAppCBs(almProfileCBs_t *appCallbacks) |
参数 | callbacks:指向应用程序的回调 |
返回 | SUCCESS - 成功;FAILURE - 失败 |
ALMProfile_SetParameter
功能 | 设置ALM Profile参数 |
---|---|
函数定义 | bStatus_t ALMProfile_SetParameter(uint8 param, uint8 len, void *pValue) |
参数1 | param:Profile参数ID |
参数2 | len:写入的数据长度 |
参数3 | pValue:指向写入的数据 |
返回 | SUCCESS - 成功;FAILURE - 失败 |
ALMProfile_GetParameter
功能 | 获取ALM Profile参数 |
---|---|
函数定义 | bStatus_t ALMProfile_GetParameter(uint8 param, void *pValue) |
参数1 | param:Profile参数ID |
参数2 | pValue:指向读出的数据 |
返回 | SUCCESS - 成功;FAILURE - 失败 |
ALMProfile_Notify
功能 | Notify发送函数 |
---|---|
函数定义 | bStatus_t ALMProfile_Notify(uint8 param, uint16 connHandle, uint8 *pValue, uint8 len) |
参数1 | param:特征值通道参数 |
参数2 | connHandle:连接句柄 |
参数3 | pValue:指向要通知的数据 |
参数4 | len:要通知的数据长度 |
返回 | SUCCESS - 成功;FAILURE - 失败 |
四、新增特征
4.1 添加只读特征
在DEFINITIONS中修改,原来5,新增一个带读的特征+3
#define SERVAPP_NUM_ATTR_SUPPORTED 8 // 原来5,新增一个带读的特征+3
在GLOBAL VARIABLES中新增
// 特征2 UUID: 0xFFE2
CONST uint8 g_ALMProfileChar2UUID[ATT_BT_UUID_SIZE] =
{
LO_UINT16(ALMPROFILE_CHAR2_UUID), HI_UINT16(ALMPROFILE_CHAR2_UUID)
};
在Profile Attributes - variables中新增
/*--------------------- 特征2 ---------------------*/
static uint8 s_ALMProfileChar2Props = GATT_PROP_READ; // 特征权限
static uint8 s_ALMProfileChar2[ALMPROFILE_CHAR2_LEN] = {0}; // 特征值
static uint8 s_ALMProfileChar2UserDesp[10] = "ALM Char2\0"; // 特征用户描述
在Profile Attributes - Table中新增
/*--------------------- 特征2 ---------------------*/
// 特征2的权限
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&s_ALMProfileChar2Props
},
// 特征2的值
{
{ ATT_BT_UUID_SIZE, g_ALMProfileChar2UUID },
GATT_PERMIT_READ,
0,
s_ALMProfileChar2
},
// 特征2的用户描述
{
{ ATT_BT_UUID_SIZE, charUserDescUUID },
GATT_PERMIT_READ,
0,
s_ALMProfileChar2UserDesp
},
在almProfile_ReadAttrCB回调函数中,switch(uuid)增加一个case,因为特征2有读权限
case ALMPROFILE_CHAR2_UUID:
*pLen = ALMPROFILE_CHAR2_LEN;
VOID memcpy(pValue, pAttr->pValue, ALMPROFILE_CHAR2_LEN);
break;
在ALMProfile_SetParameter函数中新增
case ALMPROFILE_CHAR2:
if(len == ALMPROFILE_CHAR2_LEN)
{
VOID memcpy(s_ALMProfileChar2, pValue, ALMPROFILE_CHAR2_LEN);
}
else
{
ret = bleInvalidRange;
}
break;
在ALMProfile_GetParameter函数中新增
case ALMPROFILE_CHAR2:
VOID memcpy(pValue, s_ALMProfileChar2, ALMPROFILE_CHAR2_LEN);
break;
在alm_gatt_profile.h头文件中新增
#define ALMPROFILE_CHAR2 1
#define ALMPROFILE_CHAR2_UUID 0xFFE2 // CHAR2 UUID
#define ALMPROFILE_CHAR2_LEN 1
4.2 添加可读写特征
在DEFINITIONS中修改,原来8,新增一个带读的特征+3
#define SERVAPP_NUM_ATTR_SUPPORTED 11 // 原来8,新增一个带读的特征+3
在GLOBAL VARIABLES中新增
// 特征3 UUID: 0xFFE3
CONST uint8 g_ALMProfileChar3UUID[ATT_BT_UUID_SIZE] =
{
LO_UINT16(ALMPROFILE_CHAR3_UUID), HI_UINT16(ALMPROFILE_CHAR3_UUID)
};
在Profile Attributes - variables中新增
/*--------------------- 特征3 ---------------------*/
static uint8 s_ALMProfileChar3Props = GATT_PROP_READ | // 特征权限
GATT_PROP_WRITE;
static uint8 s_ALMProfileChar3[ALMPROFILE_CHAR3_LEN] = {0}; // 特征值
static uint8 s_ALMProfileChar3UserDesp[10] = "ALM Char3\0"; // 特征用户描述
在Profile Attributes - Table中新增
/*--------------------- 特征3 ---------------------*/
// 特征3的权限
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&s_ALMProfileChar3Props
},
// 特征2的值
{
{ ATT_BT_UUID_SIZE, g_ALMProfileChar3UUID },
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
0,
s_ALMProfileChar3
},
// 特征3的用户描述
{
{ ATT_BT_UUID_SIZE, charUserDescUUID },
GATT_PERMIT_READ,
0,
s_ALMProfileChar3UserDesp
},
在almProfile_ReadAttrCB回调函数中,switch(uuid)增加一个case,因为特征3有读权限
case ALMPROFILE_CHAR3_UUID:
*pLen = ALMPROFILE_CHAR3_LEN;
VOID memcpy(pValue, pAttr->pValue, ALMPROFILE_CHAR3_LEN);
break;
在almProfile_WriteAttrCB回调函数中,switch(uuid)增加一个case,因为特征3有写权限
case ALMPROFILE_CHAR3_UUID:
if(offset == 0)
{
if(len != ALMPROFILE_CHAR3_LEN)
{
status = ATT_ERR_INVALID_VALUE_SIZE;
}
}
else
{
status = ATT_ERR_ATTR_NOT_LONG;
}
// 将接收到的数据写进特征值中,并且置标志位
if(status == SUCCESS)
{
VOID memcpy(pAttr->pValue, pValue, len); // len - 原ALMPROFILE_CHAR3_LEN
notifyApp = ALMPROFILE_CHAR3;
}
break;
在ALMProfile_SetParameter函数中新增
case ALMPROFILE_CHAR3:
if(len == ALMPROFILE_CHAR3_LEN)
{
VOID memcpy(s_ALMProfileChar3, pValue, ALMPROFILE_CHAR3_LEN);
}
else
{
ret = bleInvalidRange;
}
break;
在ALMProfile_GetParameter函数中新增
case ALMPROFILE_CHAR3:
VOID memcpy(pValue, s_ALMProfileChar3, ALMPROFILE_CHAR3_LEN);
break;
在alm_gatt_profile.h头文件中新增
#define ALMPROFILE_CHAR3 2
#define ALMPROFILE_CHAR3_UUID 0xFFE3 // CHAR3 UUID
#define ALMPROFILE_CHAR3_LEN 1
4.3 添加可读可写可通知特征
如原来的特征1
五、加入服务和特征到工程
5.1 添加头文件
#include "alm_gatt_profile.h"
5.2 添加初始化代码
例在multi_role.c 的 multi_role_init 函数末尾中
ALMProfile_AddService(GATT_ALL_SERVICES); // 自定义服务
// ALM GATT Profile
{
// 初始化特征值
ALMProfile_SetParameter(ALMPROFILE_CHAR1, ALMPROFILE_CHAR1_LEN, &g_ALMProfile_Char1Value);
// 初始化特征2
ALMProfile_SetParameter(ALMPROFILE_CHAR2, ALMPROFILE_CHAR2_LEN, &g_ALMProfile_Char2Value);
// 初始化特征3
ALMProfile_SetParameter(ALMPROFILE_CHAR3, ALMPROFILE_CHAR3_LEN, &g_ALMProfile_Char3Value);
// 注册ALM GATT Profile的回调函数
VOID ALMProfile_RegisterAppCBs(&simpleBLEPeripheral_ALMProfileCBs);
}
5.3 定义服务的回调函数及处理函数
static void ALM_Profile_ChangeCB(uint8 paramId)
{
multi_role_enqueueMsg(MR_ALM_CHAR_CHANGE_EVT, paramId);
}
static void ALM_Profile_CharValueChangeEvt(uint8 paramId)
{
uint16 connHandle;
uint8 buffer[20] = {0};
uint8* pValue = buffer;
// 判断是哪个特征值
switch(paramId)
{
// 特征值 1
case ALMPROFILE_CHAR1:
{
// 获取连接句柄,GAPROLE_CONNHANDLE在主从一体工程在multi.h中或从机工程在peripheral.h中
GAPRole_GetParameter(GAPROLE_CONNHANDLE, &connHandle);
// 写一个 20 字节的测试缓冲区的数据
for(uint8 i = 0; i < 20; i++)
{
*(pValue + i) = i;
}
// 发送数据
ALMProfile_Notify(ALMPROFILE_CHAR1, connHandle, pValue, 20);
break;
}
// 特征值 2
case ALMPROFILE_CHAR2:
break;
// 特征值 3
case ALMPROFILE_CHAR3:
break;
// 其他
default:
break;
}
}
5.4 声明服务的回调函数和处理函数
static void ALM_Profile_ChangeCB(uint8 paramId);
static void ALM_Profile_CharValueChangeEvt(uint8 paramId);
5.5 注册回调函数
static almProfileCBs_t simpleBLEPeripheral_ALMProfileCBs =
{
ALM_Profile_ChangeCB // Charactersitic value change callback
};
5.6 添加自定义服务处理事件
① 添加自定义服务处理事件的宏定义
#define MR_ALM_CHAR_CHANGE_EVT Event_Id_08
② 添加自定义服务处理事件的处理部分
例在multi_role.c的multi_role_processAppMsg函数中添加
case MR_ALM_CHAR_CHANGE_EVT:
ALM_Profile_CharValueChangeEvt(*(pMsg->pData));
break;
5.7 运行结果
参考【CC2640R2F】香瓜CC2640R2F之自定义服务 中实验结果部分
• 由 Leung 写于 2019 年 3 月 13 日