
本文共 22151 字,大约阅读时间需要 73 分钟。
一、背景
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 从机中的温度值。二、移植文件
链接: 提取码:8t9u
将 alm_gatt_profile.c 和 alm_gatt_profile.h 两个文件拖拽至CCS工程的PROFILES文件夹下2.1 alm_gatt_profile.c
/********************************************************************* * INCLUDES */#include#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: 0xFFE0CONST uint8 g_ALMProfileServUUID[ATT_BT_UUID_SIZE] ={ LO_UINT16(ALMPROFILE_SERV_UUID), HI_UINT16(ALMPROFILE_SERV_UUID)}; // 特征1 UUID: 0xFFE1CONST 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 __cplusplusextern "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 changedtypedef 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: 0xFFE2CONST 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: 0xFFE3CONST 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 运行结果
参考 中实验结果部分
• 由 写于 2019 年 3 月 13 日
• 参考:
发表评论
最新留言
关于作者
