Adding HID to my UART + DFU app causes HCI Error 0x3E

System details:

  • Board: Custom
  • Chip: nRF52832
  • PCA Number: PCA10040
  • SoftDevice: S132
  • SDK: Version 16.0.0

I have been building a custom nRF52832 device for a custom application that incorporates a handful of common services from the nRF SDK examples. This project first began by incorporating the TWI peripheral example with the BLE UART example, and at this point the application supported the UART and DIS services (both of these services were advertised). Next, I incorporated the DFU service (it was never an advertised service) and once I got this service to work quite well, I came to depend on the fact that I could connect to my device via the iOS nRF Connect app and perform an OTA DFU. For added context, my custom device is a fully-encapsulated, wirelessly-charged device, which means that I have no access to debug or perform DFU via the SeggerES once I encapsulate the hardware...so this use of the nRF Connect iOS app is very helpful.

Now, I just got around to adding the HIDS mouse service (which also requires the BAS and DIS services), and although I am able to successfully control my computer's cursor when using my device (sometimes), I can no longer perform my OTA DFU (nRF Toolbox and Connect for both iOS and Mac OS). I can connect to the device with a separate, custom CLI application I have built that I was using previously for BLE UART communication, but I CAN NOT make a connection to the device when trying to perform OTA DFU (I have tried nRF Toolbox, and nRF Connect for iOS and for Mac Desktop with a nRF52DK as a dongle). To add a little more color, when I select "Connect" to my custom device in the nRF Connect app, it times out and shows a log/pop-up error that connection cant be established. I have also gotten a pop-up that says "Peer removed pairing information". When trying the same thing via the Desktop nRF Connect app, the HCI Error 0x3E is thrown. On the other hand, my custom CLI application scans for UART-enabled devices of a particular name, and connects via the Bleak BLE library. This essentially works as before, only now it connects to the UART service AND to the HID mouse service (I can see it pop up in my computer's bluetooth preferences as a connected HID device).

Another observation related to connection...I was going to make a new ticket about this, but felt it is probably related: It might even be an intended feature of the HID mouse code, but I notice that sometimes when the device is turned on & advertising, and my computer bluetooth is on, a connection will rapidly be made and then disconnected over and over again between the device and my computer. I can see this happen in the Debug Console, and this behavior has only occurred after installing HIDS. I tried erasing bonds, but it doesnt appear to prevent that repeated connection attempt behavior. This behavior doesnt seem to affect the performance of the UART service, which can still be connected to after this back and forth connection/disconnection and it sends/receives messages just fine. However, it does often affect the performance of the HID mouse code, which sometimes wont work at all when the BLE connection is made after the series of repeated connections/disconnections. When viewing the Debug Console, I notice that whenever the HIDS mouse fails to get set up properly (and thus fails to control my computer mouse), it is due to a "Connection security failed: Bonding error 133". As I mentioned, I have erase_bonds = true and this parameter is passed in to advertising_start(erase_bonds) upon device initialization. I also call delete_bonds() in the ble_evt_handler on BLE_GAP_EVT_DISCONNECTED, but this event never occurs because the HIDS mouse code just stays connected. Lastly, I have looked into the way that I am disconnecting from my device, and realize that it is likely problematic. Previously, I just had to unsubscribe from the UART TX characteristic and disconnect. Now I am also unsubscribing from the Input Report Characteristic, but I believe I am doing this wrong, and also I worry that I am forgetting to unsubscribe from some other HID characteristics that I dont know enough about. Any guidance/advice/resources on disconnecting from HID properly is much appreciated!

Finally, Id like to share the edits/tweaks I have made along the way that differ from the basic example code:

  • sdk_config.h: Attached in .zip below for general reference. My concern is I have somehow mismatched some configs that are problematic without me knowing...
  • mouth_mouse.c: Attached in .zip below. The file containing all HIDS-specific code.
    • NOTE that at the end of the function mouse_movement_send(), I have added the if check (err_code != NRF_ERROR_FORBIDDEN) along with the others to handle this error that would keep getting thrown because I have set the mouse movement period to be every 50ms
    • This was suggested in this post. Is there a better more official method I should use?
  • ble_stack.c: Attached in .zip below. The main file for all BLE-related code (advertising, connections, most services, NUS event handler)
    • NOTE that instead of advertising UART and DIS as I previously did, now we are advertising UART and HID, no DIS. Would this change anything?
  • data_sampling.c: Attached in .zip below. This is the main code that reads all sensor data and streams it via BLE UART.
  • main.c: Attached in .zip below for reference.

4846.files.zip

  • Hello,

    Please note that we are very short staffed here in Norway due to Easter Holidays this week (and Monday next week). This will lead to a longer than usual response time. I am sorry for the inconvenience. Because of this, my answer today is also a bit short, and I would normally try to read up a bit more before answering.

    From my first glance of this ticket, it may look like you are trying to use a bootloader that requires bonding, but the nRF doesn't have any bonding information stored when you enter DFU mode. Is it intentional (to use bonding) ? If so, I suggest you don't delete the bonding information, like you do in the disconnected event, or during startup (don't press the button). If you delete the bonding information, then the bootloader will require the device to be bonded in order to connect. However, the bootloader has no way of creating new bonds, and hence, it is stuck in a deadlock.

    Best regards,

    Edvin

  • Hi Edvin, thanks! I understand regarding the Easter Holidays. Thank you for the heads-up, and for the response.

    I am using the secure bootloader with my device, and you were correct: disabling my code that erased the previous bonds allowed me to connect to the nRF Connect iOS app. Oddly enough, however, this code change breaks my previous method of connecting to my device using my custom CLI application...so now, although I can connect to nRF Connect and perform the OTA DFU, my custom CLI script fails to make a connection to my device and it throws a connection timeout error. Double oddly enough, the sdk_configs for the bootloader seem to show that the service_changed and requires_bonds variables are both set to 0.

    As I was debugging this, I noticed that even if I do not disable bond erasing (aka I keep erase_bonds = true), I can actually connect perfectly to my device using the nRF Connect iOS app so long as I do so after the device has restarted, and before any new connection is made.

    I tried to figure out why this was happening....It appears to be the case that I am failing to disconnect from the device properly now that I have added the HIDS mouse service. Previously, I have been able to disconnect from the UART characteristics we were subscribed to with no issues. With HIDS added, and while using the SES debugger, I can see that when I run the old "Disconnect" routine from my custom CLI code, the device no longer disconnects, aka the BLE_APP_EVT_DISCONNECTED event in the ble_evt_handler() is no longer triggered, aka I must still be subscribed to some HIDS characteristics. Instead of a proper disconnection, all I see is a print statement from the Peer Manager Handler (screenshot below) when trying to disconnect. Thus, when I try to connect via the nRF Connect iOS app after this type of connection to the device, the iOS app can never actually connect, and my guess is that this happens because there is still technically another HIDS device connection still active that is getting in the way.

    One last detail: Immediately after a device restart, I can connect to the nRF Connect iOS app as I mentioned above. This is great. Also, if I decide to first connect to my custom CLI application instead, everything with the device works as expected (I can stream data via UART perfectly, and the HIDS mouse also controls my computer mouse perfectly). This is also great. However, the issues occur after trying to disconnect from this first connection. Subsequent connections to the nRF Connect iOS app fail, as I mentioned. And although subsequent connections to my custom UART CLI code succeed perfectly on the UART side, this connection no longer supports the HIDS mouse functionality...since my disconnection is failing, that first connection with HIDS isnt cleaned up properly, so the second time around fails. The device runs, but the HIDS mouse code experiences the "Connection Security Failure, error 133" from my original post.

    Are there any direct resources to help me learn how to properly understand which characteristics I am getting automatically subscribed to? Any resources out there for properly disconnecting from a HIDS + UART hybrid app?


    EDIT: Adding the output from my Bleak BLE client connecting to my nRF52 custom device. I queried the device client for all services, and these are the only ones showing. Im curious why I cannot see anything related to HIDS mouse services/characteristics? Could it be there is some separate GATT server I need to query? I just want to find the subscriptions I need to unsubscribe from....

    Service Description:  Nordic Semiconductor ASA

    Service UUID:  0000fe59-0000-1000-8000-00805f9b34fb

            Characteristic Description:  Buttonless DFU

            Characteristic UUID:  8ec90003-f315-4f60-9fb8-838830daea50

    Service Description:  Nordic UART Service

    Service UUID:  6e400001-b5a3-f393-e0a9-e50e24dcca9e

            Characteristic Description:  Nordic UART RX

            Characteristic UUID:  6e400002-b5a3-f393-e0a9-e50e24dcca9e

            Characteristic Description:  Nordic UART TX

            Characteristic UUID:  6e400003-b5a3-f393-e0a9-e50e24dcca9e

    Service Description:  Device Information

    Service UUID:  0000180a-0000-1000-8000-00805f9b34fb

            Characteristic Description:  Manufacturer Name String

            Characteristic UUID:  00002a29-0000-1000-8000-00805f9b34fb

            Characteristic Description:  PnP ID

            Characteristic UUID:  00002a50-0000-1000-8000-00805f9b34fb

    Service Description:  Battery Service

    Service UUID:  0000180f-0000-1000-8000-00805f9b34fb

            Characteristic Description:  Battery Level

            Characteristic UUID:  00002a19-0000-1000-8000-00805f9b34fb



  • Just to clear up a few things:

    cor10 said:
    Previously, I have been able to disconnect from the UART characteristics we were subscribed to with no issues

    You don't disconnect from a service. A device is either connected or disconnected. 

    cor10 said:
    when I run the old "Disconnect" routine from my custom CLI code, the device no longer disconnects, aka the BLE_APP_EVT_DISCONNECTED event in the ble_evt_handler() is no longer triggered

    So what does your disconnect routine do? Do you call sd_ble_gap_disconnect() from your nRF Application? If so, what does it return?

    cor10 said:
    However, the issues occur after trying to disconnect from this first connection. Subsequent connections to the nRF Connect iOS app fail, as I mentioned

    I guess that this is because one side (either nRF chip or connected device (mobile? PC?) still has bonding information stored, while the other does not. If you intend to have bonding enabled (which may be a good idea, if it is a hid device), then I suggest you try to figure out the issues that you have when you connect. 

    I think this is far from one of our standard samples, if you have combined the ble_app_uart with a HID device, so I don't know how you expect the device to work, and what that isn't working as you expect it to. 

    To answer your initial question once more:

    What is your central? (What do you connect the nRF52 chip to?)

    It looks like this device has stored some bonding keys, while the nRF has not. Why do you erase the bonds? You say that your connection doesn't work otherwise? Did you test this properly? Try erasing the bonds on both sides before testing this.

    Best regards,

    Edvin

  • Hi Edvin, thanks for getting back to me on this. I appreciate you clearing things up with some of my bluetooth fundamentals. And yea, this is definitely straying a bit away from the very standard examples in the nrf sdk, so the expected/desired behaviors are strange. All I want out of my device is to connect when we want (for both the real-time UART data stream, and HIDS mouse control), and then to FULLY DISCONNECT when we want (this means no more UART data stream, no more HIDS mouse movement, no more connection to the central at all). I have found that with the HIDS example and the new bonding and advertising code that comes with it, our device wants to stay connected to the central, even though we are running the exact same disconnection code from before (when it was UART without the HIDS).

    Our previous disconnection worked by largely relying on the built-in disconnect() method that comes with the Bleak BLE library we are using. We also unsubscribe from a UART characteristic right before the Bleak disconnect() method, but from what youre saying, this 'unsubscription' is not critical for the disconnect, so that was my mistake for thinking that was more important. Here is that code:

    async def disconnect(device_client: ble.BleClient) -> ble.BleClient:
        if device_client is None:
            return None
        device_client = await _unsubscribe(device_client, TX_CHARACTERISTIC)
        await device_client.disconnect()
        return device_client

    In this example, the device_client is a Bleak BLE client. If a device is either connected, or disconnected, then perhaps this Bleak BLE library doesnt expect this disconnect() method to be used on a HIDS mouse and so it isnt working anymore? As I mentioned above, when we run this disconnect(), although no disconnection occurs, we do see a peer manager event get handled. Should I try and catch that event (which is triggered by our disconnect() method), and simply call sd_ble_gap_disconnect() as you mention? Or what would be the best practice for me to properly trigger this device disconnection?

    Thank you so much for your time with this. And to answer your question, my central is my MacBookPro 2019. I have been going to my bluetooth settings and forcibly removing (not just disconnecting, but removing entirely) the ble device after each test, so I was hoping that this would remove the bonds. I do the same thing on my iPhone before connecting to the device again via the nRF Connect app. Is this BLE device removal not sufficient? If not, I dont know what options I will have for my iPhone, but it seems like the Bleak BLE library supports an unpair() function that might help?

  • This snippet is not something we (Nordic) have written. This is C++, right?

    So what does device_client.disconnect() do? does it return anything? And where is this application running? On the nRF or the computer?

    cor10 said:
    If not, I dont know what options I will have for my iPhone, but it seems like the Bleak BLE library supports an unpair() function that might help?

    I am not familiar with OSx, but on an iPhone, the "forget this device" will remove bonding information.

    What device_client.disconnect() does, and if it doesn't work the way you expect it to, you need to discuss this with the author of this library. You can disconnect from the nRFs side using sd_ble_gap_disconnect(), but it should be possible to disconnect from the central side as well.

    Best regards,

    Edvin

1 2