Support multiple subscription for single service with multiple characteristics

Hi,

I am working on project where the peripheral has 2 services, and each has multiple characteristics.

The central needs to subscribe to all the characteristics and when doing so I am seeing error: -120 (Socket already connected)

Below is the device log from the central, as you can see I am reading 2 characteristics COMMAND and INDICATION.

When trying to subscribe to the characteristics, I can subscribe to the first characteristics whereas when subscribing to the second characteristics I am getting the error.

Found CMD CHR: 14
Found IND CHR: 17
bt_gatt_subscribe: 0, ccc_handle: 15
[SUBSCRIBED]
bt_gatt_subscribe: -120, ccc_handle: 18

Let me also add our peripheral service server list here for reference

BT_GATT_SERVICE_DEFINE(dev_svc,
    BT_GATT_PRIMARY_SERVICE(BT_UUID_DEV),
    BT_GATT_CHARACTERISTIC(BT_UUID_CMD,
                           (BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE),
                           BT_GATT_PERM_WRITE,
                           NULL, callback_write_cmd,
                           NULL),
	BT_GATT_CCC(notif_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
    BT_GATT_CHARACTERISTIC(BT_UUID_IND,
                           (BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE),
                           BT_GATT_PERM_WRITE,
                           NULL, callback_write_ind,
                           NULL),
	BT_GATT_CCC(notif_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);

Please let me know if I am doing anything wrong or any config we should add.

I am working on NRF Connect SDK v2.7.0

Board: NRF52840

Parents
  • Hi,

    There is no fundamental issue with having multiple characteristics with nofify or indicate in the same service. I have some qustions in order to understand more:

    1. How are you testing?
    2. Are you abel to subscribe to any of the two characteristics, just not both at the same time?
    3. Have you tested with a different central implementation (for instance nRF Connect for Mobile) to narrow down the issue to understand if the issue is on the central or peripheral side?

    That said, I assume we need to look more closely at your central implementation. bt_gatt_subscribe() will return -EALREADY (-120) if allready subscribed, so I suspect you are referencing the same CCCD for some reason.

  • Hi Einar,

    Thank you for the response, let me put my comments here

    How are you testing?

    --> I have here a NRF52840DK and a custom PCB with NRF52840 in it.

    So, I have configured DK as central device for logging and custom PCB as peripheral.

    When I try to subscribe using my DK, I am seeing the above error.

    Are you abel to subscribe to any of the two characteristics, just not both at the same time?

    --> Yes, in the above log you can see, the CMD characteristics is successfully subscribed and from the characteristics the peripheral can send the indication and central receive correctly.

    If I subscribe to IND characteristics without subscribing to the CMD characteristics, it works.

    What I see is I am not able to subscribe to both at a time.

    Have you tested with a different central implementation (for instance nRF Connect for Mobile) to narrow down the issue to understand if the issue is on the central or peripheral side?

    --> Not yet, I just got to know about the mobile app (NRF Connect), will give a try with that and update to you here.

    I suspect you are referencing the same CCCD for some reason.

    --> Yes, I am also thinking same. 

    Let me share you my service discovery code snippet, so you can get to know any mistake I am doing.

    static uint8_t discover_cb (struct bt_conn *conn, const struct bt_gatt_attr *attr,
    			                struct bt_gatt_discover_params *params)
    {
    	int err;
    	static app_task_msg_t msg;
    	static struct bt_uuid_128 uuid = BT_UUID_INIT_128(0);
    
    	if (!attr) {
    		// The control should not come here. Just in case, the attribute handles are not handled
            // properly it comes, disconnect the peer and wait for reconnection
            //
    		(void)memset(params, 0, sizeof(*params));
    		bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
    
    		return BT_GATT_ITER_STOP;
    	}
    
    	if (!bt_uuid_cmp(g_discover_params.uuid, BT_UUID_NOTIFY_DEV))
        {
    		// Found the NOTIF_DEV_SERVICE. Look for the CMD characteristic handle
            //
    		memcpy(&uuid, BT_UUID_CMD, sizeof(uuid));
    		g_discover_params.uuid = &uuid.uuid;
    		g_discover_params.start_handle = attr->handle + 1;
    		g_discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
    		bt_gatt_discover(conn, &g_discover_params);
    	}
        else if (!bt_uuid_cmp(g_discover_params.uuid, BT_UUID_CMD)) {
    		// Copy command characteristics handle for future reference
            //
    		current_dev_info.cmd_chr_handle = (attr->handle + 1);
    
            // As we have found command characteristics, look for indication characteristics
            //
            printk("Found CMD CHR: %d\n", current_dev_info.cmd_chr_handle);
    		memcpy(&uuid, BT_UUID_IND, sizeof(uuid));
    		g_discover_params.uuid = &uuid.uuid;
    		g_discover_params.start_handle = attr->handle;
    		g_discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
    		bt_gatt_discover(conn, &g_discover_params);
    	}
        else if (!bt_uuid_cmp(g_discover_params.uuid, BT_UUID_IND)) {
    		// Copy indication characteristics handle for future reference
            //
    		current_dev_info.ind_chr_handle = (attr->handle + 1);
    
    		// Found the NOTIF_DEV_CMD_CHR handle. Find the DESCRIPTOR handle
    		//
            printk("Found IND CHR: %d\n", current_dev_info.ind_chr_handle);
    		memcpy(&uuid, BT_UUID_GATT_CCC, sizeof(struct bt_uuid_16));
    		g_discover_params.uuid = &uuid.uuid;
    		g_discover_params.start_handle = attr->handle + 2;
    		g_discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
    		g_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
    		bt_gatt_discover(conn, &g_discover_params);
    	}
        else {
    		// Subscribe to the INDICATIONS, this will enable indications on the peripheral
    		//
    		g_subscribe_params.notify = indication_cb;
    		g_subscribe_params.value = BT_GATT_CCC_INDICATE;
    		g_subscribe_params.ccc_handle = current_dev_info.cmd_chr_handle + 1;
    
            // Subscribe for CMD 
            //
    		err = bt_gatt_subscribe(conn, &g_subscribe_params);
            printk("bt_gatt_subscribe: %d, ccc_handle: %d\n", err, g_subscribe_params.ccc_handle);
    		if (err == 0) {
                printk("[SUBSCRIBED]\n");
    		}
    
            // Subscribe for IND
            //
            g_subscribe_params.ccc_handle = current_dev_info.ind_chr_handle + 1;
    		err = bt_gatt_subscribe(conn, &g_subscribe_params);
            printk("bt_gatt_subscribe: %d, ccc_handle: %d\n", err, g_subscribe_params.ccc_handle);
    		if (err == 0) {
                printk("[SUBSCRIBED]\n");
    		}
    	}
    
    	return BT_GATT_ITER_STOP;
    }
    

    Please review it once and let me know.

    Thank you in advance

Reply
  • Hi Einar,

    Thank you for the response, let me put my comments here

    How are you testing?

    --> I have here a NRF52840DK and a custom PCB with NRF52840 in it.

    So, I have configured DK as central device for logging and custom PCB as peripheral.

    When I try to subscribe using my DK, I am seeing the above error.

    Are you abel to subscribe to any of the two characteristics, just not both at the same time?

    --> Yes, in the above log you can see, the CMD characteristics is successfully subscribed and from the characteristics the peripheral can send the indication and central receive correctly.

    If I subscribe to IND characteristics without subscribing to the CMD characteristics, it works.

    What I see is I am not able to subscribe to both at a time.

    Have you tested with a different central implementation (for instance nRF Connect for Mobile) to narrow down the issue to understand if the issue is on the central or peripheral side?

    --> Not yet, I just got to know about the mobile app (NRF Connect), will give a try with that and update to you here.

    I suspect you are referencing the same CCCD for some reason.

    --> Yes, I am also thinking same. 

    Let me share you my service discovery code snippet, so you can get to know any mistake I am doing.

    static uint8_t discover_cb (struct bt_conn *conn, const struct bt_gatt_attr *attr,
    			                struct bt_gatt_discover_params *params)
    {
    	int err;
    	static app_task_msg_t msg;
    	static struct bt_uuid_128 uuid = BT_UUID_INIT_128(0);
    
    	if (!attr) {
    		// The control should not come here. Just in case, the attribute handles are not handled
            // properly it comes, disconnect the peer and wait for reconnection
            //
    		(void)memset(params, 0, sizeof(*params));
    		bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
    
    		return BT_GATT_ITER_STOP;
    	}
    
    	if (!bt_uuid_cmp(g_discover_params.uuid, BT_UUID_NOTIFY_DEV))
        {
    		// Found the NOTIF_DEV_SERVICE. Look for the CMD characteristic handle
            //
    		memcpy(&uuid, BT_UUID_CMD, sizeof(uuid));
    		g_discover_params.uuid = &uuid.uuid;
    		g_discover_params.start_handle = attr->handle + 1;
    		g_discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
    		bt_gatt_discover(conn, &g_discover_params);
    	}
        else if (!bt_uuid_cmp(g_discover_params.uuid, BT_UUID_CMD)) {
    		// Copy command characteristics handle for future reference
            //
    		current_dev_info.cmd_chr_handle = (attr->handle + 1);
    
            // As we have found command characteristics, look for indication characteristics
            //
            printk("Found CMD CHR: %d\n", current_dev_info.cmd_chr_handle);
    		memcpy(&uuid, BT_UUID_IND, sizeof(uuid));
    		g_discover_params.uuid = &uuid.uuid;
    		g_discover_params.start_handle = attr->handle;
    		g_discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
    		bt_gatt_discover(conn, &g_discover_params);
    	}
        else if (!bt_uuid_cmp(g_discover_params.uuid, BT_UUID_IND)) {
    		// Copy indication characteristics handle for future reference
            //
    		current_dev_info.ind_chr_handle = (attr->handle + 1);
    
    		// Found the NOTIF_DEV_CMD_CHR handle. Find the DESCRIPTOR handle
    		//
            printk("Found IND CHR: %d\n", current_dev_info.ind_chr_handle);
    		memcpy(&uuid, BT_UUID_GATT_CCC, sizeof(struct bt_uuid_16));
    		g_discover_params.uuid = &uuid.uuid;
    		g_discover_params.start_handle = attr->handle + 2;
    		g_discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
    		g_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
    		bt_gatt_discover(conn, &g_discover_params);
    	}
        else {
    		// Subscribe to the INDICATIONS, this will enable indications on the peripheral
    		//
    		g_subscribe_params.notify = indication_cb;
    		g_subscribe_params.value = BT_GATT_CCC_INDICATE;
    		g_subscribe_params.ccc_handle = current_dev_info.cmd_chr_handle + 1;
    
            // Subscribe for CMD 
            //
    		err = bt_gatt_subscribe(conn, &g_subscribe_params);
            printk("bt_gatt_subscribe: %d, ccc_handle: %d\n", err, g_subscribe_params.ccc_handle);
    		if (err == 0) {
                printk("[SUBSCRIBED]\n");
    		}
    
            // Subscribe for IND
            //
            g_subscribe_params.ccc_handle = current_dev_info.ind_chr_handle + 1;
    		err = bt_gatt_subscribe(conn, &g_subscribe_params);
            printk("bt_gatt_subscribe: %d, ccc_handle: %d\n", err, g_subscribe_params.ccc_handle);
    		if (err == 0) {
                printk("[SUBSCRIBED]\n");
    		}
    	}
    
    	return BT_GATT_ITER_STOP;
    }
    

    Please review it once and let me know.

    Thank you in advance

Children
Related