Multiple HID Descriptors Leonardo R3

Hello everyone. Pretty recently I got my first Arduino, the Leonardo R3. Im not very experienced with this so I might get some things wrong, just a heads up. Im trying to create a "mouse" from the Arduino but with multiple custom HID descriptors. This way I could create things like macros that exists in a lot of modern mice.

So obviously im using the Mouse library for the mouse movement. This is basically how im trying to add these HID descriptors. The HID descriptor in there is just a temporary one to showcase.

CustomHID.h

#ifndef CustomHID_h
#define CustomHID_h
#include "Arduino.h"

// make sure that it is a supported Architecture
//#ifndef ARDUINO_ARCH_AVR
//#error "Unsupported Architecture"
//#endif

#include "PluggableUSB.h"
#include "HID.h"

#define CustomHID_D_HIDREPORT(length) { 9, 0x21, 0x11, 0x01, 0, 1, 0x22, lowByte(length), highByte(length) }
typedef struct {
  InterfaceDescriptor hid;
  HIDDescDescriptor desc;
  EndpointDescriptor in;
  EndpointDescriptor out;
} CustomHIDHIDDescriptor;
typedef union {
  uint8_t dataInHeader[0];
  uint8_t dataInBlock[0];
  uint8_t dataOutHeader[0];
  uint8_t dataOutBlock[0];
  struct {
    uint8_t dataIn;
    uint8_t dataOut;
  };
} CustomHIDDataPacket;

static const uint8_t CustomHIDReportDescriptor[] PROGMEM = {
  //Custom
  // Vendor Specific Usage Page Collection (Report ID 16)
  0x06, 0x00, 0xFF,              // Usage Page (Vendor Defined 0xFF00)
  0x09, 0x01,                    // Usage (0x01)
  0xA1, 0x01,                    // Collection (Application)
    0x85, 0x10,                  //   Report ID (16)
    0x95, 0x06,                  //   Report Count (6)
    0x75, 0x08,                  //   Report Size (8)
    0x15, 0x00,                  //   Logical Minimum (0)
    0x26, 0xFF, 0x00,            //   Logical Maximum (255)
    0x09, 0x01,                  //   Usage (0x01)
    0x81, 0x00,                  //   Input (Data,Array,Abs)
    0x09, 0x01,                  //   Usage (0x01)
    0x91, 0x00,                  //   Output (Data,Array,Abs)
  0xC0,                          // End Collection

  // Vendor Specific Usage Page Collection (Report ID 17)
  0x06, 0x00, 0xFF,              // Usage Page (Vendor Defined 0xFF00)
  0x09, 0x02,                    // Usage (0x02)
  0xA1, 0x01,                    // Collection (Application)
    0x85, 0x11,                  //   Report ID (17)
    0x95, 0x13,                  //   Report Count (19)
    0x75, 0x08,                  //   Report Size (8)
    0x15, 0x00,                  //   Logical Minimum (0)
    0x26, 0xFF, 0x00,            //   Logical Maximum (255)
    0x09, 0x02,                  //   Usage (0x02)
    0x81, 0x00,                  //   Input (Data,Array,Abs)
    0x09, 0x02,                  //   Usage (0x02)
    0x91, 0x00,                  //   Output (Data,Array,Abs)
  0xC0                           // End Collection
};

#define CustomHIDInterface pluggedInterface
#define CustomHIDEndpointIn pluggedEndpoint
#define CustomHIDEndpointOut (pluggedEndpoint + 1)
#define CustomHIDTX CustomHIDEndpointIn
#define CustomHIDRX CustomHIDEndpointOut

class CustomHID : public PluggableUSBModule {
public:
  CustomHID();
private:

protected:
  uint8_t endpointTypes[2];
  uint8_t protocol;
  uint8_t idle;

  int getInterface(uint8_t *interfaceNumber);
  int getDescriptor(USBSetup &setup);
  bool setup(USBSetup &setup);
};

#endif

CustomHID.cpp

#include "CustomHID.h"
#include "Arduino.h"
 
CustomHID::CustomHID() : PluggableUSBModule(2, 1, endpointTypes) {
  endpointTypes[0] = EP_TYPE_BULK_IN;
  endpointTypes[1] = EP_TYPE_INTERRUPT_OUT;
  PluggableUSB().plug(this);
}
 
int CustomHID::getInterface(uint8_t *interfaceNumber) {
  interfaceNumber[0] += 1;
  CustomHIDHIDDescriptor interfaceDescriptor = {
    D_INTERFACE(CustomHIDInterface, 2, USB_DEVICE_CLASS_HUMAN_INTERFACE, 0, 0),
    CustomHID_D_HIDREPORT(sizeof(CustomHIDReportDescriptor)),
    D_ENDPOINT(USB_ENDPOINT_IN(CustomHIDEndpointIn), USB_ENDPOINT_TYPE_BULK, USB_EP_SIZE, 0),
    D_ENDPOINT(USB_ENDPOINT_OUT(CustomHIDEndpointOut), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0),
  };
  return USB_SendControl(0, &interfaceDescriptor, sizeof(interfaceDescriptor));
}
 
int CustomHID::getDescriptor(USBSetup &setup) {
  // code copied and modified from NicoHood's HID-Project
  // check if it is a HID class Descriptor request
  if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; }
  if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { return 0; }
 
  // In a HID Class Descriptor wIndex cointains the interface number
  if (setup.wIndex != pluggedInterface) { return 0; }
 
  protocol = HID_REPORT_PROTOCOL;
 
  return USB_SendControl(TRANSFER_PGM, CustomHIDReportDescriptor, sizeof(CustomHIDReportDescriptor));
}
 
bool CustomHID::setup(USBSetup &setup) {
  // code copied from NicoHood's HID-Project
  if (pluggedInterface != setup.wIndex) {
    return false;
  }
 
  uint8_t request = setup.bRequest;
  uint8_t requestType = setup.bmRequestType;
 
  if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
  {
    if (request == HID_GET_REPORT) {
      // TODO: HID_GetReport();
      return true;
    }
    if (request == HID_GET_PROTOCOL) {
      // TODO: Send8(protocol);
      return true;
    }
  }
 
  if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE)
  {
    if (request == HID_SET_PROTOCOL) {
      protocol = setup.wValueL;
      return true;
    }
    if (request == HID_SET_IDLE) {
      idle = setup.wValueL;
      return true;
    }
    if (request == HID_SET_REPORT)
    {
    }
  }
 
  return false;
}

It might be a bit weird and containing unnecessary code at the moment, I basically pasted it from a HID library that I found.

This works great and as expected but when I try to use multiple of these HID descriptors its not working, only one of them is being used. Ive tried changing the endpointTypes and the interfaceNumber in the CustomHID2.cpp (my second custom HID descriptor) by doing this:
from:

  endpointTypes[0] = EP_TYPE_BULK_IN;
  endpointTypes[1] = EP_TYPE_INTERRUPT_OUT;

  interfaceNumber[0] += 1;

to:

  endpointTypes[0] = EP_TYPE_BULK_IN + 1;
  endpointTypes[1] = EP_TYPE_INTERRUPT_OUT + 1;

  interfaceNumber[0] += 2;

But it still doesn't work. Please tell me how I can modify the second custom HID descriptor to work as I intend. Thanks in advance!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.