Electronic – arduino – Modifying Bluetooth HID Descriptor from 2 to 4 axis gamepad – Arduino Nano BLE

arduinobluetoothhid

I am trying to modify an existing Arduino library (https://github.com/tcoppex/mbed-ble-hid go to src/services and its the HIDgamepadservice C++ and H files) to make the HID gamepad 16 button and 4 axis. I managed to get 16 button working by modifying the HID descriptor as so from the original found in github and modifying the header file with the appropriate buttons in the enum Button

      // Buttons
  USAGE_PAGE(1),      0x09,       // Usage Page (Buttons)
  USAGE_MINIMUM(1),   0x01,       // Usage Minimum (1)
  USAGE_MAXIMUM(1),   0x10,       // Usage Maximum (16)
  LOGICAL_MINIMUM(1), 0x00,       // Logical Minimum (0)
  LOGICAL_MAXIMUM(1), 0x01,       // Logical Maximum (1)
  REPORT_COUNT(1),    0x10,       // Report Count (16)
  REPORT_SIZE(1),     0x01,       // Report Size (1)
  INPUT(1),           0x02,       // Input (Data, Variable, Absolute)

It shows up in bluetooth as a gamepad, and if I go to joy.cpl I see 2 axis 4 button controller but inside the properties it extrapolates out to 16 buttons. Excellent.

For 4 axis, I tried the following modification to the HID descriptor:

      USAGE_PAGE(1),      0x01,       // Usage Page (Generic Desktop)
  USAGE(1),           0x30,       // Usage (X)
  USAGE(1),           0x31,       // Usage (Y)
  USAGE(1),           0x32,       // Usage (Z)
  USAGE(1),           0x33,       // Usage (RX)
  REPORT_SIZE(1),     0x08,       // Report Size (8)
  REPORT_COUNT(1),    0x04,       // Report Count (4)
  INPUT(1),           0x02,       // Input (Data, Variable, Absolute)

I also modified the structure to include the extra floats for the two new axis and updated the HIDGamepadService::motion function to include the two extra variables as defined.

// Input Report
#pragma pack(push, 1)
struct {
  uint8_t x;
  uint8_t y;
  uint8_t z;
  uint8_t rx;
  uint8_t buttons;
} hid_input_report;


void HIDGamepadService::motion(float fx, float fy, float fz, float frx) {
  uint8_t x = static_cast<int>(0x100 + fx * 0x7f) & 0xff;
  uint8_t y = static_cast<int>(0x100 + fy * 0x7f) & 0xff;
  uint8_t z = static_cast<int>(0x100 + fz * 0x7f) & 0xff;
  uint8_t rx = static_cast<int>(0x100 + frx * 0x7f) & 0xff;
  hid_input_report.x = x;
  hid_input_report.y = y;
  hid_input_report.z = z;
  hid_input_report.rx = rx;
}

void motion(float fx, float fy, float fz, float frx);

Whenever I deviate from 2 axis, if I connect via bluetooth I get a "driver error" in bluetooth devices menu. There is no game controller in joy.cpl. I'm guessing something in my HID descriptor it doesn't like and i've been having trouble finding a good resource on how to modify it. I also reached out to the library creator 3-4 days ago but no response.

Thanks in advance for any advice

Here is the full HID descriptor I am trying to use

// Report Map
static uint8_t hid_report_map[] =
{
  USAGE_PAGE(1),      0x01,       // Usage Page (Generic Desktop)
  USAGE(1),           0x05,       // Usage (Game Pad)
  COLLECTION(1),      0x01,       // Collection (Application)
    USAGE(1),           0x01,       // Usage (Pointer)
    COLLECTION(1),      0x00,       // Collection (Physical)
      USAGE_PAGE(1),      0x01,       // Usage Page (Generic Desktop)
      USAGE(1),           0x30,       // Usage (X)
      USAGE(1),           0x31,       // Usage (Y)
      USAGE(1),           0x32,       // Usage (Z)
      USAGE(1),           0x33,       // Usage (RX)
      REPORT_SIZE(1),     0x10,       // Report Size (16)
      REPORT_COUNT(1),    0x04,       // Report Count (4)
      INPUT(1),           0x02,       // Input (Data, Variable, Absolute)

      // Buttons
      USAGE_PAGE(1),      0x09,       // Usage Page (Buttons)
      USAGE_MINIMUM(1),   0x01,       // Usage Minimum (1)
      USAGE_MAXIMUM(1),   0x10,       // Usage Maximum (16)
      LOGICAL_MINIMUM(1), 0x00,       // Logical Minimum (0)
      LOGICAL_MAXIMUM(1), 0x01,       // Logical Maximum (1)
      REPORT_COUNT(1),    0x10,       // Report Count (16)
      REPORT_SIZE(1),     0x01,       // Report Size (1)
      INPUT(1),           0x02,       // Input (Data, Variable, Absolute)

      
    END_COLLECTION(0),              // End Collection (Physical)
  END_COLLECTION(0),              // End Collection (Application)
};

Best Answer

I figured it out. Needs to be a joystick so modifying the game pad identifier 0x05 to a joystick 0x04 did the trick. I now have 4 axis and 16 buttons.