我正在宣传一项主要的 BLE 服务。使用 nRF android 应用程序一切正常。另一个扫描设备想要找到广告、连接并获取 GATT 服务列表并列出它们的 UUID。扫描和连接工作正常。我确信我已连接到我的设备而不是其他东西。 Discovery 初始化为:
static struct bt_gatt_discover_params pars;
pars.func = gattDiscoveryCb;
pars.type = BT_GATT_DISCOVER_PRIMARY;
if (bt_gatt_discover(connection, &pars) != 0) {
LOG_ERR("BLE GATT discovery error");
return;
}
这里没有错误,发现开始。我理解(我希望)文档here,
params->type
表明我正在发现什么样的东西(必须是BT_GATT_DISCOVER_PRIMARY,因为发现的初始化方式),然后attr->user_data
是struct bt_gatt_service_val
,并且 uuid
其中有我现在要打印的内容。我在互联网上看到了很多示例,zephyr SDK 文件夹中也有很多示例(例如 ./zephyr/subsys/bluetooth/audio/vcp_vol_ctlr.c)用于执行相同的操作。该过程将 user_data 转换为正确的结构,然后使用 bt_uuid_to_str
从 uuid 生成一个字符串。听起来很简单:
uint8_t gattDiscoveryCb(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) {
char uuid_str[BT_UUID_STR_LEN];
if (params->type == BT_GATT_DISCOVER_PRIMARY) {
struct bt_gatt_service_val *service = (struct bt_gatt_service_val *)attr->user_data;
bt_uuid_to_str(service->uuid, uuid_str, sizeof(uuid_str));
LOG_INF("Primary service UUID: %s", uuid_str);
}
return BT_GATT_ITER_CONTINUE;
}
然而,在 bt_uuid_to_str 线上,整个事情爆炸了,它说:
00> [00:00:00.400,054] <err> os: ***** BUS FAULT *****
00> [00:00:00.400,054] <err> os: Precise data bus error
00> [00:00:00.400,085] <err> os: BFAR Address: 0xef8008f3[0m
00> [00:00:00.400,085] <err> os: r0/a1: 0xef8008f3 r1/a2: 0x20005448 r2/a3: 0x00000025
00> [00:00:00.400,115] <err> os: r3/a4: 0xef8008f3 r12/ip: 0x00000000 r14/lr: 0x00010e49
00> [00:00:00.400,115] <err> os: xpsr: 0x21000000
00> [00:00:00.400,146] <err> os: Faulting instruction address (r15/pc): 0x00013962
00> [00:00:00.400,177] <err> os: >>> ZEPHYR FATAL ERROR 25: Unknown error on CPU 0
00> [00:00:00.400,207] <err> os: Current thread: 0x20001d60 (unknown)
00> [00:00:01.627,502] <err> fatal_error: Resetting system[0m
不幸的是我不明白发生了什么。任何帮助将不胜感激!
更新:可能很多人都怀疑
attr == NULL
是悲伤的,但却是事实。但为什么呢?
总线错误是尝试使用空指针(就好像它指向数据一样)的结果。 this文档未能清楚地解释的是,当发现某些东西时...或当某些东西未发现时,会调用发现回调。:D(最好的工程...)提示和关键的一句话在this页面,谈论回调函数:
“如果发现过程已完成,将调用此回调,并将 attr 设置为 NULL。”
因此
params->type
和 attr->user_data
之间的关系并不像文档所暗示的那么简单,因为调用回调时可能会使用 params->type
为 BT_GATT_DISCOVER_PRIMARY
和 attr
为 NULL
。
除非通过返回
BT_GATT_ITER_STOP
来停止发现,否则将调用回调:
attr->user_data
设置为相应的事物attr
设置为 NULL还值得一提的是,传入的
bt_gatt_discover_params
结构体是通过 bt_gatt_discover()
函数调用进行修改的:当发现项目时,
start_handle
将被更改。 (不要尝试将此值用于任何有用的事情!发现的项目的句柄不存在)此行为意味着根据用例,必须在连续的 start_handle
调用之间一遍又一遍地设置 bt_gatt_discover()
。
另一个有趣(令人困惑)的地方是发现功能的文档给出了可能发现的项目类型的后面:主要服务,包括服务,特征,描述符。这是由传入的
type
的 bt_gatt_discover_params
成员控制的。(分别为 BT_GATT_DISCOVER_PRIMARY、BT_GATT_DISCOVER_INCLUDE、BT_GATT_DISCOVER_CHARACTERISTIC、BT_GATT_DISCOVER_DESCRIPTOR)。回调函数的 documentation 指出 param->type
的值保持不变。然而,回调函数的页面列出了 params->type
的另外两个可能值:BT_GATT_DISCOVER_STD_CHAR_DESC 和 BT_GATT_DISCOVER_ATTRIBUTE。 (Attiubute 非常有趣,因为可能发现的每个项目都是一个属性。事实上,当发现属性时,所有内容都已找到......)这必定意味着 bt_gatt_discover()
函数文档缺少两种可能的类型。
最后我想指出的是回调中
attr->user_data
的类型。回调的文档在表中指出,当发现描述符或属性时,attr->user_data
为 NULL。这不是真的。如果是 CCC(可能还有 CEP、SCC 和 CPF,甚至可能是其他),描述符和属性发现类型都会在 struct bt_gatt_scc
中返回 attr->user_data
。另一方面,使用 BT_GATT_DISCOVER_STD_CHAR_DESC
类型发现这些结构应该正确返回,却什么也没发现。