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.

  • Hello,

    The attribute handle for BAS in the log (22) does not seem to match what is shown in the sniffer capture (0x12), but I I'm not immediately able to spot anything wrong from the code snippets you posted. As a test, could you please try connecting to this peripheral from the Bluetooth Low energy app in nRF connect for desktop and check the log which attribute handle is being used when enabling notifications on this characteristic?

    Best regards,

    Vidar

  • Hi Vidar, thanks for your reply. I see what you mean about the handle value. And (great idea), the Bluetooth Low Energy app confirms that my code is not selecting the right handle for the Battery Level CCC.

    It must be a bug in my code then. I'll keep staring at it until I can see the problem :-)

  • Thank you for the update. Looking at the description again I think I may have misread the sniffer capture. With the battery characteristic declaration at handle 0x12 it seems correct to have the CCC handle at 0x16 (22). Please let me know if you are still experiencing the issue. If so, I will try to reproduce it here.

  • 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