nRF Desktop - Restart advertising as peripheral & In-range detection

Hi there, I'm using ncs2.4.2 with the nrf52840dk_nrf52840 configuration of nrf desktop.

We have modified nRF desktop to be a bt peripheral composite mouse and keyboard.

1. Once connected to a central, I would like to restart advertising. Once advertising, if there is an incoming pairing request, we would like to connect to the incoming pairing request and unpair from the previously active connection. I am struggling to figure out how to do this within the nRF Desktop framework. I guess I could just do independent API calls in my own threads, but I would like to keep the nRF Desktop bt infrastructure properly synchronized. I see many references to erase advertising, but my understanding is that deletes bonds from a bt identity and changes identity, We would like to keep previously connected bonds and not change identity.

the enum "state" at the top of ble_bond.c feels close. Is there some example code or other resources you could point me to to help me implement this functionality?

2. The other thing I am not sure how to implement (perhaps due to my unfamiliarity with bluetooth) is to scan (discover?) previously bonded connections, and give some feedback on whether they are in range or not. I was imagining testing RSSI to a specific BT address. Is this how you would recommend implementing this?

Thank you very much for your time!

Parents
  • Hi mrd,

    1. Once connected to a central, I would like to restart advertising. Once advertising, if there is an incoming pairing request, we would like to connect to the incoming pairing request and unpair from the previously active connection. I am struggling to figure out how to do this within the nRF Desktop framework. I guess I could just do independent API calls in my own threads, but I would like to keep the nRF Desktop bt infrastructure properly synchronized.

    The nRF Desktop application is based on the Common Application Framework, which in turn is based on the Application Event Manager. In this setup, ever modules communicate with each other by sending events. I think all of the events are defined here: https://github.com/nrfconnect/sdk-nrf/tree/v2.5.0/include/caf/events.

    The nRF Desktop application already has a Peer Control feature that has a lot in common with your requirement. I find it easier to understand the code and documentation after reading the feature "manual" for the Gaming Mouse; this section, in particular:

    Long-press to initialize the peer erase. When LED1 starts blinking rapidly, double-press to confirm the operation. After the confirmation, Bluetooth advertising using a new local identity is started. When a new Bluetooth Central device successfully connects and bonds, the old bond is removed and the new bond is used instead. If the new peer does not connect in the predefined period of time, the advertising ends and the application switches back to the old peer.

    You can cancel the ongoing peer operation with a standard button press.

    Next, we will want to look into the modules involved with the management of BLE in nRF Desktop. I will list my findings on what is of interest for your requirement.

    BLE Bond module.

    The default implementation in CAF is not used. An implementation specific to nRF Desktop is used. The documentation is here, and the source code is here (ble_bond.c and Kconfig.ble_bond).

    We can find the peer erase control flow in the Module State section of the docs. The flow starts with transition to the STATE_ERASE_PEER upon a long click event, then to STATE_ERASE_ADV, and back to STATE_IDLE.

    From that, we can see in the implementation that the state transitions are defined in the state_switch table. The relevant event handlings are in app_event_handler() and handle_click().

    Notice that upon erasing a bond, the local identity associated with it is also reset. This seem to be not what you want; but perhaps you could consider to keep.

    Based on that, you can update the logic to have new state transitions, or perhaps new states as needed.

    BLE State module

    The default implementation in CAF is used.
    The CAF documentation is here, but there are some additional details in the nRF Desktop docs
    The source code is here (ble_state.c and Kconfig.ble_state).

    Notice in the Connection State Change section of the docs, a disconnection can be triggered by the regular Zephyr API bt_conn_disconnect().

    BLE Advertising module

    The default implementation in CAF is used.
    The CAF documentation is here, but there are some additional details in the nRF Desktop docs.
    The source code is here (ble_adv.c and Kconfig.ble_adv).

    There don't seem to be anything particular about this module. Without using CAF, normally, advertising is automatically restarted if the maximum number of connections allows. 
    I expect it to work similarly even when this module is in used. You can check by increasing CONFIG_BT_MAX_CONN and CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT to 2, then see if advertising is restarted normally after a connection is established. 

    Besides those, if you want to reconfigure these Kconfigs: 

    CONFIG_CAF_BLE_STATE_MAX_LOCAL_ID_BONDS
    CONFIG_CAF_BLE_ADV_FILTER_ACCEPT_LIST

    2. The other thing I am not sure how to implement (perhaps due to my unfamiliarity with bluetooth) is to scan (discover?) previously bonded connections, and give some feedback on whether they are in range or not. I was imagining testing RSSI to a specific BT address. Is this how you would recommend implementing this?

    Do you want to realize the feature above for the central device or the peripheral device?

    Only a central/observer device can scan for advertisement. In our case, that means only the central devices can detect the presence of previously bonded peer.

    As noted above, the local identity is reset when a bond is erased. That means that the device will not advertise with the same identity again. Therefore, even if the central device tries to look for a previously bonded peer, it will not succeed.

    If you change the implementation to not reset the identity upon bond deletion, then it depends on the kind of advertisement.
    If the device advertises with a Public Address or Random Static Address, it can be found again for sure.
    If the device advertises with a Random Private Address, it depends on if the IRK is reused.

    If you need more information about the address types, you can refer to Lesson 2 of our Bluetooth Low Energy Fundamentals course.


    I will be out of office next week, but please don't hesitate to reply if you have any further questions. Unless someone from the community have helped you, another engineer will continue to support you.

    Hieu

    ---

    Due to the ongoing Christmas - New Year holiday season, we are severely understaffed, and there would be delays in responses. The situation should start improving next week. Our apologies for the inconveniences. 

  • Hi Hieu,

    Thank you for the detailed response!

    A few points of clarification.

    For 1, my understanding of how nRF Desktop works is that (as peripheral) when it bonds to the new device, it assumes a new identity and forgets previous bonds. This is shown in the state_switch table you linked to: there is only IDLE, ERASE_ADV, and STATE_ERASE_PEER. The state I'm looking for in state_switch is ADV, but it doesn't exist. No connection deleting should take place until we reach the maximum number of ble devices the device can save.

    The functional flow of my app is: Connect to a central. Trigger advertising again and have a second central connect. Disconnect from first central. Remember both centrals. 

    I see ble_adv_start_undirected in ble_adv.c - it does what I want, but I don't want to break expected ble_adv states by calling functions out of sync

    For 2, this would be from peripheral perspective. In my understanding, the nRF Desktop peripheral has a list of devices it has previously connected to (aka bonded with?). I would like to query each device in the list and see if they are available to connect to. I have found a functions in ble_bond.c (bt_foreach_bond) which I might be able to use to query each one, but I'm not quite sure how to to do the querying part.

    Thank you for the addtional address information. I expect we would use a random static address so that our device can be easily be found again.

  • Hi mrd,

    I think one point here that we need to remember is that the central is the device that decides when a connection happens in BLE. 

    mrd said:
    I was thinking this was a basic bluetooth use case. Connect to a central. Advertise again and connect to a second central. Disconnect from first central. Remember both centrals.

    I see. I think this is more similar to how Bluetooth audio devices work. If you want your product to work this way, then you might need to modify the existing states of the BLE Bond module or create new ones.

    The state machine of the BLE Bond module, including all the state definitions, is completely internal to that module, so changing it should not conflict with other modules in the CAF.

    mrd said:
    What I would like to do is bond to the new device but still remember devices that we connected to in the past, instead of forgetting.

    My apology. I previously misunderstood that you want the old bond forgotten. This is also perfectly possible.

    If you want multiple bonds remembered on the same identity, then you should also look at these Kconfigs:

    CONFIG_CAF_BLE_STATE_MAX_LOCAL_ID_BONDS
    CONFIG_BT_MAX_PAIRED 

    That is on top of a modified BLE Bond state machine.


    I understand your requirement here and think it would work fine. However, just in case there is any misunderstanding about the nRF Desktop application, I would like to visit its approach to manage new peers.

    Refer again to the Peer Control feature docs:

    • Short-press to initialize the peer selection. (The LED1 changes color and starts blinking.) During the peer selection:
      • Short-press to toggle between available peers. The LED1 changes color for each peer and keeps blinking.
      • Double-press to confirm the peer selection. The peer is changed after the confirmation. LED1 stops blinking.
        When it rotates to an identity that has yet had a bond, the application will be open to form a new bond with a new central.

    As you can see, this solution also allows for multiple bonds remembered at the same time.

    The behavior here is also the exact same as how my Bluetooth mice work. I personally find it more convenient for keyboard and mouse device. But again, you know your product best, so it's your call.

    mrd said:
    For 2, this would be from peripheral perspective. In my understanding, the nRF Desktop peripheral has a list of devices it has previously connected to (aka bonded with?). I would like to query each device in the list and see if they are available to connect to. I have found a functions in ble_bond.c (bt_foreach_bond) which I might be able to use to query each one, but I'm not quite sure how to to do the querying part.

    A peripheral can perform Directed Advertising to a bonded peer. If the central is scanning and detects that advertisement, it can send a connection establishment packet, or a Scan Request. Upon receiving those packets, the peripheral would know the central is available. 

    There are a few points to notice:

    • The central is completely free to choose what to do when it receives the Directed Advertising packet.
      • I didn't have the chance to look up the Bluetooth Specification thoroughly, so I am not sure if there are any required behavior for a central device upon receiving a Directed Advertising packet.

      • Note that Apple's Accessory Design Guidelines recommend against the use of Directed Advertising. They don't explain and I am not sure why.
        What this means is that we cannot be sure that Apple devices will automatically connect to Directed Advertisements.


    • The central has to be scanning.
    mrd said:
    Thank you for the addtional address information. I expect we would use a random static address so that our device can be easily be found again.

    If the device should be found again by a bonded central, then Random Private Address should also make it easy. A bonded central would have the IRK, Identity Resolving Key, to decrypt the Random Private Address into an identifiable address.

  • Hello Hieu,

    I have the first part of the application working.

    As you described, I increased CONFIG_BT_MAX_PAIRED=4 and CONFIG_BT_ID_MAX=5.

    Now I have 3 "user" bonds to play around with.

    I send click events to the module to select different peers and advertise in empty slots with the  CONFIG_DESKTOP_BLE_PEER_SELECT functions, and I erase bonds with the CONFIG_DESKTOP_BLE_PEER_ERASE functions.

    One of the most confusing parts is that ble_bond.c uses it's own identities which are then mapped to bt identities, but bt_foreach_bond() iterates through bt identities only. To iterate through the user profiles, you must do this. Note get_bt_stack_peer_id(), which maps between the ble_bond identity i and the bt stack id

    for (size_t i = 0; i < MAX_USER_BT_CONNS; i++)
        {
            bt_foreach_bond(get_bt_stack_peer_id(i), your_function, &your_data);
        }

    Thanks for all your help. Consider this case closed.

Reply
  • Hello Hieu,

    I have the first part of the application working.

    As you described, I increased CONFIG_BT_MAX_PAIRED=4 and CONFIG_BT_ID_MAX=5.

    Now I have 3 "user" bonds to play around with.

    I send click events to the module to select different peers and advertise in empty slots with the  CONFIG_DESKTOP_BLE_PEER_SELECT functions, and I erase bonds with the CONFIG_DESKTOP_BLE_PEER_ERASE functions.

    One of the most confusing parts is that ble_bond.c uses it's own identities which are then mapped to bt identities, but bt_foreach_bond() iterates through bt identities only. To iterate through the user profiles, you must do this. Note get_bt_stack_peer_id(), which maps between the ble_bond identity i and the bt stack id

    for (size_t i = 0; i < MAX_USER_BT_CONNS; i++)
        {
            bt_foreach_bond(get_bt_stack_peer_id(i), your_function, &your_data);
        }

    Thanks for all your help. Consider this case closed.

Children
Related