目录
[1 BT_GATT_SERVICE_DEFINE 的定义](#1 BT_GATT_SERVICE_DEFINE 的定义)
[1.1 宏定义说明](#1.1 宏定义说明)
[1.2 核心属性宏详解](#1.2 核心属性宏详解)
[2 使用示例](#2 使用示例)
[2.1 基本服务定义](#2.1 基本服务定义)
[2.2 带通知功能的温度服务](#2.2 带通知功能的温度服务)
[2.3 多特征复合服务](#2.3 多特征复合服务)
[3 高级用法介绍](#3 高级用法介绍)
[3.1 动态特征值更新](#3.1 动态特征值更新)
[3.2 安全连接处理](#3.2 安全连接处理)
[3.3 服务发现响应](#3.3 服务发现响应)
[3.4 自定义属性](#3.4 自定义属性)
[3.5 参考范例](#3.5 参考范例)
[3.5.1 服务组织结构](#3.5.1 服务组织结构)
[3.5.2 功耗优化](#3.5.2 功耗优化)
[3.5.3 错误处理](#3.5.3 错误处理)
[4 常见问题解决](#4 常见问题解决)
[5 配置选项 (prj.conf)](#5 配置选项 (prj.conf))
概述
BT_GATT_SERVICE_DEFINE
是 Zephyr 蓝牙协议栈中用于定义和注册 GATT 服务 的核心宏。它允许开发者以声明式的方式创建完整的 GATT 服务结构,包括服务本身、特征、描述符以及相关回调函数。该宏是构建 Zephyr 蓝牙服务的基石,通过合理组织服务结构、实现高效回调函数和优化资源配置,可以创建稳定可靠且功能丰富的 GATT 服务。
1 BT_GATT_SERVICE_DEFINE
的定义
1.1 宏定义说明
1) 宏定义原型
cpp
BT_GATT_SERVICE_DEFINE(_name, _attrs...)
2) 参数说明
参数 | 说明 |
---|---|
_name |
服务名称(用于在代码中引用) |
_attrs |
服务属性列表(服务声明、特征、描述符等) |
3) 核心功能
服务声明:定义服务及其 UUID
特征定义:包含特征值、属性、权限和回调
描述符添加:如 CCCD(客户端特征配置描述符)
自动注册:服务在蓝牙协议栈初始化时自动注册
内存管理:自动分配服务所需的内存空间
1.2 核心属性宏详解
1) 服务声明宏
cpp
BT_GATT_PRIMARY_SERVICE(&service_uuid)
参数 | 说明 |
---|---|
service_uuid |
服务UUID (16位、32位或128位) |
2) 特征声明宏
cpp
BT_GATT_CHARACTERISTIC(&char_uuid, properties, perm, read_func, write_func, user_data)
参数 | 说明 |
---|---|
char_uuid |
特征UUID |
properties |
特征属性 (BT_GATT_CHRC_READ , WRITE , NOTIFY , INDICATE 等) |
perm |
访问权限 (BT_GATT_PERM_READ , WRITE , ENCRYPT 等) |
read_func |
读回调函数 (NULL 表示直接访问) |
write_func |
写回调函数 (NULL 表示直接写入) |
user_data |
指向特征值的指针 |
3) 常用描述符宏
宏 | 功能 | 参数 |
---|---|---|
BT_GATT_CCC(cfg_changed, perm) |
客户端特征配置描述符 | cfg_changed : CCC变更回调 perm : 访问权限 |
BT_GATT_CUD(description, perm) |
特征用户描述描述符 | description : 描述字符串 perm : 访问权限 |
BT_GATT_CPF(format) |
特征呈现格式描述符 | format : 数据格式结构体 |
BT_GATT_DESCRIPTOR(uuid, perm, read, write, value) |
通用描述符 | 完整参数控制 |
2 使用示例
2.1 基本服务定义
cpp
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
// 定义自定义UUID
#define BT_UUID_CUSTOM_SERVICE_VAL \
BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0)
#define BT_UUID_CUSTOM_CHAR_VAL \
BT_UUID_128_ENCODE(0x87654321, 0x4321, 0x8765, 0x4321, 0x0fedcba98765)
static const struct bt_uuid_128 custom_service_uuid =
BT_UUID_INIT_128(BT_UUID_CUSTOM_SERVICE_VAL);
static const struct bt_uuid_128 custom_char_uuid =
BT_UUID_INIT_128(BT_UUID_CUSTOM_CHAR_VAL);
// 特征值变量
static uint8_t char_value = 0;
// 读回调函数
static ssize_t read_char_value(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
const uint8_t *value = attr->user_data;
return bt_gatt_attr_read(conn, attr, buf, len, offset, value, sizeof(*value));
}
// 写回调函数
static ssize_t write_char_value(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset,
uint8_t flags)
{
uint8_t *value = attr->user_data;
if (offset + len > sizeof(*value)) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
memcpy(value + offset, buf, len);
return len;
}
// 定义服务
BT_GATT_SERVICE_DEFINE(custom_service,
// 服务声明
BT_GATT_PRIMARY_SERVICE(&custom_service_uuid),
// 特征声明
BT_GATT_CHARACTERISTIC(&custom_char_uuid.uuid,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_char_value, write_char_value, &char_value),
// 客户端特征配置描述符(CCCD)
BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
// 特征用户描述描述符
BT_GATT_CUD("Custom Characteristic", BT_GATT_PERM_READ)
);
2.2 带通知功能的温度服务
cpp
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
// 使用标准UUID
static struct bt_uuid_16 temp_service_uuid = BT_UUID_INIT_16(0x1809); // 健康温度计
static struct bt_uuid_16 temp_meas_uuid = BT_UUID_INIT_16(0x2A1C); // 温度测量
// 温度数据
static int32_t temperature = 2500; // 25.00°C
static bool notify_enabled = false;
// CCCD变更回调
static void temp_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
notify_enabled = (value == BT_GATT_CCC_NOTIFY);
}
// 温度读取回调
static ssize_t read_temperature(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
const int32_t *value = attr->user_data;
return bt_gatt_attr_read(conn, attr, buf, len, offset, value, sizeof(*value));
}
// 温度服务定义
BT_GATT_SERVICE_DEFINE(temp_service,
BT_GATT_PRIMARY_SERVICE(&temp_service_uuid),
// 温度测量特征
BT_GATT_CHARACTERISTIC(&temp_meas_uuid.uuid,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
read_temperature, NULL, &temperature),
// CCCD配置
BT_GATT_CCC(temp_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)
);
// 更新温度值并通知
void update_temperature(int32_t new_temp)
{
temperature = new_temp;
if (notify_enabled) {
bt_gatt_notify(NULL, &temp_service.attrs[2], &temperature, sizeof(temperature));
}
}
2.3 多特征复合服务
cpp
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
// 定义UUID
static struct bt_uuid_128 multi_service_uuid =
BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x00000001,0x0000,0x1000,0x8000,0x00805F9B34FB));
static struct bt_uuid_128 char1_uuid =
BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x00000002,0x0000,0x1000,0x8000,0x00805F9B34FB));
static struct bt_uuid_128 char2_uuid =
BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x00000003,0x0000,0x1000,0x8000,0x00805F9B34FB));
// 特征值
static uint8_t char1_value = 0;
static uint16_t char2_value = 0;
// 服务定义
BT_GATT_SERVICE_DEFINE(multi_feature_service,
BT_GATT_PRIMARY_SERVICE(&multi_service_uuid),
// 特征1
BT_GATT_CHARACTERISTIC(&char1_uuid.uuid,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
NULL, NULL, &char1_value),
// 特征2
BT_GATT_CHARACTERISTIC(&char2_uuid.uuid,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
NULL, NULL, &char2_value),
BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
// 特征描述符
BT_GATT_DESCRIPTOR(&bt_uuid_description,
BT_GATT_PERM_READ,
bt_gatt_attr_read_description,
NULL, "Feature 2"),
// 自定义描述符
BT_GATT_DESCRIPTOR(BT_UUID_CUSTOM_DESCRIPTOR,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
custom_descriptor_read, custom_descriptor_write,
NULL)
);
3 高级用法介绍
3.1 动态特征值更新
cpp
// 在服务定义中
static uint8_t dynamic_value[20];
BT_GATT_CHARACTERISTIC(&dynamic_char_uuid,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
read_dynamic_value, NULL, NULL)
// 读回调函数
static ssize_t read_dynamic_value(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
// 动态生成值
generate_dynamic_data(dynamic_value, sizeof(dynamic_value));
return bt_gatt_attr_read(conn, attr, buf, len, offset,
dynamic_value, sizeof(dynamic_value));
}
// 通知所有连接的客户端
void notify_dynamic_value(void)
{
bt_gatt_notify(NULL, &service.attrs[2], dynamic_value, sizeof(dynamic_value));
}
3.2 安全连接处理
cpp
BT_GATT_SERVICE_DEFINE(secure_service,
BT_GATT_PRIMARY_SERVICE(&secure_service_uuid),
// 需要加密的特征
BT_GATT_CHARACTERISTIC(&secure_char_uuid,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT,
read_secure_data, write_secure_data, &secure_data),
// 需要MITM保护的描述符
BT_GATT_DESCRIPTOR(&bt_uuid_secure_desc,
BT_GATT_PERM_READ_AUTHEN | BT_GATT_PERM_WRITE_AUTHEN,
read_auth_desc, write_auth_desc, NULL)
);
// 写回调中的安全验证
static ssize_t write_secure_data(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset,
uint8_t flags)
{
// 验证连接安全级别
if (bt_conn_get_security(conn) < BT_SECURITY_L3) {
return BT_GATT_ERR(BT_ATT_ERR_AUTHENTICATION);
}
// 处理数据写入
memcpy(attr->user_data, buf, len);
return len;
}
3.3 服务发现响应
cpp
// 在服务定义中添加包含服务
static struct bt_uuid_16 included_service_uuid = BT_UUID_INIT_16(0x180A);
BT_GATT_SERVICE_DEFINE(main_service,
BT_GATT_PRIMARY_SERVICE(&main_service_uuid),
// 包含设备信息服务
BT_GATT_INCLUDE_SERVICE(&device_info_service.attrs),
// 主服务特征...
);
// 设备信息服务定义
BT_GATT_SERVICE_DEFINE(device_info_service,
BT_GATT_PRIMARY_SERVICE(&bt_uuid_device_information),
BT_GATT_CHARACTERISTIC(&bt_uuid_model_number,
BT_GATT_CHRC_READ,
BT_GATT_PERM_READ,
read_model_number, NULL, "Zephyr-ModelX"),
BT_GATT_CHARACTERISTIC(&bt_uuid_firmware_rev,
BT_GATT_CHRC_READ,
BT_GATT_PERM_READ,
read_fw_version, NULL, "1.2.3")
);
3.4 自定义属性
cpp
// 自定义属性结构
static struct bt_gatt_attr custom_attrs[] = {
BT_GATT_PRIMARY_SERVICE(&custom_service_uuid),
BT_GATT_CHARACTERISTIC(&custom_char_uuid, ...),
BT_GATT_DESCRIPTOR(...)
};
// 使用自定义属性定义服务
BT_GATT_SERVICE_DEFINE(custom_attr_service,
custom_attrs[0],
custom_attrs[1],
custom_attrs[2]
);
// 动态添加特征
int add_dynamic_characteristic(void)
{
static struct bt_uuid_128 dyn_char_uuid = BT_UUID_INIT_128(...);
static uint8_t dyn_value = 0;
struct bt_gatt_attr dyn_attr = BT_GATT_CHARACTERISTIC(&dyn_char_uuid, ...);
// 动态添加属性到服务
return bt_gatt_service_add_attr(&custom_attr_service, &dyn_attr);
}
3.5 参考范例
3.5.1 服务组织结构
cpp
// 推荐的服务文件结构
/* custom_service.h */
#pragma once
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#ifdef __cplusplus
extern "C" {
#endif
// 服务UUID声明
extern const struct bt_uuid_128 custom_service_uuid;
extern const struct bt_uuid_128 custom_char1_uuid;
extern const struct bt_uuid_128 custom_char2_uuid;
// 服务API
void custom_service_init(void);
void custom_service_update_value(uint8_t new_value);
#ifdef __cplusplus
}
#endif
/* custom_service.c */
#include "custom_service.h"
// UUID定义
const struct bt_uuid_128 custom_service_uuid = ...;
const struct bt_uuid_128 custom_char1_uuid = ...;
const struct bt_uuid_128 custom_char2_uuid = ...;
// 特征值
static uint8_t char1_value = 0;
static uint16_t char2_value = 0;
// 服务实现
BT_GATT_SERVICE_DEFINE(custom_svc,
BT_GATT_PRIMARY_SERVICE(&custom_service_uuid),
// 特征定义...
);
void custom_service_init(void)
{
// 初始化逻辑
}
void custom_service_update_value(uint8_t new_value)
{
char1_value = new_value;
// 通知逻辑...
}
3.5.2 功耗优化
cpp
// 在服务定义中使用通知抑制
static bool notifications_active = false;
BT_GATT_SERVICE_DEFINE(power_optimized_service,
BT_GATT_PRIMARY_SERVICE(&power_service_uuid),
BT_GATT_CHARACTERISTIC(&power_char_uuid,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
read_power_value, NULL, &power_value),
BT_GATT_CCC(power_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)
);
// CCC回调
static void power_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
notifications_active = (value & BT_GATT_CCC_NOTIFY);
if (notifications_active) {
// 启用高频率更新
start_high_freq_sampling();
} else {
// 禁用高频率更新
stop_high_freq_sampling();
}
}
// 数据更新函数
void update_power_value(uint16_t value)
{
if (!notifications_active) return;
power_value = value;
bt_gatt_notify(NULL, &power_optimized_service.attrs[2], &power_value, sizeof(power_value));
}
3.5.3 错误处理
cpp
// 特征读回调中的错误处理
static ssize_t read_with_validation(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
if (!is_data_valid()) {
return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
}
if (offset > MAX_VALID_OFFSET) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
return bt_gatt_attr_read(conn, attr, buf, len, offset,
attr->user_data, DATA_SIZE);
}
// 写回调中的错误处理
static ssize_t write_with_validation(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset,
uint8_t flags)
{
if (offset + len > MAX_DATA_SIZE) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
if (!validate_input(buf, len)) {
return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
}
memcpy(attr->user_data + offset, buf, len);
return len;
}
4 常见问题解决
1) 服务未显示
检查配置 :确保
prj.conf
包含CONFIG_BT=y
和CONFIG_BT_GATT=y
验证UUID:使用蓝牙调试工具检查服务UUID是否正确
检查权限:确保客户端有足够的安全权限
2) 特征不可写
验证属性 :特征必须包含
BT_GATT_CHRC_WRITE
属性检查权限 :特征权限必须包含
BT_GATT_PERM_WRITE
安全级别 :可能需要加密连接 (添加
BT_GATT_PERM_WRITE_ENCRYPT
)
3) 通知不工作
启用通知:客户端必须写入CCCD启用通知
检查属性 :特征必须包含
BT_GATT_CHRC_NOTIFY
连接验证:确保有活动的蓝牙连接
资源限制 :增加
CONFIG_BT_CONN_TX_MAX
值
4) 内存不足
增加堆大小 :在
prj.conf
中设置CONFIG_BT_BUF_CMD_TX_COUNT=10
限制连接数 :设置
CONFIG_BT_MAX_CONN=3
(默认值)优化特征:减少特征数量和大小
5 配置选项 (prj.conf)
配置的参数如下:
bash
# 基础蓝牙配置
CONFIG_BT=y
CONFIG_BT_GATT=y
# 服务配置
CONFIG_BT_DEVICE_NAME="My GATT Device"
CONFIG_BT_DEVICE_APPEARANCE=384 # 通用传感器
# 安全配置
CONFIG_BT_SMP=y
CONFIG_BT_PRIVACY=y
CONFIG_BT_GATT_AUTO_SEC_REQ=y
# 性能优化
CONFIG_BT_BUF_ACL_RX_SIZE=255
CONFIG_BT_BUF_ACL_TX_SIZE=255
CONFIG_BT_L2CAP_TX_MTU=247
CONFIG_BT_ATT_PREPARE_COUNT=5
# 调试支持
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_DEBUG_GATT=y
