Arduino micro as (raw) HID device

Hi Everyone!

I just got my Arduino Micro, I played a bit with the USB options. Made it into a usb keyboard :slight_smile: and I saw there is also a mouse class to make it into a mouse. But how about setting it up as a gamepad or even a raw hid device? How can I make it into other usb devices? Can I just write a library or are the mouse and keyboard built into the boatloader?

If the mouse and keyboard code are in the boatloader, can I just download the code for it change it and recompile?

By the way, I have knowledge of c/c++ and some basic assembly knowledge. So I don't mind writing some code.

They are not in the bootloader. The bootloader loads your new code.

The HID stuff is in the loaded code.

Find the file HID.cpp and look inside that.

Thanks, yeah I just noticed the HID.cpp file.

As I understand it in theory it is possible but there are a few snags and no one has done it yet.

Yeah it is possible, I had to make some changes to the HID.cpp file. Now the Arduino can be a gamepad too. XD

I got a working joystick and some buttons. Now I am looking if I can somehow add analog triggers. But I am not sure if those are supported on a generic hid gamepad. I can't seem to get the pc to recognize them.

Hi

It's been a while since anything had been posted in this topic but if you didn't figure it out already, i have it working on my Leonardo.
My hid descriptor for my gamepad/joystick (hid.cpp)

    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x04,                    // USAGE (Joystick)
    0xa1, 0x01,                    // COLLECTION (Application)
		0x85, 0x01,                    //     REPORT_ID (1) // change to 3 if using mouse and keyboard on 1&2
		0xa1, 0x00,                    //   COLLECTION (Physical)
			
			0x05, 0x09,                    //     USAGE_PAGE (Button)
			0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
			0x29, 0x10,                    //     USAGE_MAXIMUM (Button 16)
			0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
			0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
			0x95, 0x10,                    //     REPORT_COUNT (16)
			0x75, 0x01,                    //     REPORT_SIZE (1)
			0x81, 0x02,                    //     INPUT (Data,Var,Abs)

			0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
			0x09, 0x01,                    // USAGE (piointer)
			0xa1, 0x00,                    //   COLLECTION (Physical)	
				0x09, 0x30,                    //     USAGE (X)
				0x09, 0x31,                    //     USAGE (Y)
				0x09, 0x32,                    //     USAGE (Z)
				0x09, 0x33,                    //     USAGE (Rx)
				0x09, 0x34,                    //     USAGE (Ry)
				0x09, 0x35,                    //     USAGE (Rz)
				0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
				0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
				0x95, 0x06,                    //     REPORT_COUNT (6)
				0x75, 0x08,                    //     REPORT_SIZE (8)
				0x81, 0x02,                    //     INPUT (Data,Var,Abs)
			0xc0,                          //   END_COLLECTION

			0x05, 0x01,                     // Usage Page (Generic Desktop)
			0x09, 0x39,                     // Usage (Hat switch)
			0x15, 0x00,                     // Logical Minimum (0)
			0x25, 0x07,                     // Logical Maximum (7)
			0x35, 0x00,                     // Physical Minimum (0)
			0x46, 0x3B, 0x01,               // Physical Maximum (315)
			0x75, 0x04,                     // Report Size (4)
			0x95, 0x01,                     // Report Count (1
			0x65, 0x14,                     // Unit (20)
			0x81, 0x42,                     // Input (variable,absolute,null_state)
				// 4bits of padding
			0x95, 0x04,                    //     REPORT_COUNT (4)
			0x75, 0x01,                    //     REPORT_SIZE (1)
			0x81, 0x01,                    //     INPUT (Data,Var,Abs)
			
		0xc0,                          //   END_COLLECTION
    0xc0,                           // END_COLLECTION

16 buttons, 6 axis (8bit but can be changed easily ), 1hat(d-pad)

and the corresponding structure (in USBAPI.h):

typedef struct {
	union 
	{
		struct {
			bool	b0: 1 ;
			bool	b1: 1 ;
			bool	b2: 1 ;
			bool	b3: 1 ;
			bool	b4: 1 ;
			bool	b5: 1 ;
			bool	b6: 1 ;
			bool	b7: 1 ; 
			bool	b8: 1 ;
			bool	b9: 1 ;
			bool	b10: 1 ;
			bool	b11: 1 ;
			bool	b12: 1 ;
			bool	b13: 1 ;
			bool	b14: 1 ;
			bool	b15: 1 ; 
		} ;
		uint16_t buttons;
	};
	int8_t X	;
	int8_t Y	;
	int8_t Z	;
	int8_t Rx	;
	int8_t Ry	;
	int8_t Rz	;
	
	int8_t	hat: 4 ;  // -1 = neutral position
	int8_t	: 4 ;	  //padding  
	
} GamepadReport;

no we can send the data

GamepadReport _GamepadReport;
_GamepadReport.hat=-1; // default nullstate
_GamepadReport.X=random(-127,127);// just a test
_GamepadReport.buttons=random(0,0xff);// just a test random on first 8 bits(buttons)
_GamepadReport.b9=1;// just
// or do whatever
// and send
HID_SendReport(1,&_GamepadReport ,sizeof(GamepadReport )); //1 coresponds to report id in HID.cpp

or write a simple class

USBAPI.h

class Gamepad_
{
private:
	uint8_t rapid;
public:
	GamepadReport _GamepadReport;
	Gamepad_(void);
	void begin(uint8_t id);
	void end(void);
	void send();
};
extern Gamepad_ Gamepad;

HID.cpp:
Gamepad_::Gamepad_(void){		rapid=3;              }
void Gamepad_::begin(uint8_t sid){	rapid=sid;  }
void Gamepad_::end(void){}
void Gamepad_::send(){
	HID_SendReport(rapid,&_GamepadReport,sizeof(GamepadReport));
}

and in you sketch:

before setup():
  Gamepad_ Gamepad;

in setup():
  Gamepad.begin(1);//report_id of your HID 

and in loop():
  Gamepad._GamepadReport.X=random(-127,127);// just a test
  //bunch of IF statements
  Gamepad.send();

i also was experimenting with dual/triple gamepads or with 4 hats more axis 128 buttons ,
windows can show up to 32 buttons;
hats are all drawn into same space with different colors arrows;
and it can get confused with more than 6 axis with showing/ not showing few.

i hope this will help someone.

PS this is my 1st post here, cool to be part of the Community.