基于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这几个字段。我猜你可能在发现自定义服务时,没把参数切换到对应服务的特征发现模式,导致逻辑混乱。
正确的流程应该是:
- 初始发现所有主服务时,设置:
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; - 在服务发现回调里,找到一个服务后,立刻为这个服务单独配置特征发现参数:
// 把当前服务的结构体指针作为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
相关产品推荐
相关产品推荐

