BLE HID Haptic Custom Hardware Support?

Greetings …

I am building a few custom BLE HID joysticks/gamepads https://gitlab.com/leet/diy-gamepad

and have been watching the increasing support for haptic feedback in the PS/XBox/SwitchPro controllers and was hoping I might be able to do something similar in my hardware, but need info on the BLE HID interface.

Please could anybody point me at any good resources or if SDL has a standard that is supported (that would be awesome).

Hoping with my basic hardware and software development skills I can hack something together.

Thanks
LeeT

HID is a big spec.
You can download it at USB website:

  • The Device Class Definition for HID 1.11 is intended to supplement the USB Specification and provide HID manufacturers with the information necessary to build USB-compatible devices. It also specifies how the HID class driver should extract data from USB devices.

  • The HID Usage Tables 1.21 document defines constants that can be interpreted by an application to identify the purpose and meaning of a data field in a HID report.

Bluetooth HID is based on USB HID but with some specific changes. You can download it on Bluetooth website.

  • Human Interface Device Profile 1.1.1
    This profile defines how devices with Bluetooth wireless communications can use the HID Specification initially to discover the feature set of a Bluetooth HID device, and then communicate with the Bluetooth HID device. This profile further defines how a device with Bluetooth wireless communications can support HID services over the Bluetooth protocol stack using the Logical Link Control and Adaptation Protocol (L2CAP) layer.

Also there is another spec for BLE:

  • HID over GATT Profile Specification
    This profile defines how a device with Bluetooth low energy wireless communications can support HID services over the Bluetooth low energy protocol stack using the Generic Attribute Profile.

For force feedback that is recognized by DirectInput (that is used as haptic backed in SDL on Windows) you need to look at Physical Interface Device Page (0x0F).

Here is description of it:

  • The Device Class Definition for PID 1.0 provides information for the development of Physical Interface Devices. These devices include force feedback joysticks, steering wheels, etc. It allows peripheral and driver developers to use a common set of HID report descriptors, device usages and reports to describe the characteristics of a PID class device.

Here you can find example Hid Report Descriptor that I dumped from Xbox One Controller Windows driver (xboxgip.sys).
But In practice only a few controllers are using 0x0F and doing vendor-specific requests instead…

Thanks @DJm00n The Specs are very detailed, but just a little outside of my wheelhouse. I think I am going to try and clone src/joystick/hidapi/SDL_hidapi_xbox360.c and make a generic device and hack my way through the code and hardware. I will post any progress I make, maybe somebody else is interested too.

Thanks
LeeT

@leet I don’t recommend you to look at xbox360 driver since those controllers are not really HID controllers. Xbox 360 used proprietary XUSB protocol for communication. Xbox One controllers used GIP protocol (but newer X1 controllers have ability to use HID over Bluetooth - for easier connection with phones, TV etc).

Its funny because Microsoft - its who have invented HID protocol :slight_smile:

PS4/PS5 controllers are true HID controllers (but without haptics described in their HID Report Descriptor - so full packet format was reverse-engineered).

Also almost all “DirectInput controllers” that currently sold are in fact HID controllers. Because HID is a main DirectInput backend and all other older protocols was removed (Gameport controllers, etc).

Please note that xusb.sys and xboxgip.sys drivers on Windows emulates HID controller and thus showing on this screen:
image image
But that HID interface have serious flaws: no rumble support, LT/RT as one axis.

Thanks @DJm00n for all the info on the XBox controllers.

I currently don’t have Windows, so this will not be my first testing platform. Linux or OSX for testing and possible even Android.

I did look at the HIDAPI drivers this weekend and saw that the XBox code is way bigger and has multiple drivers vs the other drivers, so I have to agree with you here.

Now thinking that joystick/hidapi/SDL_hidapi_ps4.c or possible joystick/hidapi/SDL_hidapi_gamecube.c is a better place to start with, thou I am not sure that I totally have a grasp on code, but … ;-).

My hardware is using ESP32 microcontroller, which will be over Bluetooth or HID over BLE, which for the basic gamepad/controller is working. Adding haptic feedback is my goal, but not yet been able to work out the LED feedback yet, so I might be set on load road ahead.

I was hoping that I could find a good open implementation to work into my project. Seems this is still system in progress.

But that HID interface have serious flaws: no rumble support, LT/RT as one axis.
Are you saying that HID implementation is flawed or just the MS implementation is flawed? As my working project has LT/RT axes are disconnected, so I don’t think the HID implementation is flawed.

Thanks
LeeT

Are you saying that HID implementation is flawed or just the MS implementation is flawed? As my working project has LT/RT axes are disconnected, so I don’t think the HID implementation is flawed.

Microsoft XUSB and GIP mapping to HID mapping is flawed. https://docs.microsoft.com/windows/win32/xinput/directinput-and-xusb-devices

1 Like

Here is HID report descriptor that I dumped from Stadia Controller Model H2B connected via USB.
Take note on PID Page (0x0F) and DC Enable Actuators Usage (0x97) part with two 16 bit values - in its output report. You need to provide such descriptor in your controller for rumble support. Then it will be a chance that it will be used by SDL/DirectInput/whatever.

0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x05,        // Usage (Game Pad)
0xA1, 0x01,        // Collection (Application)
0x85, 0x03,        //   Report ID (3)
0x05, 0x01,        //   Usage Page (Generic Desktop Ctrls)
0x75, 0x04,        //   Report Size (4)
0x95, 0x01,        //   Report Count (1)
0x25, 0x07,        //   Logical Maximum (7)
0x46, 0x3B, 0x01,  //   Physical Maximum (315)
0x65, 0x14,        //   Unit (System: English Rotation, Length: Centimeter)
0x09, 0x39,        //   Usage (Hat switch)
0x81, 0x42,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x45, 0x00,        //   Physical Maximum (0)
0x65, 0x00,        //   Unit (None)
0x75, 0x01,        //   Report Size (1)
0x95, 0x04,        //   Report Count (4)
0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x09,        //   Usage Page (Button)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x01,        //   Logical Maximum (1)
0x75, 0x01,        //   Report Size (1)
0x95, 0x0F,        //   Report Count (15)
0x09, 0x12,        //   Usage (0x12)
0x09, 0x11,        //   Usage (0x11)
0x09, 0x14,        //   Usage (0x14)
0x09, 0x13,        //   Usage (0x13)
0x09, 0x0D,        //   Usage (0x0D)
0x09, 0x0C,        //   Usage (0x0C)
0x09, 0x0B,        //   Usage (0x0B)
0x09, 0x0F,        //   Usage (0x0F)
0x09, 0x0E,        //   Usage (0x0E)
0x09, 0x08,        //   Usage (0x08)
0x09, 0x07,        //   Usage (0x07)
0x09, 0x05,        //   Usage (0x05)
0x09, 0x04,        //   Usage (0x04)
0x09, 0x02,        //   Usage (0x02)
0x09, 0x01,        //   Usage (0x01)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01,        //   Report Size (1)
0x95, 0x01,        //   Report Count (1)
0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01,        //   Usage Page (Generic Desktop Ctrls)
0x15, 0x01,        //   Logical Minimum (1)
0x26, 0xFF, 0x00,  //   Logical Maximum (255)
0x09, 0x01,        //   Usage (Pointer)
0xA1, 0x00,        //   Collection (Physical)
0x09, 0x30,        //     Usage (X)
0x09, 0x31,        //     Usage (Y)
0x75, 0x08,        //     Report Size (8)
0x95, 0x02,        //     Report Count (2)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0x09, 0x01,        //   Usage (Pointer)
0xA1, 0x00,        //   Collection (Physical)
0x09, 0x32,        //     Usage (Z)
0x09, 0x35,        //     Usage (Rz)
0x75, 0x08,        //     Report Size (8)
0x95, 0x02,        //     Report Count (2)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0x05, 0x02,        //   Usage Page (Sim Ctrls)
0x75, 0x08,        //   Report Size (8)
0x95, 0x02,        //   Report Count (2)
0x15, 0x00,        //   Logical Minimum (0)
0x26, 0xFF, 0x00,  //   Logical Maximum (255)
0x09, 0xC5,        //   Usage (Brake)
0x09, 0xC4,        //   Usage (Accelerator)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x85, 0x05,        //   Report ID (5)
0x06, 0x0F, 0x00,  //   Usage Page (PID Page)
0x09, 0x97,        //   Usage (0x97)
0x75, 0x10,        //   Report Size (16)
0x95, 0x02,        //   Report Count (2)
0x27, 0xFF, 0xFF, 0x00, 0x00,  //   Logical Maximum (65534)
0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0,              // End Collection

// 156 bytes

Greetings …

So after much cloning and coping & pasting my way through quite a few different projects, I think I have gone as far as I can.

I was thinking what might be easier to get right, was the Player Number feedback, like the old PS3 player indicator.

Tested T-vK/ESP32-BLE-Keyboard LED feedback support, so that I knew this could work, I did a fork and with GitHub - LeeNX/ESP32-BLE-Gamepad at PlayerLEDs.

but I could not find a way to send feedback to BLE device. I was thinking I might be able to use the HIDAPI and send feedback, but got lost here. I would not connect to my ESP32 BLE device.

I did think I could try and clone SDL and do some hacking GitHub - LeeNX/SDL-mirror at generic, but I could not get testrumble.c nor testhaptic.c to detect my device nor my PS4 controller?

Thou testgamecontroller.c did work with the PS4 controller lightbar and rumblers? Might just jack the code and test further.

Does anybody have suggestions for testing?

Thanks

Greetings, after some time …

Further research and hacking, I am at point that I hope I can get feedback and possible help.
BLE/HID gamepad section is not generalised or uses GATT Services/Characteristics, at least not that I understand.

My fork of ESP32-BLE-Gamepad - ESP32-BLE-Gamepad/README.md at test-led · LeeNX/ESP32-BLE-Gamepad · GitHub

has added code/hacking, that includes attributes / Characteristic like Serial Number String and Firmware Revision String, but don’t see how to expose this in SDL. Any suggestions?

Would it be worth listing a standard types of attribs and their usage? PlayerID or LED Bar?
I have setup HID Report Descriptor, as follows

// 0x95, 0x01,   /*   REPORT_COUNT (1) */? 8
// 0x75, 0x08,   /*   REPORT_SIZE (8 */? 1
// 0x05, 0x08,   /*   USAGE_PAGE (LEDs) */
// 0x19, 0x61,   /*   USAGE_MINIMUM () - 0x61 Player 1*/
// 0x29, 0x68,   /*   USAGE_MAXIMUM () - 0x68 Player 8 */
// 0x15, 0x00,   /*   LOGICAL_MINIMUM (0) */
// 0x25, 0x01,   /*   LOGICAL_MAXIMUM (1) */
// 0x91, 0x02,   /*   OUTPUT (Data,Var,Abs) */

And able to write to gamepad, to control the LEDS

sudo ./hidapitester --vidpid E502:ABCD -l 2 --open --send-output 3,5
Opening device, vid/pid: 0xE502/0xABCD
Writing output report of 2-bytes...wrote 2 bytes:
 03 05
Closing device

While monitoring the gamepad

18:00:12.531 -> D NimBLECharacteristic: Characteristic 0x2a4d Write event
18:00:12.531 -> D NimBLECharacteristic: >> setValue: length=1, data=05, characteristic UUID=0x2a4d
18:00:12.531 -> D NimBLECharacteristic: << setValue
18:00:12.531 -> [7877892][I][BleGamepadOutputCallbacks.cpp:19] onWrite(): [BLEGamepad] leds: 5
18:00:12.531 -> [7877899][I][BleGamepadOutputCallbacks.cpp:20] onWrite(): [BLEGamepad] Callbacks written to
18:00:12.568 -> D NimBLECharacteristicCallbacks: onWrite: default

Now to get libSDL to use this. If anybody can assist, please feel free to poke at me.

Thanks
LeeT