Zephyr OS 下的 bt_gatt_read函数的功能和应用

目录

概述

1 GATT 基本概念

1.1 GATT 的介绍

1.2 GATT 的角色

 1.3 核心组件

 1.3.1 层级结构

1.3.2 关键组件说明

1.4 客户端操作

2 bt_gatt_read函数介绍

2.1 函数原型与参数

2.2 读取模式

2.2.1 单句柄读取

2.2.2 多句柄批量读取

3 bt_gatt_read的用法

3.1 长数据分片读取

3.2 带认证的读取

4 错误处理

4.1 常见ATT错误码

 4.2 错误处理示例

5 性能优化

5.1 MTU协商优化

5.2 批量读取优化

6 资源管理注意事项

6.1 参数生命周期管理

6.2 连接状态检查:

6.3 取消读取操作


概述

本文详细介绍了蓝牙低功耗(BLE)中的GATT协议及其关键组件,重点分析了Zephyr协议栈中的bt_gatt_read函数。文章首先阐述了GATT的基本概念及其在BLE通信中的核心作用,包括服务(Service)、特性(Characteristic)和描述符(Descriptor)的层级结构。然后深入解析了bt_gatt_read函数的原型参数、读取模式(单句柄/多句柄)及具体实现方法,并提供了长数据分片读取和安全读取的代码示例。此外,文章还介绍了常见的ATT错误处理方案,提出了通过MTU协商和批量读取优化性能的方法,最后强调了参数生命周期管理和连接状态检查等资源管理注意事项。全文通过丰富的代码示例和结构化的技术说明,为BLE应用开发提供了实用参考。

1 GATT 基本概念

1.1 GATT 的介绍

GATT (Generic Attribute Profile) 是 Bluetooth Low Energy (BLE) 的核心协议,定义了 数据通信的标准框架,使BLE设备能够通过 服务(Services) 和 特征(Characteristics) 交换数据。

1.2 GATT 的角色

角色说明典型设备
GATT 服务器(Server)存储并提供数据(如传感器数据)心率带、温度计
GATT 客户端(Client)读取或写入服务器数据手机、中央设备

 1.3 核心组件

 1.3.1 层级结构

GATT Profile
├── Services (服务)
│   ├── Characteristics (特性)
│   │   ├── Value (值)
│   │   ├── Descriptors (描述符)
│   │   │   └── Client Characteristic Configuration (CCC)
│   │   └── Properties (属性)
│   └── Includes (包含服务)
└── Attributes (属性)

1.3.2 关键组件说明

组件说明示例UUID
服务(Service)功能逻辑集合0x180A (设备信息服务)
特性(Characteristic)服务中的数据项0x2A29 (厂商名称)
描述符(Descriptor)特性的元数据0x2902 (CCC描述符)
属性(Attribute)数据库基本单元由协议栈管理

1.4 客户端操作

操作函数(Zephyr示例)说明
发现服务bt_gatt_discover()扫描远程设备的GATT表
读取特征值bt_gatt_read()读取数据(如电池电量)
写入特征值bt_gatt_write()发送命令或配置
启用通知bt_gatt_subscribe()订阅实时数据(如心率)

2 bt_gatt_read函数介绍

bt_gatt_read 是 Zephyr BLE 协议栈中用于从远程设备读取 GATT 特性值或描述符的核心函数。下面我将从多个技术维度进行详细说明:

2.1 函数原型与参数

int bt_gatt_read(
    struct bt_conn *conn,
    struct bt_gatt_read_params *params
);

参数说明:

  • conn:已建立的BLE连接句柄

  • params:读取参数结构体,包含以下关键字段:

struct bt_gatt_read_params {
    uint16_t handle_count;  // 要读取的句柄数量
    union {
        struct {
            uint16_t handle;  // 单个句柄读取时的目标句柄
            uint16_t offset;  // 读取偏移量
        } single;
        struct {
            uint16_t *handles;  // 多个句柄数组
            uint16_t *offsets;  // 对应的偏移量数组
        } multiple;
    };
    void (*func)(struct bt_conn *conn, uint8_t err,
                struct bt_gatt_read_params *params,
                const void *data, uint16_t length);
};

2.2 读取模式

2.2.1 单句柄读取

static void read_cb(struct bt_conn *conn, uint8_t err,
                   struct bt_gatt_read_params *params,
                   const void *data, uint16_t length)
{
    if (err) {
        printk("Read failed (err 0x%02x)\n", err);
        return;
    }
    
    printk("Read %u bytes: ", length);
    for (uint16_t i = 0; i < length; i++) {
        printk("%02X ", ((const uint8_t *)data)[i]);
    }
    printk("\n");
}

void read_characteristic(struct bt_conn *conn, uint16_t handle)
{
    static struct bt_gatt_read_params params = {
        .func = read_cb,
        .handle_count = 1,
        .single.handle = handle,
        .single.offset = 0,  // 从起始位置读取
    };
    
    int err = bt_gatt_read(conn, &params);
    if (err) {
        printk("Read request failed (err %d)\n", err);
    }
}

2.2.2 多句柄批量读取

static uint16_t handles[] = {0x0003, 0x0005, 0x0007};
static uint16_t offsets[] = {0, 0, 0};  // 每个句柄的偏移量

static void multi_read_cb(struct bt_conn *conn, uint8_t err,
                         struct bt_gatt_read_params *params,
                         const void *data, uint16_t length)
{
    static uint8_t read_count = 0;
    
    if (err) {
        printk("Multi-read error (err 0x%02x)\n", err);
        return;
    }
    
    printk("Read chunk %d: len=%u\n", ++read_count, length);
    
    if (length == 0) {
        printk("Multi-read complete\n");
        read_count = 0;
    }
}

void read_multiple_characteristics(struct bt_conn *conn)
{
    static struct bt_gatt_read_params params = {
        .func = multi_read_cb,
        .handle_count = ARRAY_SIZE(handles),
        .multiple.handles = handles,
        .multiple.offsets = offsets,
    };
    
    bt_gatt_read(conn, &params);
}

bt_gatt_read的用法

3.1 长数据分片读取

static uint16_t current_offset = 0;
static uint8_t buffer[256];

static void long_read_cb(struct bt_conn *conn, uint8_t err,
                        struct bt_gatt_read_params *params,
                        const void *data, uint16_t length)
{
    if (err) {
        printk("Long read error (err 0x%02x)\n", err);
        return;
    }
    
    if (length > 0) {
        // 存储接收到的数据
        memcpy(buffer + current_offset, data, length);
        current_offset += length;
        
        // 继续读取下一片段
        params->single.offset = current_offset;
        bt_gatt_read(conn, params);
    } else {
        printk("Long read complete, total %u bytes\n", current_offset);
        current_offset = 0;
    }
}

void read_long_characteristic(struct bt_conn *conn, uint16_t handle)
{
    static struct bt_gatt_read_params params = {
        .func = long_read_cb,
        .handle_count = 1,
        .single.handle = handle,
        .single.offset = 0,
    };
    
    current_offset = 0;
    bt_gatt_read(conn, &params);
}

3.2 带认证的读取

void read_with_security(struct bt_conn *conn, uint16_t handle)
{
    // 首先检查安全等级
    if (bt_conn_get_security(conn) < BT_SECURITY_L2) {
        // 提升安全等级
        int err = bt_conn_set_security(conn, BT_SECURITY_L2);
        if (err) {
            printk("Security upgrade failed (err %d)\n", err);
            return;
        }
        
        // 通常需要等待安全等级提升事件
        // 这里简化处理,实际应使用状态机
        k_sleep(K_MSEC(500));
    }
    
    // 执行安全读取
    struct bt_gatt_read_params params = {
        .func = read_cb,
        .handle_count = 1,
        .single.handle = handle,
    };
    
    bt_gatt_read(conn, &params);
}

4 错误处理

4.1 常见ATT错误码

错误码说明
0x00成功
0x01无效句柄
0x02读取不被允许
0x04无效PDU
0x05认证不足
0x06请求不支持
0x07无效偏移量
0x08授权不足

 4.2 错误处理示例

static void read_cb(struct bt_conn *conn, uint8_t err,
                   struct bt_gatt_read_params *params,
                   const void *data, uint16_t length)
{
    switch (err) {
    case 0x00:
        // 成功处理数据
        break;
    case 0x05:  // 认证不足
        printk("Authentication required\n");
        bt_conn_set_security(conn, BT_SECURITY_L2);
        break;
    case 0x02:  // 读取不被允许
        printk("Read not permitted\n");
        break;
    default:
        printk("Read error 0x%02x\n", err);
    }
}

5 性能优化

5.1 MTU协商优化

static void exchange_mtu_cb(struct bt_conn *conn, uint8_t err,
                          struct bt_gatt_exchange_params *params)
{
    if (err) {
        printk("MTU exchange failed (err 0x%02x)\n", err);
        return;
    }
    
    uint16_t mtu = bt_gatt_get_mtu(conn);
    printk("MTU updated to %u\n", mtu);
}

void optimize_read_size(struct bt_conn *conn)
{
    static struct bt_gatt_exchange_params params = {
        .func = exchange_mtu_cb
    };
    
    bt_gatt_exchange_mtu(conn, &params);
}

5.2 批量读取优化

void optimized_batch_read(struct bt_conn *conn)
{
    // 按物理邻近的句柄分组读取
    static uint16_t group1[] = {0x0003, 0x0004, 0x0005};
    static uint16_t group2[] = {0x0007, 0x0008};
    
    struct bt_gatt_read_params params[2] = {
        {.handle_count = ARRAY_SIZE(group1), .multiple.handles = group1},
        {.handle_count = ARRAY_SIZE(group2), .multiple.handles = group2},
    };
    
    bt_gatt_read(conn, &params[0]);
    bt_gatt_read(conn, &params[1]);
}

6 资源管理注意事项

6.1 参数生命周期管理

// 错误示例(栈变量风险)
void temp_read(struct bt_conn *conn, uint16_t handle) {
    struct bt_gatt_read_params temp = {...};
    bt_gatt_read(conn, &temp); // 回调时temp可能已失效
}

// 正确做法(静态或动态分配)
static struct bt_gatt_read_params persistent_params;
// 或
struct bt_gatt_read_params *params = k_malloc(sizeof(*params));

6.2 连接状态检查

if (bt_conn_get_state(conn) != BT_CONN_CONNECTED) {
    printk("Connection not ready\n");
    return;
}

6.3 取消读取操作

void cancel_read(struct bt_conn *conn) {
    bt_gatt_read_cancel(conn, &params);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值