目录
1 bt_gatt_indicate_params 数据结构介绍
概述
bt_gatt_indicate_params
是 Zephyr 蓝牙协议栈中用于配置 GATT 指示操作的核心数据结构。指示(Indication)是一种需要客户端确认的可靠数据传输机制,适用于关键数据更新和控制操作。该数据结构是 Zephyr 蓝牙协议栈中实现可靠数据传输的关键数据结构。通过合理管理其生命周期、正确处理回调函数并优化资源使用,可以构建高效可靠的蓝牙应用系统,特别适用于需要确认的关键操作和数据传输场景。
1 bt_gatt_indicate_params 数据结构介绍
1.1 结构定义
struct bt_gatt_indicate_params {
// 指示操作完成时的回调函数
void (*func)(struct bt_conn *conn,
struct bt_gatt_indicate_params *params,
uint8_t err);
// 指示参数析构函数(可选)
void (*destroy)(struct bt_gatt_indicate_params *params);
// 指向要指示的特征属性的指针
const struct bt_gatt_attr *attr;
// 要发送的数据指针
const void *data;
// 数据长度
uint16_t len;
// 内部使用字段(协议栈管理)
struct bt_att_req _req;
};
1.2 成员详解
1.2.1 func
- 指示完成回调
void (*func)(struct bt_conn *conn,
struct bt_gatt_indicate_params *params,
uint8_t err);
功能:当指示被确认或失败时调用的回调函数
参数:
conn
:目标连接句柄(发送指示的连接)
params
:指向当前指示参数结构的指针
err
:错误代码(0 表示成功)
典型实现:
static void indicate_cb(struct bt_conn *conn,
struct bt_gatt_indicate_params *params,
uint8_t err)
{
if (err) {
printk("Indication failed: 0x%02x\n", err);
} else {
printk("Indication confirmed\n");
}
// 释放资源
k_free(params);
}
1.2.2 destroy
- 析构函数(可选)
void (*destroy)(struct bt_gatt_indicate_params *params);
功能:指示完成后用于清理资源的回调
使用场景:
释放动态分配的内存
重置状态标志
示例:
static void params_destroy(struct bt_gatt_indicate_params *params)
{
struct custom_context *ctx = CONTAINER_OF(params, struct custom_context, params);
ctx->busy = false;
}
1.2.3 attr
- 目标特征属性
const struct bt_gatt_attr *attr;
-
功能:指向要指示的特征值属性
-
获取方式:
// 在服务定义中获取属性指针 BT_GATT_SERVICE_DEFINE(my_service, BT_GATT_PRIMARY_SERVICE(...), BT_GATT_CHARACTERISTIC(...) // 索引0: 服务声明, 索引1: 特征声明, 索引2: 特征值 ); // 使用索引获取特征值属性 const struct bt_gatt_attr *char_value_attr = &my_service.attrs[2];
1.2.4
data
- 数据指针
const void *data;
-
功能:指向要发送的数据缓冲区的指针
-
注意事项:
-
数据在发送期间必须保持有效
-
设置为
NULL
将使用特征当前值
-
-
典型用法:
uint8_t alert_data = ALERT_HIGH; params.data = &alert_data;
1.2.5
len
- 数据长度
uint16_t len;
功能:要发送的数据长度(字节)
注意事项:
设置为 0 且
data
为NULL
时将使用特征值长度不能超过 MTU 限制(
bt_gatt_get_mtu(conn) - 3
)
1.2.6 _req
- 内部请求结构
struct bt_att_req _req;
功能:协议栈内部使用的请求管理结构
开发者注意事项:
不要直接访问或修改此字段
由协议栈在指示生命周期内管理
2 使用流程
2.1 准备指示参数
// 定义自定义上下文结构
struct indicate_context {
struct bt_gatt_indicate_params params;
uint8_t data[20];
uint32_t timestamp;
};
// 创建指示参数
struct indicate_context *create_indication(struct bt_conn *conn,
const void *data, uint16_t len)
{
// 分配内存
struct indicate_context *ctx = k_malloc(sizeof(*ctx));
if (!ctx) return NULL;
// 设置参数
ctx->params.func = indicate_callback;
ctx->params.destroy = NULL; // 在回调中手动释放
ctx->params.attr = get_target_characteristic_attr();
ctx->params.data = data ? data : ctx->data;
ctx->params.len = len;
// 保存上下文数据
if (data && len > 0) {
memcpy(ctx->data, data, MIN(len, sizeof(ctx->data)));
}
ctx->timestamp = k_uptime_get_32();
return ctx;
}
2.2 发送指示
int send_critical_indication(struct bt_conn *conn,
const void *data, uint16_t len)
{
// 创建参数
struct indicate_context *ctx = create_indication(conn, data, len);
if (!ctx) return -ENOMEM;
// 发送指示
int err = bt_gatt_indicate(conn, &ctx->params);
if (err) {
printk("Indicate failed: %d\n", err);
k_free(ctx);
return err;
}
return 0;
}
2.3 处理回调
static void indicate_callback(struct bt_conn *conn,
struct bt_gatt_indicate_params *params,
uint8_t err)
{
// 获取上下文
struct indicate_context *ctx =
CONTAINER_OF(params, struct indicate_context, params);
// 处理结果
if (err) {
printk("Indication failed after %d ms (err 0x%02x)\n",
k_uptime_get_32() - ctx->timestamp, err);
} else {
printk("Indication confirmed in %d ms\n",
k_uptime_get_32() - ctx->timestamp);
}
// 清理资源
k_free(ctx);
}
3 高级用法
3.1 带重试机制的指示
#define MAX_RETRIES 3
struct retry_context {
struct bt_gatt_indicate_params params;
uint8_t data[32];
uint8_t retry_count;
};
static void retry_indicate(struct bt_conn *conn,
struct retry_context *ctx)
{
int err = bt_gatt_indicate(conn, &ctx->params);
if (err) {
printk("Retry failed: %d\n", err);
k_free(ctx);
}
}
static void retry_callback(struct bt_conn *conn,
struct bt_gatt_indicate_params *params,
uint8_t err)
{
struct retry_context *ctx =
CONTAINER_OF(params, struct retry_context, params);
if (err && ctx->retry_count < MAX_RETRIES) {
ctx->retry_count++;
printk("Retrying indication (%d/%d)\n",
ctx->retry_count, MAX_RETRIES);
k_work_submit(&retry_work, K_MSEC(100));
} else {
printk("Indication %s after %d attempts\n",
err ? "failed" : "succeeded", ctx->retry_count + 1);
k_free(ctx);
}
}
3.2 多连接指示广播
void broadcast_indication_to_all(const void *data, uint16_t len)
{
for (int i = 0; i < ARRAY_SIZE(connected_clients); i++) {
if (connected_clients[i].conn &&
connected_clients[i].indicate_enabled) {
struct indicate_context *ctx = create_indication(
connected_clients[i].conn, data, len);
if (!ctx) continue;
if (bt_gatt_indicate(connected_clients[i].conn, &ctx->params)) {
k_free(ctx);
}
}
}
}
3.3 指示超时处理
static void indication_timeout(struct k_work *work)
{
struct indicate_context *ctx =
CONTAINER_OF(work, struct indicate_context, timeout_work);
if (ctx->pending) {
ctx->params.func(ctx->conn, &ctx->params, BT_ATT_ERR_UNLIKELY);
}
}
void send_indication_with_timeout(struct bt_conn *conn,
const void *data, uint16_t len,
uint32_t timeout_ms)
{
struct indicate_context *ctx = create_indication(conn, data, len);
ctx->pending = true;
// 设置超时工作
k_work_init_delayable(&ctx->timeout_work, indication_timeout);
k_work_schedule(&ctx->timeout_work, K_MSEC(timeout_ms));
if (bt_gatt_indicate(conn, &ctx->params)) {
k_work_cancel_delayable(&ctx->timeout_work);
k_free(ctx);
}
}
static void indicate_callback(/*...*/)
{
struct indicate_context *ctx = /*...*/;
ctx->pending = false;
k_work_cancel_delayable(&ctx->timeout_work);
// ...其他处理...
}
3.4 生命周期管理
3.4.1 内存分配策略
策略 | 适用场景 | 示例 |
---|---|---|
静态分配 | 单连接、低频指示 | static struct bt_gatt_indicate_params params; |
动态分配 | 多连接、可变数据 | k_malloc(sizeof(struct indicate_context)); |
内存池 | 高频指示 | k_mem_slab_alloc(&indicate_slab, &ctx, K_NO_WAIT); |
3.4.2 资源清理方法
// 方法1:在回调中直接释放
static void callback_direct_free(struct bt_conn *conn,
struct bt_gatt_indicate_params *params,
uint8_t err)
{
k_free(params);
}
// 方法2:使用析构函数
static void params_destroy(struct bt_gatt_indicate_params *params)
{
struct custom_ctx *ctx = CONTAINER_OF(params, struct custom_ctx, params);
ctx->in_use = false;
}
// 方法3:内存池释放
static void mempool_callback(struct bt_conn *conn,
struct bt_gatt_indicate_params *params,
uint8_t err)
{
k_mem_slab_free(&indicate_slab, (void **)¶ms);
}
4 错误处理
4.1 常见错误代码
错误代码 | 值 | 说明 |
---|---|---|
BT_ATT_ERR_INSUFFICIENT_RESOURCES | 0x11 | 资源不足 |
BT_ATT_ERR_UNLIKELY | 0x0E | 一般错误 |
BT_ATT_ERR_WRITE_NOT_PERMITTED | 0x03 | 写权限不足 |
BT_ATT_ERR_INVALID_OFFSET | 0x07 | 无效偏移量 |
4.2 健壮的错误处理
static void robust_indicate_callback(struct bt_conn *conn,
struct bt_gatt_indicate_params *params,
uint8_t err)
{
struct indicate_context *ctx = /*...*/;
switch (err) {
case BT_ATT_ERR_INSUFFICIENT_RESOURCES:
printk("Error: Insufficient resources\n");
schedule_retry(ctx);
break;
case BT_ATT_ERR_UNLIKELY:
printk("Error: General failure\n");
log_error(ctx);
k_free(ctx);
break;
case 0: // 成功
handle_success(ctx);
k_free(ctx);
break;
default:
printk("Unhandled error: 0x%02x\n", err);
k_free(ctx);
break;
}
}