You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Zephyr RTOS的nRF52840 DK BLE Central自定义UUID(服务、特征与CCCD)发现异常问题

基于Zephyr RTOS的nRF52840 DK BLE Central自定义UUID(服务、特征与CCCD)发现异常问题

兄弟,我之前在Zephyr上做BLE Central的时候也踩过几乎一模一样的坑!你说的自定义服务UUID重复输出的问题,大概率是上下文管理没做好——要么是全局变量的缓存逻辑出问题,要么是发现参数的复用/初始化有漏洞,我给你捋几个核心排查点和解决办法:


1. 先检查你的discover_phase状态管理逻辑

你用discover_phase来跟踪「服务→特征→CCCD」的发现流程,但如果切换阶段时没有及时更新或保存当前服务的UUID,就会导致所有服务都复用了同一个缓存值。比如你可能用了全局变量存当前服务UUID,但发现新服务时没及时覆盖,或者覆盖逻辑有问题。

解决建议:

  • 定义一个结构体数组/链表来存储每个服务的完整信息(UUID、handle区间、特征列表等),每发现一个新服务就立刻把它的UUID拷贝到专属的结构体里,别依赖全局变量临时缓存。
  • 示例结构体可以这么写:
    typedef struct {
        struct bt_uuid uuid;
        uint16_t start_handle;
        uint16_t end_handle;
        // 后续可以扩展特征、CCCD的存储字段
    } discovered_service_t;
    
    discovered_service_t services[BT_GATT_MAX_SERVICES];
    uint8_t service_count = 0;
    

2. 别乱用bt_gatt_discover_params,要按阶段重置参数

Zephyr的discover_params如果复用的话,必须在每个发现阶段(服务/特征/CCCD)正确重置参数,特别是uuid、func回调、user_data这几个字段。我猜你可能在发现自定义服务时,没把参数切换到对应服务的特征发现模式,导致逻辑混乱。

正确的流程应该是:

  1. 初始发现所有主服务时,设置:
    memset(&discover_params, 0, sizeof(discover_params));
    discover_params.type = BT_GATT_DISCOVER_PRIMARY;
    discover_params.uuid = NULL; // NULL表示发现所有主服务
    discover_params.func = service_discover_cb;
    discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
    discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
    
  2. 在服务发现回调里,找到一个服务后,立刻为这个服务单独配置特征发现参数:
    // 把当前服务的结构体指针作为user_data传递,确保回调能关联到所属服务
    params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
    params->uuid = NULL; // 发现该服务下所有特征
    params->func = characteristic_discover_cb;
    params->user_data = &services[service_count-1];
    // 限制发现范围在当前服务的handle区间内,避免跨服务发现
    params->start_handle = services[service_count-1].start_handle;
    params->end_handle = services[service_count-1].end_handle;
    

3. 打印UUID别自己瞎解析,用Zephyr自带的工具函数

你说打印三种格式的UUID,但自定义服务是128位的,如果手动判断UUID类型(16/32/128位)的逻辑出错,就会输出重复的错误值。直接用Zephyr提供的bt_uuid_to_str()函数就好,它能自动处理所有UUID类型。

正确的打印方式:

char uuid_str[BT_UUID_STR_LEN];
bt_uuid_to_str(&svc->uuid, uuid_str, sizeof(uuid_str));
printk("发现服务:%s(Handle范围:0x%04x-0x%04x)\n", 
       uuid_str, svc->start_handle, svc->end_handle);

4. 贴个简化版的正确发现流程代码参考

// 存储已发现服务的结构体
typedef struct {
    struct bt_uuid uuid;
    uint16_t start_handle;
    uint16_t end_handle;
} discovered_service_t;

discovered_service_t services[BT_GATT_MAX_SERVICES];
uint8_t service_count = 0;

// CCCD发现回调
static uint8_t cccd_discover_cb(struct bt_conn *conn,
                                const struct bt_gatt_attr *attr,
                                struct bt_gatt_discover_params *params)
{
    if (!attr) {
        return BT_GATT_ITER_STOP;
    }

    discovered_service_t *svc = params->user_data;
    char uuid_str[BT_UUID_STR_LEN];
    bt_uuid_to_str(&attr->uuid, uuid_str, sizeof(uuid_str));
    printk("服务%s的CCCD:%s\n",
           bt_uuid_to_str(&svc->uuid, uuid_str, sizeof(uuid_str)), uuid_str);
    return BT_GATT_ITER_CONTINUE;
}

// 特征发现回调
static uint8_t characteristic_discover_cb(struct bt_conn *conn,
                                          const struct bt_gatt_attr *attr,
                                          struct bt_gatt_discover_params *params)
{
    if (!attr) {
        // 特征发现完成,切换到CCCD发现
        params->type = BT_GATT_DISCOVER_DESCRIPTOR;
        params->uuid = BT_UUID_GATT_CCC;
        params->func = cccd_discover_cb;
        return bt_gatt_discover(conn, params);
    }

    discovered_service_t *svc = params->user_data;
    char uuid_str[BT_UUID_STR_LEN];
    bt_uuid_to_str(&attr->uuid, uuid_str, sizeof(uuid_str));
    printk("服务%s的特征:%s\n",
           bt_uuid_to_str(&svc->uuid, uuid_str, sizeof(uuid_str)), uuid_str);
    return BT_GATT_ITER_CONTINUE;
}

// 服务发现回调
static uint8_t service_discover_cb(struct bt_conn *conn,
                                   const struct bt_gatt_attr *attr,
                                   struct bt_gatt_discover_params *params)
{
    if (!attr) {
        // 所有服务发现完成
        return BT_GATT_ITER_STOP;
    }

    // 保存当前服务信息
    if (service_count < BT_GATT_MAX_SERVICES) {
        discovered_service_t *svc = &services[service_count++];
        bt_uuid_copy(&svc->uuid, &attr->uuid);
        svc->start_handle = attr->handle;
        svc->end_handle = attr->handle + attr->end_group - 1;

        // 打印服务UUID
        char uuid_str[BT_UUID_STR_LEN];
        bt_uuid_to_str(&svc->uuid, uuid_str, sizeof(uuid_str));
        printk("发现服务:%s(Handle:0x%04x-0x%04x)\n",
               uuid_str, svc->start_handle, svc->end_handle);

        // 初始化当前服务的特征发现参数
        params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
        params->uuid = NULL;
        params->func = characteristic_discover_cb;
        params->user_data = svc;
        params->start_handle = svc->start_handle;
        params->end_handle = svc->end_handle;

        int err = bt_gatt_discover(conn, params);
        if (err) {
            printk("特征发现失败:%d\n", err);
        }
    }

    return BT_GATT_ITER_CONTINUE;
}

// 连接成功回调
static void connected_cb(struct bt_conn *conn, uint8_t conn_err)
{
    const bt_addr_le_t *addr = bt_conn_get_dst(conn);

    if (conn_err) {
        bt_conn_unref(default_conn);
        default_conn = NULL;
        start_scan();
        return;
    }

    // 初始化服务发现参数
    memset(&discover_params, 0, sizeof(discover_params));
    discover_params.type = BT_GATT_DISCOVER_PRIMARY;
    discover_params.uuid = NULL;
    discover_params.func = service_discover_cb;
    discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
    discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;

    int err = bt_gatt_discover(conn, &discover_params);
    if (err) {
        printk("服务发现启动失败:%d\n", err);
        bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
    }
}

核心问题就是没有给每个服务单独分配上下文,导致UUID被复用或者没有正确存储,按照上面的思路调整后,应该就能解决重复输出的问题了。

备注:内容来源于stack exchange,提问作者Groux

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 10:44:32