profile 可以理解为一种规范,建立的蓝牙应用任务,蓝牙任务实际上分为两类:标准蓝牙任务规范 profile(公有任务),非标准蓝牙任务规范 profile(私有任务)。
service 可以理解为一个服务,在 BLE 从机中有多个服务,例如:电量信息服务、系统信息服务等;
每个 service 中又包含多个 characteristic 特征值;
每个具体的 characteristic 特征值才是 BLE 通信的主题,比如当前的电量是 80%,电量的 characteristic 特征值存在从机的 profile 里,这样主机就可以通过这个 characteristic 来读取 80% 这个数据。
GATT 服务一般包含几个具有相关的功能,比如特定传感器的读取和设置,人机接口的输入输出。组织具有相关的特性到服务中既实用又有效,因为它使得逻辑上和用户数据上的边界变得更加清晰,同时它也有助于不同应用程序间代码的重用。
characteristic 特征,BLE 主从机的通信均是通过 characteristic 来实现,可以理解为一个标签,通过这个标签可以获取或者写入想要的内容。
uuid 通用唯一识别码,我们刚才提到的 service 和 characteristic 都需要一个唯一的 uuid 来标识;
每个从机都会有一个 profile,不管是自定义的 simpleprofile,还是标准的防丢器 profile,他们都是由一些 service 组成,每个 service 又包含了多个 characteristic,主机和从机之间的通信,均是通过characteristic来实现。
举个例子,假设蓝牙设备中有 2 个服务,温湿度服务(假设 UUID 为 0x1110)和电量服务(假设 UUID 为 0x2220)。其中温湿度服务中包含了温度特征(假设 UUID 为 0x1111)、湿度特征(假设 UUID 为0x1112)。
此时你想用另一个 NRF52832 作为主机读取温度值,那么 NRF52832 主机会做如下事情:
1)连接设备:扫描并连接你的蓝牙设备从机。
2)发现服务:查找设备的服务中是否包含 UUID 为 0x1110 的服务(温湿度服务)。
3)发现特征:查找 UUID 为 0x1110 的服务(温湿度服务)中是否包含 UUID 为 0x1111 的特征值(温度特征值)。
4)获得特征句柄:查找到温度特征后,获取问读特征句柄,假设为 0x0038。
5)利用句柄来读取温度值:使用 0x0038 的句柄发送读指令,则此时 NRF52832 主机可读取到 NRF52832 从机中的温度值。
注意:以下出现缺失common.h文件错误,去除即可。uint8改为uint8_t或unsigned char或自己宏定义
链接:https://pan.baidu.com/s/1XrsBpbX7KvrntZCHAH07Ag 提取码:e547
/*********************************************************************
* INCLUDES
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_ALMINITPROFILE)
#include "ble_srv_common.h"
#include "alm_init_profile.h"
#include "common.h"
static void almInitProfile_writeAttrCallback(AlmInitProfile_t *pAlmInitProfile, ble_evt_t const *pBleEvent);
/*********************************************************************
* LOCAL VARIABLES
*/
BLE_ALMINITPROFILE_DEF(m_almInitProfile);
/*********************************************************************
* EXTERN FUNCTIONS
*/
// 在主函数定义
extern void AlmInitProfile_HandleCharValue(uint16 connHandle, uint8 charId, AlmInitProfile_t *pAlmInitProfile,
const uint8 *pCharValue, uint16 length);
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/**
@brief 添加ALM Init Profile服务
@param 无
@return NRF_SUCCESS - 成功;其他值 - 失败
*/
uint32 AlmInitProfile_AddService(void)
{
AlmInitProfileCallback_t almInitProfileCallback = {0};
almInitProfileCallback.almInitProfileCharWriteHandler = AlmInitProfile_HandleCharValue; // 调用外部函数:应用层处理特征值
return AlmInitProfile_RegisterAppCallback(&m_almInitProfile, &almInitProfileCallback);
}
/**
@brief 注册应用程序回调函数。只调用这个函数一次
@param pAlmInitProfile -[out] ALM初始化服务结构体
@param pAppCallback -[in] 指向应用程序的回调
@return NRF_SUCCESS - 成功;其他值 - 失败
*/
uint32 AlmInitProfile_RegisterAppCallback(AlmInitProfile_t *pAlmInitProfile, const AlmInitProfileCallback_t *pAppCallback)
{
uint32 errCode;
ble_uuid_t bleUuid;
ble_add_char_params_t addCharParams;
// 初始化服务结构体
pAlmInitProfile->almInitProfileCharWriteHandler = pAppCallback->almInitProfileCharWriteHandler;
/*--------------------- 服务 ---------------------*/
ble_uuid128_t baseUuid = {ALMINITPROFILE_UUID_BASE};
errCode = sd_ble_uuid_vs_add(&baseUuid, &pAlmInitProfile->uuidType);
VERIFY_SUCCESS(errCode);
bleUuid.type = pAlmInitProfile->uuidType;
bleUuid.uuid = ALMINITPROFILE_UUID_SERVICE;
errCode = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &bleUuid, &pAlmInitProfile->serviceHandle);
VERIFY_SUCCESS(errCode);
/*--------------------- 特征1 ---------------------*/
memset(&addCharParams, 0, sizeof(addCharParams));
addCharParams.uuid = ALMINITPROFILE_UUID_CHAR1;
addCharParams.uuid_type = pAlmInitProfile->uuidType;
addCharParams.init_len = sizeof(uint8_t);
addCharParams.max_len = ALMINITPROFILE_CHAR1_LEN; // 特征长度
addCharParams.char_props.read = 1; // 可读
addCharParams.char_props.write = 1; // 可写
addCharParams.char_props.notify = 1; // 可通知
addCharParams.read_access = SEC_OPEN;
addCharParams.write_access = SEC_OPEN;
addCharParams.cccd_write_access = SEC_OPEN;
errCode = characteristic_add(pAlmInitProfile->serviceHandle, &addCharParams, &pAlmInitProfile->char1Handle);
if(errCode != NRF_SUCCESS)
{
return errCode;
}
/*--------------------- 特征2 ---------------------*/
memset(&addCharParams, 0, sizeof(addCharParams));
addCharParams.uuid = ALMINITPROFILE_UUID_CHAR2;
addCharParams.uuid_type = pAlmInitProfile->uuidType;
addCharParams.init_len = sizeof(uint8_t);
addCharParams.max_len = ALMINITPROFILE_CHAR2_LEN; // 特征长度
addCharParams.char_props.read = 1; // 可读
addCharParams.char_props.write = 1; // 可写
addCharParams.read_access = SEC_OPEN;
addCharParams.write_access = SEC_OPEN;
errCode = characteristic_add(pAlmInitProfile->serviceHandle, &addCharParams, &pAlmInitProfile->char2Handle);
if(errCode != NRF_SUCCESS)
{
return errCode;
}
/*--------------------- 特征3 ---------------------*/
memset(&addCharParams, 0, sizeof(addCharParams));
addCharParams.uuid = ALMINITPROFILE_UUID_CHAR3;
addCharParams.uuid_type = pAlmInitProfile->uuidType;
addCharParams.init_len = sizeof(uint8_t);
addCharParams.max_len = ALMINITPROFILE_CHAR3_LEN; // 特征长度
addCharParams.char_props.read = 1; // 可读
addCharParams.char_props.write = 1; // 可写
addCharParams.read_access = SEC_OPEN;
addCharParams.write_access = SEC_OPEN;
errCode = characteristic_add(pAlmInitProfile->serviceHandle, &addCharParams, &pAlmInitProfile->char3Handle);
if(errCode != NRF_SUCCESS)
{
return errCode;
}
/*--------------------- 特征4 ---------------------*/
memset(&addCharParams, 0, sizeof(addCharParams));
addCharParams.uuid = ALMINITPROFILE_UUID_CHAR4;
addCharParams.uuid_type = pAlmInitProfile->uuidType;
addCharParams.init_len = sizeof(uint8_t);
addCharParams.max_len = ALMINITPROFILE_CHAR4_LEN; // 特征长度
addCharParams.char_props.read = 1; // 可读
addCharParams.char_props.write = 1; // 可写
addCharParams.read_access = SEC_OPEN;
addCharParams.write_access = SEC_OPEN;
errCode = characteristic_add(pAlmInitProfile->serviceHandle, &addCharParams, &pAlmInitProfile->char4Handle);
if(errCode != NRF_SUCCESS)
{
return errCode;
}
return errCode;
}
/**
@brief 处理来自蓝牙协议栈的应用事件
@param pBleEvent -[in] 来自蓝牙协议栈的事件
@param pContext -[in] ALM初始化服务结构体
@return 无
*/
void HandleAlmInitProfileOnBleStackEvent(ble_evt_t const *pBleEvent, void *pContext)
{
AlmInitProfile_t *pAlmInitProfile = (AlmInitProfile_t *)pContext;
switch(pBleEvent->header.evt_id)
{
case BLE_GATTS_EVT_WRITE:
almInitProfile_writeAttrCallback(pAlmInitProfile, pBleEvent);
break;
default:
// No implementation needed.
break;
}
}
/**
@brief 推送通知数据
@param connHandle -[in] 连接句柄
@param pAlmInitProfile -[in] ALM初始化服务结构体
@param pData -[in] 通知内容
@param dataLen -[in] 通知内容长度
@return NRF_SUCCESS - 成功;其他值 - 失败
*/
uint32 AlmInitProfile_PushNotifyData(uint16 connHandle, AlmInitProfile_t *pAlmInitProfile, uint8 *pData, uint16 dataLen)
{
ble_gatts_hvx_params_t params;
if(connHandle == BLE_CONN_HANDLE_INVALID)
{
return NRF_ERROR_NOT_FOUND;
}
if(dataLen > ALMINITPROFILE_CHAR1_LEN)
{
return NRF_ERROR_INVALID_PARAM;
}
memset(¶ms, 0, sizeof(params));
params.type = BLE_GATT_HVX_NOTIFICATION;
params.handle = pAlmInitProfile->char1Handle.value_handle;
params.p_data = pData;
params.p_len = &dataLen;
return sd_ble_gatts_hvx(connHandle, ¶ms);
}
/*********************************************************************
* LOCAL FUNCTIONS
*/
/**
@brief 写属性,在写入之前验证属性数据
@param pAlmInitProfile -[in] ALM初始化服务结构体
@param pBleEvent -[in] 来自蓝牙协议栈的事件
@return 无
*/
static void almInitProfile_writeAttrCallback(AlmInitProfile_t *pAlmInitProfile, ble_evt_t const *pBleEvent)
{
ble_gatts_evt_write_t const *pEventWrite = &pBleEvent->evt.gatts_evt.params.write;
uint8 characteristicId;
/*--------------------- 特征1 ---------------------*/
if((pEventWrite->handle == pAlmInitProfile->char1Handle.value_handle)
&& (pEventWrite->len == ALMINITPROFILE_CHAR1_LEN)
&& (pAlmInitProfile->almInitProfileCharWriteHandler != NULL))
{
characteristicId = ALMINITPROFILE_CHAR1;
pAlmInitProfile->almInitProfileCharWriteHandler(pBleEvent->evt.gap_evt.conn_handle, characteristicId,
pAlmInitProfile, pEventWrite->data, pEventWrite->len);
}
/*--------------------- 特征2 ---------------------*/
else if((pEventWrite->handle == pAlmInitProfile->char2Handle.value_handle)
&& (pEventWrite->len == ALMINITPROFILE_CHAR2_LEN)
&& (pAlmInitProfile->almInitProfileCharWriteHandler != NULL))
{
characteristicId = ALMINITPROFILE_CHAR2;
pAlmInitProfile->almInitProfileCharWriteHandler(pBleEvent->evt.gap_evt.conn_handle, characteristicId,
pAlmInitProfile, pEventWrite->data, pEventWrite->len);
}
/*--------------------- 特征3 ---------------------*/
else if((pEventWrite->handle == pAlmInitProfile->char3Handle.value_handle)
&& (pEventWrite->len == ALMINITPROFILE_CHAR3_LEN)
&& (pAlmInitProfile->almInitProfileCharWriteHandler != NULL))
{
characteristicId = ALMINITPROFILE_CHAR3;
pAlmInitProfile->almInitProfileCharWriteHandler(pBleEvent->evt.gap_evt.conn_handle, characteristicId,
pAlmInitProfile, pEventWrite->data, pEventWrite->len);
}
/*--------------------- 特征4 ---------------------*/
else if((pEventWrite->handle == pAlmInitProfile->char4Handle.value_handle)
&& (pEventWrite->len == ALMINITPROFILE_CHAR4_LEN)
&& (pAlmInitProfile->almInitProfileCharWriteHandler != NULL))
{
characteristicId = ALMINITPROFILE_CHAR4;
pAlmInitProfile->almInitProfileCharWriteHandler(pBleEvent->evt.gap_evt.conn_handle, characteristicId,
pAlmInitProfile, pEventWrite->data, pEventWrite->len);
}
}
#endif // NRF_MODULE_ENABLED(BLE_ALMINITPROFILE)
/****************************************************END OF FILE****************************************************/
#ifndef _ALM_INIT_PROFILE_H_
#define _ALM_INIT_PROFILE_H_
/*********************************************************************
* INCLUDES
*/
#include
#include
#include "ble.h"
#include "ble_srv_common.h"
#include "nrf_sdh_ble.h"
#include "common.h"
/*********************************************************************
* DEFINITIONS
*/
#ifdef __cplusplus
extern "C" {
#endif
#define BLE_ALMINITPROFILE_DEF(_name) \
static AlmInitProfile_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_ALMINITPROFILE_BLE_OBSERVER_PRIO, \
HandleAlmInitProfileOnBleStackEvent, &_name)
#define ALMINITPROFILE_UUID_BASE {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, \
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
#define ALMINITPROFILE_UUID_SERVICE 0xFEF0
#define ALMINITPROFILE_UUID_CHAR1 0xFEFF
#define ALMINITPROFILE_UUID_CHAR2 0xFE00
#define ALMINITPROFILE_UUID_CHAR3 0xFE10
#define ALMINITPROFILE_UUID_CHAR4 0xFE31
// Profile Parameters
#define ALMINITPROFILE_CHAR1 0 // uint8 - Profile Characteristic 1 value
#define ALMINITPROFILE_CHAR2 1 // uint8 - Profile Characteristic 2 value
#define ALMINITPROFILE_CHAR3 2 // uint8 - Profile Characteristic 3 value
#define ALMINITPROFILE_CHAR4 3 // uint8 - Profile Characteristic 4 value
#define ALMINITPROFILE_CHAR5 4 // uint8 - Profile Characteristic 5 value
// Length of Characteristic in bytes
#define ALMINITPROFILE_CHAR1_LEN 20 // CHAR1 LEN
#define ALMINITPROFILE_CHAR2_LEN 20 // CHAR2 LEN
#define ALMINITPROFILE_CHAR3_LEN 20 // CHAR3 LEN
#define ALMINITPROFILE_CHAR4_LEN 20 // CHAR4 LEN
#define ALMINITPROFILE_CHAR5_LEN 20 // CHAR5 LEN
/*********************************************************************
* TYPEDEFS
*/
// Forward declaration of the AlmInitProfile_t type.
typedef struct almInitProfileService_s AlmInitProfile_t;
typedef void (*AlmInitProfileCharWriteHandler_t)(uint16 connHandle, uint8 charId, AlmInitProfile_t *pAlmInitProfile,
const uint8 *pCharValue, uint8 length);
/*
ALM Init Service init structure.
This structure contains all options
and data needed for initialization of the service.
*/
typedef struct
{
AlmInitProfileCharWriteHandler_t almInitProfileCharWriteHandler; // Event handler to be called when the Characteristic is written.
} AlmInitProfileCallback_t;
/*
ALM Init Service structure.
This structure contains various status information for the service.
*/
struct almInitProfileService_s
{
uint16 serviceHandle; // Handle of ALM Init Service (as provided by the BLE stack).
ble_gatts_char_handles_t char1Handle; // Handles related to the Characteristic 1.
ble_gatts_char_handles_t char2Handle; // Handles related to the Characteristic 2.
ble_gatts_char_handles_t char3Handle; // Handles related to the Characteristic 3.
ble_gatts_char_handles_t char4Handle; // Handles related to the Characteristic 4.
uint8 uuidType; // UUID type for the ALM Init Service.
AlmInitProfileCharWriteHandler_t almInitProfileCharWriteHandler; // Event handler to be called when the Characteristic is written.
};
/*********************************************************************
* API FUNCTIONS
*/
uint32 AlmInitProfile_AddService(void);
uint32 AlmInitProfile_RegisterAppCallback(AlmInitProfile_t *pAlmInitProfile, const AlmInitProfileCallback_t *pAppCallback);
void HandleAlmInitProfileOnBleStackEvent(ble_evt_t const *pBleEvent, void *pContext);
uint32 AlmInitProfile_PushNotifyData(uint16 connHandle, AlmInitProfile_t *pAlmInitProfile, uint8 *pData, uint16 dataLen);
#ifdef __cplusplus
}
#endif
#endif /* _ALM_INIT_PROFILE_H_ */
用到的数据结构 AlmInitProfileCallback_t 和 AlmInitProfile_t
自定义服务不依赖于任何启动或停止,所以只使用一个函数作为回调函数,当特征被写入时调用该处理。
/*
ALM Init Service init structure.
This structure contains all options
and data needed for initialization of the service.
*/
typedef struct
{
AlmInitProfileCharWriteHandler_t almInitProfileCharWriteHandler; // Event handler to be called when the Characteristic is written.
} AlmInitProfileCallback_t;
在这个结构体中,函数类型的定义如下(在头文件中必须在 AlmInitProfileCallback_t 定义之前添加)
typedef void (*AlmInitProfileCharWriteHandler_t)(uint16 connHandle, uint8 charId, AlmInitProfile_t *pAlmInitProfile,
const uint8 *pCharValue, uint8 length);
该结构体包括:
// Forward declaration of the AlmInitProfile_t type.
typedef struct almInitProfileService_s AlmInitProfile_t;
/*
ALM Init Service structure.
This structure contains various status information for the service.
*/
struct almInitProfileService_s
{
uint16 serviceHandle; // Handle of ALM Init Service (as provided by the BLE stack).
ble_gatts_char_handles_t char1Handle; // Handles related to the Characteristic 1.
ble_gatts_char_handles_t char2Handle; // Handles related to the Characteristic 2.
ble_gatts_char_handles_t char3Handle; // Handles related to the Characteristic 3.
ble_gatts_char_handles_t char4Handle; // Handles related to the Characteristic 4.
uint8 uuidType; // UUID type for the ALM Init Service.
AlmInitProfileCharWriteHandler_t almInitProfileCharWriteHandler; // Event handler to be called when the Characteristic is written.
};
AlmInitProfile_AddService 添加服务函数,相当于服务初始化。这个函数主要完成上面定义的结构体 AlmInitProfileCallback_t 的调用。
首先定义了一个服务回调的结构体,然后指定应用层的处理函数(可在 main.c 定义,在这里调用),然后初始化服务结构体的一些参数。
/**
@brief 添加ALM Init Profile服务
@param 无
@return NRF_SUCCESS - 成功;其他值 - 失败
*/
uint32 AlmInitProfile_AddService(void)
{
AlmInitProfileCallback_t almInitProfileCallback = {0};
almInitProfileCallback.almInitProfileCharWriteHandler = AlmInitProfile_HandleCharValue; // 调用外部函数:应用层处理特征值
return AlmInitProfile_RegisterAppCallback(&m_almInitProfile, &almInitProfileCallback);
}
}
在这里对服务结构体的参数进行初始化配置
/**
@brief 注册应用程序回调函数。只调用这个函数一次
@param pAlmInitProfile -[out] ALM初始化服务结构体
@param pAppCallback -[in] 指向应用程序的回调
@return NRF_SUCCESS - 成功;其他值 - 失败
*/
uint32 AlmInitProfile_RegisterAppCallback(AlmInitProfile_t *pAlmInitProfile, const AlmInitProfileCallback_t *pAppCallback)
{
uint32 errCode;
ble_uuid_t bleUuid;
ble_add_char_params_t addCharParams;
// 初始化服务结构体
pAlmInitProfile->almInitProfileCharWriteHandler = pAppCallback->almInitProfileCharWriteHandler;
/*--------------------- 服务 ---------------------*/
ble_uuid128_t baseUuid = {ALMINITPROFILE_UUID_BASE};
errCode = sd_ble_uuid_vs_add(&baseUuid, &pAlmInitProfile->uuidType);
VERIFY_SUCCESS(errCode);
bleUuid.type = pAlmInitProfile->uuidType;
bleUuid.uuid = ALMINITPROFILE_UUID_SERVICE;
errCode = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &bleUuid, &pAlmInitProfile->serviceHandle);
VERIFY_SUCCESS(errCode);
/*--------------------- 特征1 ---------------------*/
memset(&addCharParams, 0, sizeof(addCharParams));
addCharParams.uuid = ALMINITPROFILE_UUID_CHAR1;
addCharParams.uuid_type = pAlmInitProfile->uuidType;
addCharParams.init_len = sizeof(uint8_t);
addCharParams.max_len = ALMINITPROFILE_CHAR1_LEN; // 特征长度
addCharParams.char_props.read = 1; // 可读
addCharParams.char_props.write = 1; // 可写
addCharParams.char_props.notify = 1; // 可通知
addCharParams.read_access = SEC_OPEN;
addCharParams.write_access = SEC_OPEN;
addCharParams.cccd_write_access = SEC_OPEN;
errCode = characteristic_add(pAlmInitProfile->serviceHandle, &addCharParams, &pAlmInitProfile->char1Handle);
if(errCode != NRF_SUCCESS)
{
return errCode;
}
/*--------------------- 特征2 ---------------------*/
memset(&addCharParams, 0, sizeof(addCharParams));
addCharParams.uuid = ALMINITPROFILE_UUID_CHAR2;
addCharParams.uuid_type = pAlmInitProfile->uuidType;
addCharParams.init_len = sizeof(uint8_t);
addCharParams.max_len = ALMINITPROFILE_CHAR2_LEN; // 特征长度
addCharParams.char_props.read = 1; // 可读
addCharParams.char_props.write = 1; // 可写
addCharParams.read_access = SEC_OPEN;
addCharParams.write_access = SEC_OPEN;
errCode = characteristic_add(pAlmInitProfile->serviceHandle, &addCharParams, &pAlmInitProfile->char2Handle);
if(errCode != NRF_SUCCESS)
{
return errCode;
}
/*--------------------- 特征3 ---------------------*/
memset(&addCharParams, 0, sizeof(addCharParams));
addCharParams.uuid = ALMINITPROFILE_UUID_CHAR3;
addCharParams.uuid_type = pAlmInitProfile->uuidType;
addCharParams.init_len = sizeof(uint8_t);
addCharParams.max_len = ALMINITPROFILE_CHAR3_LEN; // 特征长度
addCharParams.char_props.read = 1; // 可读
addCharParams.char_props.write = 1; // 可写
addCharParams.read_access = SEC_OPEN;
addCharParams.write_access = SEC_OPEN;
errCode = characteristic_add(pAlmInitProfile->serviceHandle, &addCharParams, &pAlmInitProfile->char3Handle);
if(errCode != NRF_SUCCESS)
{
return errCode;
}
/*--------------------- 特征4 ---------------------*/
memset(&addCharParams, 0, sizeof(addCharParams));
addCharParams.uuid = ALMINITPROFILE_UUID_CHAR4;
addCharParams.uuid_type = pAlmInitProfile->uuidType;
addCharParams.init_len = sizeof(uint8_t);
addCharParams.max_len = ALMINITPROFILE_CHAR4_LEN; // 特征长度
addCharParams.char_props.read = 1; // 可读
addCharParams.char_props.write = 1; // 可写
addCharParams.read_access = SEC_OPEN;
addCharParams.write_access = SEC_OPEN;
errCode = characteristic_add(pAlmInitProfile->serviceHandle, &addCharParams, &pAlmInitProfile->char4Handle);
if(errCode != NRF_SUCCESS)
{
return errCode;
}
return errCode;
}
在 AlmInitProfile_RegisterAppCallback 函数中,对服务的基础 UUID 进行了设置。
/*--------------------- 服务 ---------------------*/
ble_uuid128_t baseUuid = {ALMINITPROFILE_UUID_BASE};
errCode = sd_ble_uuid_vs_add(&baseUuid, &pAlmInitProfile->uuidType);
VERIFY_SUCCESS(errCode);
bleUuid.type = pAlmInitProfile->uuidType;
bleUuid.uuid = ALMINITPROFILE_UUID_SERVICE;
errCode = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &bleUuid, &pAlmInitProfile->serviceHandle);
VERIFY_SUCCESS(errCode);
在 alm_init_profile.h 中对 128 位的基础 UUID 和 16 位的服务 UUID 进行了宏定义。
#define ALMINITPROFILE_UUID_BASE {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, \
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
#define ALMINITPROFILE_UUID_SERVICE 0xFEF0
#define ALMINITPROFILE_UUID_CHAR1 0xFEFF
#define ALMINITPROFILE_UUID_CHAR2 0xFE00
#define ALMINITPROFILE_UUID_CHAR3 0xFE10
#define ALMINITPROFILE_UUID_CHAR4 0xFE31
// Profile Parameters
#define ALMINITPROFILE_CHAR1 0 // uint8 - Profile Characteristic 1 value
#define ALMINITPROFILE_CHAR2 1 // uint8 - Profile Characteristic 2 value
#define ALMINITPROFILE_CHAR3 2 // uint8 - Profile Characteristic 3 value
#define ALMINITPROFILE_CHAR4 3 // uint8 - Profile Characteristic 4 value
#define ALMINITPROFILE_CHAR5 4 // uint8 - Profile Characteristic 5 value
// Length of Characteristic in bytes
#define ALMINITPROFILE_CHAR1_LEN 20 // CHAR1 LEN
#define ALMINITPROFILE_CHAR2_LEN 20 // CHAR2 LEN
#define ALMINITPROFILE_CHAR3_LEN 20 // CHAR3 LEN
#define ALMINITPROFILE_CHAR4_LEN 20 // CHAR4 LEN
#define ALMINITPROFILE_CHAR5_LEN 20 // CHAR5 LEN
/*
ALM Init Service structure.
This structure contains various status information for the service.
*/
struct almInitProfileService_s
{
uint16 serviceHandle; // Handle of ALM Init Service (as provided by the BLE stack).
ble_gatts_char_handles_t char1Handle; // Handles related to the Characteristic 1.
ble_gatts_char_handles_t char2Handle; // Handles related to the Characteristic 2.
ble_gatts_char_handles_t char3Handle; // Handles related to the Characteristic 3.
ble_gatts_char_handles_t char4Handle; // Handles related to the Characteristic 4.
uint8 uuidType; // UUID type for the ALM Init Service.
AlmInitProfileCharWriteHandler_t almInitProfileCharWriteHandler; // Event handler to be called when the Characteristic is written.
};
开启可读的权限 addCharParams.char_props.read = 1
和 addCharParams.read_access = SEC_OPEN
/*--------------------- 特征1 ---------------------*/
memset(&addCharParams, 0, sizeof(addCharParams));
addCharParams.uuid = ALMINITPROFILE_UUID_CHAR1;
addCharParams.uuid_type = pAlmInitProfile->uuidType;
addCharParams.init_len = sizeof(uint8_t);
addCharParams.max_len = ALMINITPROFILE_CHAR1_LEN; // 特征长度
addCharParams.char_props.read = 1; // 可读
addCharParams.read_access = SEC_OPEN;
errCode = characteristic_add(pAlmInitProfile->serviceHandle, &addCharParams, &pAlmInitProfile->char1Handle);
if(errCode != NRF_SUCCESS)
{
return errCode;
}
开启可读的权限 addCharParams.char_props.read = 1
和 addCharParams.read_access = SEC_OPEN
开启可写的权限 addCharParams.char_props.write = 1
和 addCharParams.write_access = SEC_OPEN
/*--------------------- 特征2 ---------------------*/
memset(&addCharParams, 0, sizeof(addCharParams));
addCharParams.uuid = ALMINITPROFILE_UUID_CHAR2;
addCharParams.uuid_type = pAlmInitProfile->uuidType;
addCharParams.init_len = sizeof(uint8_t);
addCharParams.max_len = ALMINITPROFILE_CHAR2_LEN; // 特征长度
addCharParams.char_props.read = 1; // 可读
addCharParams.char_props.write = 1; // 可写
addCharParams.read_access = SEC_OPEN;
addCharParams.write_access = SEC_OPEN;
errCode = characteristic_add(pAlmInitProfile->serviceHandle, &addCharParams, &pAlmInitProfile->char2Handle);
if(errCode != NRF_SUCCESS)
{
return errCode;
}
特征被写入时的回调函数,在这里对写入长度进行验证
/**
@brief 写属性,在写入之前验证属性数据
@param pAlmInitProfile -[in] ALM初始化服务结构体
@param pBleEvent -[in] 来自蓝牙协议栈的事件
@return 无
*/
static void almInitProfile_writeAttrCallback(AlmInitProfile_t *pAlmInitProfile, ble_evt_t const *pBleEvent)
{
ble_gatts_evt_write_t const *pEventWrite = &pBleEvent->evt.gatts_evt.params.write;
uint8 characteristicId;
/*--------------------- 特征1 ---------------------*/
if((pEventWrite->handle == pAlmInitProfile->char1Handle.value_handle)
&& (pEventWrite->len == ALMINITPROFILE_CHAR1_LEN)
&& (pAlmInitProfile->almInitProfileCharWriteHandler != NULL))
{
characteristicId = ALMINITPROFILE_CHAR1;
pAlmInitProfile->almInitProfileCharWriteHandler(pBleEvent->evt.gap_evt.conn_handle, characteristicId,
pAlmInitProfile, pEventWrite->data, pEventWrite->len);
}
/*--------------------- 特征2 ---------------------*/
else if((pEventWrite->handle == pAlmInitProfile->char2Handle.value_handle)
&& (pEventWrite->len == ALMINITPROFILE_CHAR2_LEN)
&& (pAlmInitProfile->almInitProfileCharWriteHandler != NULL))
{
characteristicId = ALMINITPROFILE_CHAR2;
pAlmInitProfile->almInitProfileCharWriteHandler(pBleEvent->evt.gap_evt.conn_handle, characteristicId,
pAlmInitProfile, pEventWrite->data, pEventWrite->len);
}
}
开启可读的权限 addCharParams.char_props.read = 1
和 addCharParams.read_access = SEC_OPEN
开启可写的权限 addCharParams.char_props.write = 1
和 addCharParams.write_access = SEC_OPEN
开启通知的权限 addCharParams.char_props.notify = 1
和 addCharParams.cccd_write_access = SEC_OPEN
/*--------------------- 特征1 ---------------------*/
memset(&addCharParams, 0, sizeof(addCharParams));
addCharParams.uuid = ALMINITPROFILE_UUID_CHAR1;
addCharParams.uuid_type = pAlmInitProfile->uuidType;
addCharParams.init_len = sizeof(uint8_t);
addCharParams.max_len = ALMINITPROFILE_CHAR1_LEN; // 特征长度
addCharParams.char_props.read = 1; // 可读
addCharParams.char_props.write = 1; // 可写
addCharParams.char_props.notify = 1; // 可通知
addCharParams.read_access = SEC_OPEN;
addCharParams.write_access = SEC_OPEN;
addCharParams.cccd_write_access = SEC_OPEN;
errCode = characteristic_add(pAlmInitProfile->serviceHandle, &addCharParams, &pAlmInitProfile->char1Handle);
if(errCode != NRF_SUCCESS)
{
return errCode;
}
添加推送通知的函数
/**
@brief 推送通知数据
@param connHandle -[in] 连接句柄
@param pAlmInitProfile -[in] ALM初始化服务结构体
@param pData -[in] 通知内容
@param dataLen -[in] 通知内容长度
@return NRF_SUCCESS - 成功;其他值 - 失败
*/
uint32 AlmInitProfile_PushNotifyData(uint16 connHandle, AlmInitProfile_t *pAlmInitProfile, uint8 *pData, uint16 dataLen)
{
ble_gatts_hvx_params_t params;
if(connHandle == BLE_CONN_HANDLE_INVALID)
{
return NRF_ERROR_NOT_FOUND;
}
if(dataLen > ALMINITPROFILE_CHAR1_LEN)
{
return NRF_ERROR_INVALID_PARAM;
}
memset(¶ms, 0, sizeof(params));
params.type = BLE_GATT_HVX_NOTIFICATION;
params.handle = pAlmInitProfile->char1Handle.value_handle;
params.p_data = pData;
params.p_len = &dataLen;
return sd_ble_gatts_hvx(connHandle, ¶ms);
}
#ifndef BLE_TPS_ENABLED
#define BLE_TPS_ENABLED 0
#endif
// BLE_ALMINITPROFILE_ENABLED - ALM Init Service
#ifndef BLE_ALMINITPROFILE_ENABLED
#define BLE_ALMINITPROFILE_ENABLED 1
#endif
完成后在 Configuration Wizard 中显示勾选了该服务
// BLE_TPS_BLE_OBSERVER_PRIO
// Priority with which BLE events are dispatched to the TX Power Service.
#ifndef BLE_TPS_BLE_OBSERVER_PRIO
#define BLE_TPS_BLE_OBSERVER_PRIO 2
#endif
// BLE_ALMINITPROFILE_BLE_OBSERVER_PRIO
// Priority with which BLE events are dispatched to the ALM Init Service.
#ifndef BLE_ALMINITPROFILE_BLE_OBSERVER_PRIO
#define BLE_ALMINITPROFILE_BLE_OBSERVER_PRIO 2
#endif
改写原则:,每增加一个独立的 128bit UUID 服务,RAM 空间起始增加 0x10,同时应用空间减少 0x10。
如图,Start 起始位 0x20002A98+0x10;Size 空间 0xD568-0x10。
#include "alm_init_profile.h"
在服务初始化函数 services_init 中添加自定义服务
/**@brief Function for initializing services that will be used by the application.
*/
static void services_init(void)
{
ret_code_t err_code;
nrf_ble_qwr_init_t qwr_init = {0};
// Initialize Queued Write Module.
qwr_init.error_handler = nrf_qwr_error_handler;
err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
APP_ERROR_CHECK(err_code);
#if NRF_MODULE_ENABLED(BLE_ALMINITPROFILE)
// Initialize ALM Init Service.
err_code = AlmInitProfile_AddService();
APP_ERROR_CHECK(err_code);
#endif
}
在 main.c 中加入
/**
@brief 自定义初始化服务处理特征值函数
@param connHandle -[in] 连接句柄
@param charId -[in] 特征值ID
@param pAlmInitProfile -[in] ALM初始化服务结构体
@param pCharValue -[in] 写入特征值
@param length -[in] 写入特征长度
@return 无
*/
void AlmInitProfile_HandleCharValue(uint16 connHandle, uint8 charId, AlmInitProfile_t *pAlmInitProfile,
const uint8 *pCharValue, uint16 length)
{
switch(charId)
{
case ALMINITPROFILE_CHAR1: // 通知功能
AlmInitProfile_PushNotifyData(m_conn_handle, pAlmInitProfile, (uint8 *)pCharValue, ALMINITPROFILE_CHAR1_LEN); // 通知
break;
case ALMINITPROFILE_CHAR2:
NRF_LOG_INFO("ss2");
break;
case ALMINITPROFILE_CHAR3:
AlmInitProfile_PushNotifyData(m_conn_handle, pAlmInitProfile, (uint8 *)pCharValue, ALMINITPROFILE_CHAR3_LEN); // 通知
break;
case ALMINITPROFILE_CHAR4:
break;
default:
break;
}
}
• 由 Leung 写于 2020 年 3 月 7 日
• 参考:青风电子社区