HID Feature Reports over BLE: A serial configuration transport channel.

Hello Nordic Dev Team!

Environment Details

Hardware nRF52840 (dev kit for test, xiao-ble-sense for production)
NCS Version v2.5.0
Host operating system Windows 11 -- though should be OS-independent

Objective

I am working on a wearable computer mouse for people with motor disabilities. I would like to enable configuration of mouse parameters like sensitivity, and many others, over BLE using HID Feature Reports.

We have a local app that can send and receive feature report data, so my main focus is on the NCS Input/Output flow of data.

The end-goal is to be able to have a bi-directional conversation with an application on the Host Computer. We will send a few kB (~2kB) of data to the host when requested -- which we expect to serialize into MTU-size chunks. We will also receive data sent to our device and store it in the device's persistent memory.

Why not just build your own service?

It would be really nice to just have a custom GATT Service to handle this, but we have two goals which make that more complicated.

  1. No local software installation required. We would like our device to work out-of-the-box without extensive setup required on our users' machines.
  2. We would like the configuration process of this mouse to be achievable using only our mouse. Therefore, it has to be connected as an HID Device while it is configured.

To make this happen, we are configuring the device via a Google Chrome Web App. Although Chrome can directly pair a device and access a service, this doesn't allow simultaneous configuration and operation of the device. Both Windows and Mac seem to have some security guardrails that prevent a web browser from connecting to services on a device that is also handling HID input to the host.

Resources

In this section, I list out resources that I've used to build my understanding of the system, both to cite my sources, and to create a resource for future readers of this post

1. [Chrome Dev WebHID] developer.chrome.com/.../webhid
2. DevZone Posts
    1. ["Need an Example" (Bk37)] devzone.nordicsemi.com/.../need-an-hid-feature-report-example-using-zephyr
        1. Points to post #2
    2. ["Zephyr USB Feature reports"] devzone.nordicsemi.com/.../405636
        1. Refer to nRF Desktop
        2. [nRF Desktop Doc Link] docs.nordicsemi.com/.../README.html
        3. ["Hid Report Desc Code File"] docs.nordicsemi.com/.../README.html
    3. ["Need BLE HoG feature report sample"] devzone.nordicsemi.com/.../need-ble-hog-feature-report-sample
        1. This user describes basically exactly what I want to do, says they figured it out, and then didn't say how they achieved their goals.
3. Tools for HID Feature Report Introspection
    1. [HID Feature Report Explorer] nondebug.github.io/.../
        1. Note: must run on chrome
4. Nordic Docs about Config Channel
    1. [nRF Desktop Config Channel Docs] docs.nordicsemi.com/.../config_channel.html
    2. [HID Service Module Doc Page] docs.nordicsemi.com/.../hids.html

Request for Guidance

While I work on an isolated and minimized sample, I'd like to ask for more general guidance about the behavior of the config channel in nRF Desktop.

  • When I connect an nRF Desktop Peripheral with the config channel enabled in the prj.conf, then connect to it via the HID Feature Report Explorer, I am able to connect to the feature report service (which shows up as 1915:xxxx, with xs being either a keyboard or mouse identity, as configured).
  • When I send data, if it begins with 06 06, it is received in the hids.c feature_report_handler method, and I can log the data with a hex-dump, and observe incoming data from the tool.
  • If, after sending that data, I click the "receive" button, I get data back from the device, but I'm confused as to where that information is populated?

Conclusion

My most important questions, while I try to create a more concise and minimized example is this:

What is the correct way to utilize this system in the way I have described?

What resources would you recommend to better understand this system?

Thank you very much for your time and guidance.

- Finn
Parents
  • So the takeaway from your post is that you want to send data to a computer, without the need of any special drivers or applications installed on that computer. And you found some information on the Chrome web app, that uses a HID-interface. Is that right?

    Just to take a step back, you could use a custom service/characteristic, which would probably be the easiest way to go from a BLE perspective, but that would indeed need something on the computer side to interpret the custom service. 

    This user describes basically exactly what I want to do, says they figured it out, and then didn't say how they achieved their goals.

    I believe he just used the feature report field and packed it with custom data, and then his application on the central device just grabbed that custom data and interpreted it. But that is just guessing. 

    As for the HID protocol, we don't much other than what is provided by Zephyr and the HID samples. I never got to actually understand how these HID tables are built up, and how to parse them, but there are online resources that describe them (none from us, I believe). 

    Best regards,

    Edvin

  • Hi Edvin,

    you want to send data to a computer, without the need of any special drivers or applications installed on that computer

    That's exactly right, and I think I've made a reasonable implementation. I'll attach that either to this post or to a subsequent update once it's a little less tangled up with code I don't want to post publicly yet.

    We're building a lot of atypical features, and trying to be careful about how we do it. Our company (Auli Tech), is building a mouse for people with motor impairments. Since that group cannot easily push buttons on the outside of the device, it is important that all configuration is digitized.

    A few months ago, we did implement a custom service for passing our configuration data into and out of a different web app. The problem is that, for what I assume are security (e.g. keylogger) concerns, it is not possible to subscribe to a custom service while a device is being used as an HID input. Thus, we had to discard our wireless config that was built on top of the Nordic UART Service (NUS).

    That NUS adaptation also served as a sensor stream that we use to capture IMU data in conjunction with a third custom web app that we use to train a machine learning model for recognizing user gestures. Those custom gestures are then used to trigger mouse clicks.

    We may end up utilizing the feature-report "config transport channel" to stream sensor data, so it will be interesting to see what kind of throughput is possible in terms of data rate.

    In case someone bumps into this thread down the line and also wants to know how the feature report gets linked to subsystem / how they are populated on request -- here's as far as I traced.

    • in hids.c, the feature_report_handler is added to a struct bt_hids_outp_feat_rep called hids_init_param
    • hids_init_param gets passed to subsystem using hids_init as the return statement of module_init
    • This takes us into nrf/subsys/bluetooth/services/hids.c, where (line 909, ncs v2.5.0) hids_feat_report_register adds the container of the feature_report_handler to the hids_obj
    • within hids_feat_report_register, a macro called "BT_GATT_POOL_CHRC" links a read and write callback.

    I ran into the problem that sometimes data does not get sent or received if the host sends too few bytes, but that's easily worked around by filling up to maximum size with 0x00 bytes.

    Thanks for getting back to me Edvin, I appreciate the pointers. The HID Tables in the NCS samples serve their purpose well.

    Readings I found helpful were:

    Exercises I found helpful:

    Kind regards,

        - Finn

Reply
  • Hi Edvin,

    you want to send data to a computer, without the need of any special drivers or applications installed on that computer

    That's exactly right, and I think I've made a reasonable implementation. I'll attach that either to this post or to a subsequent update once it's a little less tangled up with code I don't want to post publicly yet.

    We're building a lot of atypical features, and trying to be careful about how we do it. Our company (Auli Tech), is building a mouse for people with motor impairments. Since that group cannot easily push buttons on the outside of the device, it is important that all configuration is digitized.

    A few months ago, we did implement a custom service for passing our configuration data into and out of a different web app. The problem is that, for what I assume are security (e.g. keylogger) concerns, it is not possible to subscribe to a custom service while a device is being used as an HID input. Thus, we had to discard our wireless config that was built on top of the Nordic UART Service (NUS).

    That NUS adaptation also served as a sensor stream that we use to capture IMU data in conjunction with a third custom web app that we use to train a machine learning model for recognizing user gestures. Those custom gestures are then used to trigger mouse clicks.

    We may end up utilizing the feature-report "config transport channel" to stream sensor data, so it will be interesting to see what kind of throughput is possible in terms of data rate.

    In case someone bumps into this thread down the line and also wants to know how the feature report gets linked to subsystem / how they are populated on request -- here's as far as I traced.

    • in hids.c, the feature_report_handler is added to a struct bt_hids_outp_feat_rep called hids_init_param
    • hids_init_param gets passed to subsystem using hids_init as the return statement of module_init
    • This takes us into nrf/subsys/bluetooth/services/hids.c, where (line 909, ncs v2.5.0) hids_feat_report_register adds the container of the feature_report_handler to the hids_obj
    • within hids_feat_report_register, a macro called "BT_GATT_POOL_CHRC" links a read and write callback.

    I ran into the problem that sometimes data does not get sent or received if the host sends too few bytes, but that's easily worked around by filling up to maximum size with 0x00 bytes.

    Thanks for getting back to me Edvin, I appreciate the pointers. The HID Tables in the NCS samples serve their purpose well.

    Readings I found helpful were:

    Exercises I found helpful:

    Kind regards,

        - Finn

Children
No Data
Related