目录
概述
BT_GATT_CHARACTERISTIC
是 Zephyr 蓝牙协议栈中用于定义 GATT 特征的核心宏。它是构建 GATT 服务的基础组件,用于实现设备间数据交换的关键特性。该宏是构建蓝牙 GATT 服务的基石,通过合理设计特征属性、权限和回调函数,可以实现从简单的数据传输到复杂的设备控制等各种应用场景。
1 BT_GATT_CHARACTERISTIC
介绍
1.1 宏定义原型
#define BT_GATT_CHARACTERISTIC(_uuid, _props, _perm, _read, _write, _value)
宏原型(在zephyr的bluetooth/gatt.h中):
#define BT_GATT_CHARACTERISTIC(_uuid, _props, _perm, _read, _write, _user_data)
参数解释:
_uuid:
特征的UUID,可以是16位、32位或128位的UUID常量(使用BT_UUID_DECLARE_16/32/128等宏定义)
_props:
特征的属性,使用位掩码表示,如BT_GATT_CHRC_READ, BT_GATT_CHRC_WRITE, BT_GATT_CHRC_NOTIFY等(可组合)
_perm:
访问特征值所需的权限,如BT_GATT_PERM_READ, BT_GATT_PERM_WRITE等(可组合)
_read:
读回调函数,当客户端尝试读取特征值时调用。如果为NULL,则直接返回特征值(_user_data指向的值)
_write:
写回调函数,当客户端尝试写入特征值时调用。如果为NULL,则直接写入特征值(_user_data指向的位置)
_user_data:
指向特征值的指针。当读回调或写回调为NULL时,协议栈会自动处理这个指针指向的数据。
注意:该宏用于在BT_GATT_SERVICE_DEFINE内部定义特征。
使用方法步骤:
1. 定义特征的UUID。
2. 定义特征值变量(如果需要存储特征值)。
3. 根据需要实现读回调函数和写回调函数(如果不需要自定义处理,可以设为NULL)。
4. 在BT_GATT_SERVICE_DEFINE宏中使用BT_GATT_CHARACTERISTIC添加特征。
参数介绍:
参数 | 类型 | 说明 |
---|---|---|
_uuid | const struct bt_uuid * | 特征UUID指针 |
_props | uint8_t (位掩码) | 特征属性标志 |
_perm | uint8_t (位掩码) | 访问权限标志 |
_read | bt_gatt_attr_read_func_t | 读回调函数 |
_write | bt_gatt_attr_write_func_t | 写回调函数 |
_value | void * | 特征值指针 |
1.2 特征属性 (_props
)
属性标志 | 值 | 说明 |
---|---|---|
BT_GATT_CHRC_READ | BIT(0) | 可读 |
BT_GATT_CHRC_WRITE | BIT(1) | 可写(无响应) |
BT_GATT_CHRC_WRITE_WITHOUT_RESP | BIT(1) | 同 WRITE |
BT_GATT_CHRC_WRITE_WITH_RESP | BIT(2) | 需响应的写入 |
BT_GATT_CHRC_NOTIFY | BIT(3) | 支持通知 |
BT_GATT_CHRC_INDICATE | BIT(4) | 支持指示 |
BT_GATT_CHRC_AUTH | BIT(5) | 需要认证 |
BT_GATT_CHRC_EXT_PROP | BIT(6) | 扩展属性 |
1.3 访问权限 (_perm
)
权限标志 | 说明 |
---|---|
BT_GATT_PERM_READ | 允许读取 |
BT_GATT_PERM_WRITE | 允许写入 |
BT_GATT_PERM_READ_ENCRYPT | 需要加密读取 |
BT_GATT_PERM_WRITE_ENCRYPT | 需要加密写入 |
BT_GATT_PERM_READ_AUTHEN | 需要认证读取 |
BT_GATT_PERM_WRITE_AUTHEN | 需要认证写入 |
BT_GATT_PERM_PREPARE_WRITE | 允许预写操作 |
2 完整使用示例
2.1 基础特征定义
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
// 特征UUID定义
static struct bt_uuid_16 temp_char_uuid = BT_UUID_INIT_16(0x2A1C); // 温度测量
// 特征值
static int32_t temperature = 2500; // 25.00°C
// 读回调
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));
}
// 写回调
static ssize_t write_temperature(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset,
uint8_t flags)
{
int32_t *value = attr->user_data;
if (offset + len > sizeof(*value)) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
memcpy((uint8_t *)value + offset, buf, len);
// 值更新处理
if (offset + len == sizeof(*value)) {
printk("New temperature: %d.%02dC\n",
*value / 100, abs(*value % 100));
}
return len;
}
// 服务定义
BT_GATT_SERVICE_DEFINE(temp_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_TEMPERATURE),
BT_GATT_CHARACTERISTIC(&temp_char_uuid.uuid,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_temperature, write_temperature, &temperature)
);
2.2 通知型特征
static bool notify_enabled = false;
// CCCD回调
static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
notify_enabled = (value == BT_GATT_CCC_NOTIFY);
}
// 服务定义
BT_GATT_SERVICE_DEFINE(notify_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_HTS),
BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
read_temperature, NULL, &temperature),
BT_GATT_CCC(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, ¬ify_service.attrs[1],
&temperature, sizeof(temperature));
}
}
2.3 指示型特征(带确认)
static bool indicate_enabled = false;
// CCCD回调
static void indicate_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
indicate_enabled = (value & BT_GATT_CCC_INDICATE);
}
// 指示参数
struct indicate_params {
struct bt_gatt_indicate_params params;
uint8_t value;
};
// 指示回调
static void indicate_cb(struct bt_conn *conn,
struct bt_gatt_indicate_params *params,
uint8_t err)
{
struct indicate_params *iparams = CONTAINER_OF(params, struct indicate_params, params);
printk("Indication %s\n", err ? "failed" : "confirmed");
k_free(iparams);
}
// 发送指示
void send_critical_alert(uint8_t alert_level)
{
if (!indicate_enabled) return;
struct indicate_params *params = k_malloc(sizeof(*params));
params->value = alert_level;
params->params.attr = &alert_service.attrs[1];
params->params.data = ¶ms->value;
params->params.len = sizeof(params->value);
params->params.func = indicate_cb;
bt_gatt_indicate(NULL, ¶ms->params);
}
// 服务定义
BT_GATT_SERVICE_DEFINE(alert_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_ALERT_NOTIFICATION),
BT_GATT_CHARACTERISTIC(BT_UUID_ALERT_LEVEL,
BT_GATT_CHRC_INDICATE,
BT_GATT_PERM_NONE,
NULL, NULL, NULL),
BT_GATT_CCC(indicate_ccc_cfg_changed,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)
);
3 高级用法
3.1 动态特征值
// 动态数据生成
static ssize_t read_dynamic_data(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
uint32_t timestamp = k_uptime_get_32();
float value = read_sensor_value();
uint8_t data[8];
memcpy(data, ×tamp, 4);
memcpy(data + 4, &value, 4);
return bt_gatt_attr_read(conn, attr, buf, len, offset, data, sizeof(data));
}
BT_GATT_SERVICE_DEFINE(dynamic_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_ENVIRONMENTAL_SENSING),
BT_GATT_CHARACTERISTIC(BT_UUID_ES_MEASUREMENT,
BT_GATT_CHRC_READ,
BT_GATT_PERM_READ,
read_dynamic_data, NULL, NULL)
);
3.2 安全特征
// 需要认证的读回调
static ssize_t read_secure_data(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
// 验证安全级别
if (bt_conn_get_security(conn) < BT_SECURITY_L4) {
return BT_GATT_ERR(BT_ATT_ERR_AUTHENTICATION);
}
return bt_gatt_attr_read(conn, attr, buf, len, offset,
attr->user_data, 16);
}
BT_GATT_SERVICE_DEFINE(secure_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_DEVICE_INFORMATION),
BT_GATT_CHARACTERISTIC(BT_UUID_SERIAL_NUMBER_STRING,
BT_GATT_CHRC_READ | BT_GATT_CHRC_AUTH,
BT_GATT_PERM_READ_AUTHEN,
read_secure_data, NULL, "S/N-12345")
);
3.3 长特征值
#define MAX_DATA_SIZE 512
static uint8_t large_buffer[MAX_DATA_SIZE];
// 分块读回调
static ssize_t read_large_data(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
return bt_gatt_attr_read(conn, attr, buf, len, offset,
large_buffer, MAX_DATA_SIZE);
}
// 预写操作
static ssize_t write_large_data(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset,
uint8_t flags)
{
// 验证偏移量
if (offset >= MAX_DATA_SIZE) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
// 检查缓冲区边界
if (offset + len > MAX_DATA_SIZE) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
}
// 执行写入
memcpy(large_buffer + offset, buf, len);
// 如果是最后一个分块
if (flags & BT_GATT_WRITE_FLAG_EXECUTE) {
process_complete_data();
}
return len;
}
BT_GATT_SERVICE_DEFINE(large_data_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_USER_DATA),
BT_GATT_CHARACTERISTIC(BT_UUID_LARGE_DATA,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE |
BT_GATT_PERM_PREPARE_WRITE,
read_large_data, write_large_data, NULL)
);
4 特征描述符集成
4.1 完整特征定义示例
BT_GATT_SERVICE_DEFINE(complete_char_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_BATTERY_SERVICE),
// 电池电平特征
BT_GATT_CHARACTERISTIC(BT_UUID_BATTERY_LEVEL,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
read_battery_level, NULL, NULL),
// CCCD
BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
// 特征用户描述
BT_GATT_CUD("Battery Level Percentage", BT_GATT_PERM_READ),
// 特征格式描述符
BT_GATT_CPF((struct bt_gatt_cpf) {
.format = BT_GATT_CPF_FORMAT_UINT8,
.exponent = 0,
.unit = BT_GATT_UNIT_PERCENTAGE,
.name_space = 1,
.description = 0
})
);
5 应用案例
5.1 特征设计原则
// 优化特征定义
BT_GATT_CHARACTERISTIC(
BT_UUID_OPTIMIZED_CHAR,
// 最小化属性标志
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
// 精确权限控制
BT_GATT_PERM_READ_ENCRYPT,
// 高效读回调
[](struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset) -> ssize_t {
return bt_gatt_attr_read(conn, attr, buf, len, offset,
fixed_value, sizeof(fixed_value));
},
// 无写操作
NULL,
// 静态数据
fixed_value
)
5.2 回调函数优化
// 高效读回调实现
static ssize_t optimized_read(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
// 直接返回静态数据
static const uint8_t fixed_data[] = {0x01, 0x02, 0x03};
return bt_gatt_attr_read(conn, attr, buf, len, offset,
fixed_data, sizeof(fixed_data));
}
// 高效写回调
static ssize_t optimized_write(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, uint16_t len, uint16_t offset,
uint8_t flags)
{
// 仅接受完整写入
if (offset != 0) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
if (len != 1) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
}
// 处理单字节命令
handle_command(*(const uint8_t *)buf);
return len;
}
5.3 内存高效使用
// 共享特征值缓冲区
static uint8_t shared_buffer[32];
BT_GATT_SERVICE_DEFINE(multi_char_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_CUSTOM_SERVICE),
// 特征1
BT_GATT_CHARACTERISTIC(BT_UUID_CHAR1,
BT_GATT_CHRC_READ,
BT_GATT_PERM_READ,
read_shared, NULL, &shared_buffer[0]),
// 特征2
BT_GATT_CHARACTERISTIC(BT_UUID_CHAR2,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_shared, write_shared, &shared_buffer[16])
);
6 调试与问题排查
6.1 常见错误处理
// 健壮的回调函数
static ssize_t safe_read(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
// 验证连接
if (!conn || bt_conn_get_state(conn) != BT_CONN_CONNECTED) {
return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
}
// 验证偏移量
if (offset >= MAX_DATA_SIZE) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
// 处理读取
uint16_t read_len = MIN(len, MAX_DATA_SIZE - offset);
return bt_gatt_attr_read(conn, attr, buf, len, offset,
data_buffer + offset, read_len);
}
6.2 启用 GATT 调试
# prj.conf 配置
CONFIG_BT_DEBUG_GATT=y
CONFIG_BT_DEBUG_ATT=y
6.3 配置优化
# GATT 相关配置
CONFIG_BT_GATT_ENABLE=y
CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_GATT_CLIENT=y
# 特征值缓存
CONFIG_BT_SETTINGS=y
CONFIG_BT_GATT_SERVICE_CHANGED=y
# 性能优化
CONFIG_BT_ATT_PREPARE_COUNT=10
CONFIG_BT_L2CAP_TX_MTU=247
7 典型应用场景
7.1 环境传感器
BT_GATT_SERVICE_DEFINE(env_sensor_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_ENVIRONMENTAL_SENSING),
// 温度
BT_GATT_CHARACTERISTIC(BT_UUID_TEMPERATURE,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
read_temp, NULL, NULL),
BT_GATT_CCC(temp_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
BT_GATT_CUD("Temperature (Celsius)", BT_GATT_PERM_READ),
// 湿度
BT_GATT_CHARACTERISTIC(BT_UUID_HUMIDITY,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
read_humidity, NULL, NULL),
BT_GATT_CCC(humidity_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
BT_GATT_CUD("Relative Humidity (%)", BT_GATT_PERM_READ)
);
7.2 设备控制服务
BT_GATT_SERVICE_DEFINE(device_control_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_CUSTOM_CONTROL),
// 命令特征
BT_GATT_CHARACTERISTIC(BT_UUID_CONTROL_COMMAND,
BT_GATT_CHRC_WRITE_WITH_RESP,
BT_GATT_PERM_WRITE_ENCRYPT,
NULL, handle_command, NULL),
// 状态特征
BT_GATT_CHARACTERISTIC(BT_UUID_DEVICE_STATUS,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ_ENCRYPT,
read_status, NULL, &device_status),
BT_GATT_CCC(status_ccc_changed,
BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT)
);