Limit HIDs max client count

Hello and thank you for reading. We are using SDK 2.5.0 and the nrf5340, and would appreciate some help with the HID over Gatt functionality.

Our device implements multiple services, including NUS and HID over GATT. We are now experimenting with having multiple centrals connect to our peripheral. 

The idea would be that up to five centrals could be paired, and at least two could be connected and subscribed to NUS at the same time. We dont, however, want more than one device to be subscribed to HID at a time. The difficulty is handling the case where client A is connected and subscribed, but then client B connects and subscribes. Ideally client A would be unsubscribed but the connection not dropped so client A can continue using NUS.

Is this do-able? Ive tried setting CONFIG_BT_HIDS_MAX_CLIENT_COUNT to 1, but I can still connect two devices at once and both have mouse movements. Is there some other config I can set? Or is there a way I can in my firmware unsubscribe a client? Or any other way of achieving this?

I have included the prj.conf I used for testing multiple devices below, in case that helps.

Thanks!

# Increased stack due to settings API usage
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

CONFIG_BT=y
CONFIG_LOG=y
CONFIG_BT_SMP=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DIS=y
CONFIG_BT_BAS=y
CONFIG_BT_DEVICE_NAME="Test HoG mouse"
CONFIG_BT_DEVICE_APPEARANCE=962

CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y

CONFIG_GPIO=y

CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_LOG_SNIFFER_INFO=y

CONFIG_BT_AUTO_PHY_UPDATE=n
CONFIG_BT_USER_PHY_UPDATE=y
CONFIG_BT_CTLR_PHY_2M=y
# ----------------------------

CONFIG_BT_MAX_PAIRED=5 
CONFIG_BT_MAX_CONN=6

CONFIG_BT_HIDS=y
CONFIG_BT_HIDS_MAX_CLIENT_COUNT=1

CONFIG_BT_NUS=y
CONFIG_BT_NUS_AUTHEN=n

Parents
  • The CONFIG_BT_HIDS_MAX_CLIENT_COUNT configuration determines the maximum number of HID clients that can be tracked by the HID service instance. Setting this value to 1 means the HID service will maintain context for only one client. However, this does not inherently prevent multiple devices from connecting or subscribing to the HID service; it merely limits the internal tracking to a single client. Therefore, additional application-level logic is necessary to enforce the desired subscription behavior. You can handle the connection and disconnection behavior as your application need. Based on your description you can do something like this (for example).. There are many ways to do this, but the point here is that you cannot just rely on CONFIG_BT_HIDS_MAX_CLIENT_COUNT 

    static void connected(struct bt_conn *conn, uint8_t err)
    {
        if (err) {
            LOG_ERR("Connection failed (err %d)", err);
            return;
        }
    
        // Disconnect the old connection , if I understood your text right, this is what you want?
        if (centrals[0].conn) {
            LOG_WRN("New connection detected! Disconnecting previous device.");
            bt_conn_disconnect(centrals[0].conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
            bt_conn_unref(centrals[0].conn);
        }
    
        centrals[0].conn = bt_conn_ref(conn);
        centrals[0].subscribed_nus = false;
        centrals[0].subscribed_hid = false;
        LOG_INF("New device connected, old device disconnected.");
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
        if (centrals[0].conn == conn) {
            LOG_INF("Device disconnected, clearing connection context.");
            if (centrals[0].subscribed_hid) {
                hid_active_conn = NULL; 
            }
            bt_conn_unref(centrals[0].conn);
            centrals[0].conn = NULL;
            centrals[0].subscribed_nus = false;
            centrals[0].subscribed_hid = false;
        }
    }
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
        .connected = connected,
        .disconnected = disconnected,
    };


Reply
  • The CONFIG_BT_HIDS_MAX_CLIENT_COUNT configuration determines the maximum number of HID clients that can be tracked by the HID service instance. Setting this value to 1 means the HID service will maintain context for only one client. However, this does not inherently prevent multiple devices from connecting or subscribing to the HID service; it merely limits the internal tracking to a single client. Therefore, additional application-level logic is necessary to enforce the desired subscription behavior. You can handle the connection and disconnection behavior as your application need. Based on your description you can do something like this (for example).. There are many ways to do this, but the point here is that you cannot just rely on CONFIG_BT_HIDS_MAX_CLIENT_COUNT 

    static void connected(struct bt_conn *conn, uint8_t err)
    {
        if (err) {
            LOG_ERR("Connection failed (err %d)", err);
            return;
        }
    
        // Disconnect the old connection , if I understood your text right, this is what you want?
        if (centrals[0].conn) {
            LOG_WRN("New connection detected! Disconnecting previous device.");
            bt_conn_disconnect(centrals[0].conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
            bt_conn_unref(centrals[0].conn);
        }
    
        centrals[0].conn = bt_conn_ref(conn);
        centrals[0].subscribed_nus = false;
        centrals[0].subscribed_hid = false;
        LOG_INF("New device connected, old device disconnected.");
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
        if (centrals[0].conn == conn) {
            LOG_INF("Device disconnected, clearing connection context.");
            if (centrals[0].subscribed_hid) {
                hid_active_conn = NULL; 
            }
            bt_conn_unref(centrals[0].conn);
            centrals[0].conn = NULL;
            centrals[0].subscribed_nus = false;
            centrals[0].subscribed_hid = false;
        }
    }
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
        .connected = connected,
        .disconnected = disconnected,
    };


Children
  • Hello,

    Thank you for your reply, and especially for clarifying the functionality of CONFIG_BT_HIDS_MAX_CLIENT_COUNT. 

    Your example code unfortunately isnt quite what Im looking for; I dont want to disconnect the first device when the second connects, I simply want to refuse the subscription request from the second device if the first one is still subscribed.

    I notice that the callback when the CCC changes doesnt directly allow for this implementation, as I cannot get the subscriber's address/conn* from it, nor can I return false or something to reject the subscription. Is there some other implementation, perhaps with a different callback, I could use instead?

    EDIT: Im thinking I could do something with a custom implementation of CCC_write, seeing as that function _does_ get the conn object, but I seem to be doing something wrong somehow...

    I changed my service declaration to:

    BT_GATT_SERVICE_DEFINE(
    	Amber_spp_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_AMBER_SPP),
    	BT_GATT_CHARACTERISTIC(PADDING_UUID,NULL,NULL, NULL, NULL, NULL),  //For some reason if i erase this characteristic, it doesnt notify anymore
    	
    	BT_GATT_CHARACTERISTIC(BT_UUID_AMBER_SPP_RX, BT_GATT_CHRC_WRITE, BT_GATT_PERM_WRITE, NULL,rx_data, NULL),
    	
    	BT_GATT_CHARACTERISTIC(BT_UUID_AMBER_SPP_TX, BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_NONE, NULL,NULL, NULL),
    
    	BT_GATT_DESCRIPTOR(BT_UUID_GATT_CCC,BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,bt_gatt_attr_read_ccc,my_cfg_write,NULL),
    	
    );

    And I can see in the console my on-write callback is triggering and _should_ be enabling notifications, but then I never actually get my notification.

    Does this approach make sense, is there something obvious im doing wrong?

    EDIT2: actually the more I think about it, the more I think it makes sense to just, on the HID characterisitc, use bt_gatt_notify but instead of NULL, I will pass the conn* of the first/desired peer so that only they get the update. Does that make sense, so technically both clients have notifications enabled but only one actually gets anything.

Related