Subscribing to multiple notifications not working

I have a problem which seems similar to this one:  https://devzone.nordicsemi.com/f/nordic-q-a/106158/zephyr-os-ble-central-multiple-notification-subscription-event-issue 

I started with the central_hr and peripheral_hr samples. My goal was to enable and receive battery level notifications at the same time as heart rate measurement notifications. To that end, in the central, after writing to the HRM CCC characteristic, I search for the battery level characteristic and enable notifications like this:

static void start_bas_discovery() {

    printk("start_bas_discovery\n");
    memcpy(&discover_uuid, BT_UUID_BAS, sizeof(discover_uuid));
    discover_params.uuid = &discover_uuid.uuid;
    discover_params.func = discover_bas_func;
    discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
    discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
    discover_params.type = BT_GATT_DISCOVER_PRIMARY;

    int err = bt_gatt_discover(default_conn, &discover_params);
    if (err) {
        printk("Discover failed(err %d)\n", err);
        return;
    }
}

My discover_bas_func code is as follows:

static uint8_t discover_bas_func(struct bt_conn *conn,
			     const struct bt_gatt_attr *attr,
			     struct bt_gatt_discover_params *params)
{
	int err;
    printk("discover_bas_func callback\n");

	if (!attr) {
		printk("Discovery of BAS complete\n");
		(void)memset(params, 0, sizeof(*params));
		return BT_GATT_ITER_STOP;
	}

	printk("[ATTRIBUTE] handle %u\n", attr->handle);

	if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_BAS)) {
        printk("Discovered battery level service\n");
		memcpy(&discover_uuid, BT_UUID_BAS_BATTERY_LEVEL, sizeof(discover_uuid));
		discover_params.uuid = &discover_uuid.uuid;
		discover_params.start_handle = attr->handle + 1;
		discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;

		err = bt_gatt_discover(conn, &discover_params);
		if (err) {
			printk("Discover failed (err %d)\n", err);
		}
	} else if (!bt_uuid_cmp(discover_params.uuid,
				BT_UUID_BAS_BATTERY_LEVEL)) {
        found_battery_level = 1;
        printk("Found battery level\n");
		memcpy(&discover_uuid, BT_UUID_GATT_CCC, sizeof(discover_uuid));
		discover_params.uuid = &discover_uuid.uuid;
        // look for the descriptor next- two attribute handles later in the table
		discover_params.start_handle = attr->handle + 2;
		discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
		subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);

		err = bt_gatt_discover(conn, &discover_params);
		if (err) {
			printk("Discover failed (err %d)\n", err);
		}
	} else {
        if (subscribed_battery_level == 0) {
            subscribe_params.notify = notify_func;
            subscribe_params.value = BT_GATT_CCC_NOTIFY;
            subscribe_params.ccc_handle = attr->handle;
            printk("writing to battery level CCC handle=%d\n",subscribe_params.ccc_handle);
            err = bt_gatt_subscribe(conn, &subscribe_params);
            // EALREADY means there is already a subscription
            if (err && err == -EALREADY) {
                printk("Already subscribed to battery level\n");
                subscribed_battery_level = 1;
            } else if (err && err != -EALREADY) {
                printk("Subscribe failed (err %d)\n", err);
                subscribed_battery_level = 0;
            } else {
                printk("[SUBSCRIBED to BATTERY LEVEL] %d\n",err);
                subscribed_battery_level = 1;
            }
        }

		return BT_GATT_ITER_STOP;
	}

	return BT_GATT_ITER_CONTINUE;
}

I see the following output in the central's console.

[00:00:00.001,825] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002)
[00:00:00.001,839] <inf> bt_hci_core: HW Variant: nRF54Lx (0x0005)
[00:00:00.001,851] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 252.16862 Build 1121034987
[00:00:00.002,296] <inf> bt_hci_core: HCI transport: SDC
[00:00:00.002,345] <inf> bt_hci_core: Identity: D9:5A:9B:E8:08:C4 (random)
[00:00:00.002,360] <inf> bt_hci_core: HCI: version 6.1 (0x0f) revision 0x3069, manufacturer 0x0059
[00:00:00.002,373] <inf> bt_hci_core: LMP: version 6.1 (0x0f) subver 0x3069
Bluetooth initialized
Scanning successfully started
[DEVICE]: 54:BD:79:0F:23:5F (public), AD evt type 0, AD data len 31, RSSI -73
[AD]: 1 data_len 1
[AD]: 255 data_len 26
[DEVICE]: 54:BD:79:0F:23:5F (public), AD evt type 4, AD data len 28, RSSI -73
[DEVICE]: D1:00:2D:79:94:DD (random), AD evt type 0, AD data len 11, RSSI -35
[AD]: 1 data_len 1
[AD]: 3 data_len 6
Creating connection with Coded PHY support
Connected: D1:00:2D:79:94:DD (random)
[ATTRIBUTE] handle 26
[ATTRIBUTE] handle 27
Found heart rate measurement
[ATTRIBUTE] handle 29
writing to heart rate measurement CCC handle=29
[SUBSCRIBED to HEART RATE MEASUREMENT] 0
start_bas_discovery
discover_bas_func callback
[ATTRIBUTE] handle 16
Discovered battery level service
[NOTIFICATION] data 0x2000793c length 2
discover_bas_func callback
[ATTRIBUTE] handle 17
Found battery level
discover_bas_func callback
[ATTRIBUTE] handle 22
writing to battery level CCC handle=22
Already subscribed to battery level
discover_bas_func callback
[ATTRIBUTE] handle 19
discover_bas_func callback
[ATTRIBUTE] handle 29

I noticed that subscribing to the HRM notifications with bt_gatt_subscribe returns 0 but when subscribing to the battery level notifications I'm getting EALREADY which according to the documentation for bt_gatt_subscribe means that notifications are already subscribed for on this characteristic.

The handle parameters for the two bt_gatt_subscribe calls are not the same, so it's not that I neglected to change the subscribe params for the second call.

writing to heart rate measurement CCC handle=29
[SUBSCRIBED to HEART RATE MEASUREMENT] 0
writing to battery level CCC handle=22
Already subscribed to battery level

A trace confirms that only one CCC is being written to. HRM notifications are being transmitted by the peripheral.

Could someone please explain what I need to do to get this working so that I can receive both heart rate measurement and battery level notifications at the central please?

Thanks in anticipation.

p.s. I tried setting CONFIG_BT_BAS_CLIENT=y per the other issue but this appeared to make no difference. Given I'm using base Zephyr API calls, I didn't expect it to to be honest.

Parents Reply Children
  • Hi Vidar, I'm going to persist looking at this for a while longer before I burden you. I don't like the look of 

    if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_HRS))

    discover_params contains the parameters to be used when initiating discovery, I think as opposed to the properties of the attribute just discovered which is the attr struct in the parameters to the callback function. So this looks incorrect to me. I'll explore  this when I have a bit more time.

    All the best

    M

Related