Skip to content

Turning an Arduino Leonardo into a joystick.

2012 November 3
by Connor

I recently bought a Arduino Leonardo in the interest of updating the interface in an old arcade-style controller I occasionally use to play MAME games.

As such, I needed to figure out how to get the leonardo to properly act like a joystick.
Anyways, this is heavily based on drake250′s work, which is on the freetronics forum here. HelmPCB’s USB Joystick site was also a useful reference.
There is a useful list of HID descriptors here.
Also, the “HID Descriptor Tool” from usb.org is also very useful for seeing how HID descriptors are put together. Get it here.

In the end, I wound up rewriting most of the drake250′s HID descriptor myself, based on some of the USB.org docs, and the HID Descriptor Tool.

Anyways, here are my modified files you would need to turn your own leonardo into a joystick. These files support an 8-axis joystick, with two hat-switches, and 32 buttons.
If you want fewer buttons/axes/whatever, you can either just ignore the things you don’t need, or modify the HID descriptor yourself. As it is, for everything the HID descriptor currently specifies, it only needs 12 bytes per PC-update, so I’m not too worried about the extra axes causing issues or slowing things down.


The windows joystick info dialog only seems to be able to display 7 axes. I’m fairly sure the eighth axis there. However, since the built-in windows dialog works almost the time, I have not found any tools for looking at joystick readouts other then the built-in windows dialog. I mean, how may people have an 8-axis joystick anyways?
Also, the HID documents seem to also support the belief that the HID spec allows a maximum of 63 buttons. However, again, the windows tools for inspecting a joystick only support 32. You could add more quite easily, but you would have to test their functionality yourself.

Modified arduino files (replace the files in {arduino executable dir}\hardware\arduino\cores\arduino\ with these):

USBAPI.h
HID.cpp

And the main source file:

 leoJoy.ino 

It’s contents:

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
JoyState_t joySt;		// Joystick state structure
 
void setup()
{
	pinMode(13, OUTPUT);
 
	joySt.xAxis = 0;
	joySt.yAxis = 0;
	joySt.zAxis = 0;
	joySt.xRotAxis = 0;
	joySt.yRotAxis = 0;
	joySt.zRotAxis = 0;
	joySt.throttle = 0;
	joySt.rudder = 0;
	joySt.hatSw1 = 0;
	joySt.hatSw2 = 0;
	joySt.buttons = 0;
 
}

The interesting thing here is JoyState_t, which is a struct that stores the state for all the joystick controls.

The struct is defined in USBAPI.h as such:

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
typedef struct JoyState 		// Pretty self explanitory. Simple state to store all the joystick parameters
{
	uint8_t		xAxis;
	uint8_t		yAxis;
	uint8_t		zAxis;
 
	uint8_t		xRotAxis;
	uint8_t		yRotAxis;
	uint8_t		zRotAxis;
 
	uint8_t		throttle;
	uint8_t		rudder;
 
	uint8_t		hatSw1;			// 0-7 correspond to 0-315° in 45° steps. 8 means the hat-switch is centered
	uint8_t		hatSw2;			// All other values are invalid
 
	uint32_t	buttons;		// 32 general buttons  (Each bit corresponds to a separate button)
 
} JoyState_t;

Continuing on with the contents of leoJoy.ino:

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
void loop()
{
 
 
 
	joySt.xAxis = random(255);
	joySt.yAxis = random(255);
	joySt.zAxis = random(255);
	joySt.xRotAxis = random(255);
	joySt.yRotAxis = random(255);
	joySt.zRotAxis = random(255);
	//joySt.throttle = random(255);
	joySt.rudder = random(255);
 
	joySt.throttle++;
 
 
	joySt.buttons <<= 1;
	if (joySt.buttons == 0)
		joySt.buttons = 1;
 
	joySt.hatSw1++;
	joySt.hatSw2--;
 
	if (joySt.hatSw1 > 8)
		joySt.hatSw1 = 0;
	if (joySt.hatSw2 > 8)
		joySt.hatSw2 = 8;
 
	delay(100);
 
	if (joySt.throttle > 127)
		digitalWrite(13, HIGH);
	else
		digitalWrite(13, LOW);
 
 
	// Call Joystick.move
	Joystick.setState(&joySt);
 
}

This section just generates random values and sets the analog axes to them. Each bit in buttons is also set in sequence, using bit-shifting.
It also steps through the different hat-switch positions. It steps hatSw1 fowards, and hatSw2 backwards. Lastly, the throttle axis it linearly ramped.

The last (and most important thing) here is

67
	Joystick.setState(&joySt);

This is the function that actually sends the values in the joySt struct to the computer.

At this point, all that is needed to make a useable joystick is to replace the contents of loop() with something that actually reads hardware buttons.


Anyways, everything is under the arduino license (I think it’s GPL, but the only file I edited with a license actually looks to be BSD-ish. Anyways, do whatever, just don’t be an ass).

101 Responses leave one →
  1. Colin permalink
    July 16, 2014

    I want to write a program is 5IO 25KEY ,but I don’t know hwo to write,there is a example:
    L2 = L3 = L4 = L5 = 1;L1 = 0;
    delay (); if(L2==0) {
    L1 = 1; //释放人工置低的线 if(L2==0) key = 22; else key = 1; } } if(L3==0) { delay (); if(L3==0) { L1 = 1; if(L3==0) key = 23; else key = 2; } } if(L4==0) { delay (); if(L4==0) { L1 = 1; if(L4==0) key = 24; else key = 3; } } if(L5==0) { delay ();
    if(L5==0) { L1 = 1; if(L5==0) key = 25; else key = 4; } }
    L1 = L3 = L4 = L5 = 1;L2 = 0; if(L1==0) { delay (); if(L1==0) { L2 = 1; if(L1==0) key = 21; else key = 20; } } if(L3==0) { delay (); if(L3==0) { L2 = 1; if(L3==0) key = 23; else key = 5; } } if(L4==0) { delay ();
    if(L4==0) { L2 = 1; if(L4==0) key = 24; else key = 6; } } if(L5==0) { delay (); if(L5==0) { L2 = 1; if(L5==0) key = 25; else key = 7; } }
    can you help me,thanks a lot

  2. Vindolin permalink
    August 15, 2014

    Thanks for the cool blog post!

    I made an Instructable based on the info here, for adding a little two axis joystick to a bigger joystick to controll vertical/lateral thrust in Elite Dangerous :)

    http://www.instructables.com/id/Add-a-little-two-analog-axis-thumb-joystick-to-you/

  3. Doveman permalink
    September 3, 2014

    I’ve got a Thrustmaster F16 stick and want to wire up the buttons/hats to a Pro Micro to make it a USB device.

    I found this post with code for doing this with a Teensy, which is almost compatible with the Pro Micro except for the Joystick. commands which are integrated into the Teensy code.
    http://forum.il2sturmovik.com/topic/1790-there-any-better-joystick-ms-sidewinder-force-feedback-2/#entry46472

    Would anyone be able to help me take that code and combine it with the USB HID code from here? I’ve already flashed the latter to my Pro Micro and that works fine and shows random data being received on the joystick setup screen in Windows

  4. patrick permalink
    September 7, 2014

    Hello, how do i modify the code for 10 bit analog ?

    Can i just replace on of the uint8_t with uint10_t ?

Leave a Reply

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS