I' advertising a primary BLE service. Using the nRF android app everything is full ok. Another scanning device wants to find the advert, connect, and get a list of GATT services and list their UUIDs. Scanning and connecting works fine. I know for sure that I'm connected to my device and not some other thing. Discovery is initialized as:
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;
}
No error here, discovery starts. I understand (I hope) the documentation here, that params->type
indicates what kind of thing I'm discovering (must be a BT_GATT_DISCOVER_PRIMARY because the way discovery is initialized), then attr->user_data
is a struct bt_gatt_service_val
, and uuid
in there is the thing I want to print for now. I have seen many examples on the internet, and there is plenty in the zephyr SDK folder too (for example ./zephyr/subsys/bluetooth/audio/vcp_vol_ctlr.c) for doing the same. The procedure is casting user_data to the proper struct, then making a string from the uuid with bt_uuid_to_str
. Sound easy:
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;
}
However, on the bt_uuid_to_str line the whole thing blow up and it says:
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
And here I unfortunately don't understand what's happening. Any help would be much appreciated!
Update: as probably many of you have suspected attr == NULL
is sad but true. But why?
The bus error is a consequence of trying to use the null pointer as if it pointed to data. What this documentation fails to explain clearly is that the discovery callback is called either when something is discovered... or when something is not discovered. :D (Engineering at its best...) The hint and the critical sentence is on this page that talks about the callback function:
"If discovery procedure has completed this callback will be called with attr set to NULL."
Therefore the relation between
params->type
andattr->user_data
is not as simple as the documentation suggests, as the callback will probably be called withparams->type
beingBT_GATT_DISCOVER_PRIMARY
andattr
beingNULL
.Unless the discovery is stopped by returning
BT_GATT_ITER_STOP
the callback will be called:attr->user_data
set to the corresponding thingattr
set to NULLIt is also worth mentioning that the
bt_gatt_discover_params
struct passed in is modified by thebt_gatt_discover()
function call: thestart_handle
will be changed as items are discovered. (Do not try to use this value for anything useful! The handle of the discovered item is NOT there) This behaviour means that depending on the use casestart_handle
has to be set over and over again between consecutivebt_gatt_discover()
calls.Another interesting (confusing) thin is that the documentation of the discover function gives a hind of the item types possible to discover: primary service, include service, characteristic, descriptor. This is controlled by the
type
member of thebt_gatt_discover_params
passed in. (BT_GATT_DISCOVER_PRIMARY, BT_GATT_DISCOVER_INCLUDE, BT_GATT_DISCOVER_CHARACTERISTIC, BT_GATT_DISCOVER_DESCRIPTOR respectively). The documentation of the callback function states that the value of theparam->type
is left unchanged. Yet the callback function's page lists two more possible values forparams->type
: BT_GATT_DISCOVER_STD_CHAR_DESC and BT_GATT_DISCOVER_ATTRIBUTE. (Attiubute is extra interessant, because every item possibly discovered is an attribute. And in fact when discovering attributes everything is found...) This must mean that thebt_gatt_discover()
functions documentation is missing two possible types.The last thing I want to point out is the type of the
attr->user_data
in the callback. The callback's documentation states in the table that when discovering descriptors or attributes theattr->user_data
is NULL. This is not true. Both descriptor and attribute discovery types returnstruct bt_gatt_scc
inattr->user_data
in case of CCC (and possibly CEP, SCC and CPF, maybe even the others). On the other hand discovering withBT_GATT_DISCOVER_STD_CHAR_DESC
type where these structs should be properly returned discovers nothing at all.