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).

83 Responses leave one →
  1. Yianni permalink
    November 19, 2012

    hello,

    i just checked the HID.cpp and USBAPI.h and Joystick definitions are there but i get this error:

    any ideas?

    C:\Users\Ark\AppData\Local\Temp\build5316580264749198580.tmp/sketch_nov19a.cpp:70: undefined reference to `Joystick’
    C:\Users\Ark\AppData\Local\Temp\build5316580264749198580.tmp/sketch_nov19a.cpp:70: undefined reference to `Joystick’
    C:\Users\Ark\AppData\Local\Temp\build5316580264749198580.tmp/sketch_nov19a.cpp:70: undefined reference to `Joystick_::setState(JoyState*)’

    Thanks in advance. Yianni

    • November 26, 2012

      @Yianni:

      It sounds like you didn’t properly replace USBAPI.h.
      Both the Joystick class, and Joystick_::setState are both forward-defined there.

  2. Alex Kuptsov permalink
    November 20, 2012

    I’m new to arduino world

    thanks a lot

  3. Thom-x permalink
    December 31, 2012

    Thank you ! It works great !

  4. José Ferreira permalink
    January 7, 2013

    Can you help me puting the clutch in the code.

    Thanks!!

    • January 13, 2013

      Uh… What?

      DO you mean you want to add a clutch pedal? If so, just assign it to another analog axis, or a button, depending on how you want it to function.

      You could also tweak the HID descriptor. I think (IIRC), that there is a specific descriptor for a clutch pedal, it shouldn’t be too hard to add it to the complete descriptor.

      Just don’t forget you need to update the USB report size to include the new clutch.

  5. Rômulo permalink
    January 30, 2013

    Hey, I really liked your explanation, but, how could I wire the potentiometers of the throttle, brake and clutch to the axis?

    • February 1, 2013

      You would need to use the arduino ADCs for that.

      Additionally, since the ATmega32U4 ADCs are 10 bit, you will need to divide the value they return down to the 8-bit size of the analog axes the current HID descriptor allocate.

      Google “Arduino Potentiometer” for more information.

  6. Dave permalink
    February 25, 2013

    If you pack the structure and put it in the same order as the report, you don’t need any extra code to send it — you can just send the whole structure at once.

    Have you tried two joysticks? I can’t seem to get that to work.

    Thanks for the code!

  7. Mason H. permalink
    April 1, 2013

    How would I go about adding two buttons to be bound to a digital read of pins 1 and 2 of a similar board? I’ve got one button working fine, I just can’t figure out a second one…

  8. Mario permalink
    April 8, 2013

    Hello,

    i tried to make a simple button, when i press this button the Leonardo should give a signal to the windows gamepad (Arduino Leonardo) that the button is pressed.

    With this Code i can see in the SerialMonitor if i press the Button.

    void setup(){
    Serial.begin(9600);
    pinMode(2,INPUT);
    }

    void loop(){
    int status;
    status = digitalRead(2);
    Serial.println(status,DEC);
    }

    how would the Code look like when i want to use the leoJoy.ino?

    • That Guy permalink*
      May 5, 2013

      The buttons are bit-mapped to the buttons variable in the joySt struct.

      You would need to make it so when the buttons are pressed, it sets the various bits in buttons. Then you simply call Joystick.setState(&joySt); to update the joystick state to the computer.

  9. wwAGHww permalink
    April 30, 2013

    Sory for my question, but will it work on an other arduino board ?

    • That Guy permalink*
      May 5, 2013

      No. Most of the arduinos have another IC between the microprocessor (the ATmega328P) and the computer. The Leonardo is special because it is directly connected over USB.

      You can reflash the ATmega16U2 that is used as the USB-serial converter to act as a HID device, but it no longer behaves as a normal arduino (you need to program it over ICSP at that point).

  10. Edgar permalink
    May 11, 2013

    Hi, how would this work on a board like the nanowii?
    http://flyduino.net/Multikopter-FC-Multiwii_1

    and would it be possible to let the Invensense MPU6050 be input for a 3 axis joystick?

    I have no experience with arduino or any coding at all, but this board looks like a good candidate to make a nice flight simulator yoke.

    • May 11, 2013

      I don’t see any fundamental issues.

      You would have to figure out how to feed the MPU6050 output into the joystick struct.

      One comment I have is that is that I don’t have a high opinion of using an IMU for a joystick. Actual mechanical position measurement devices are almost invariably nicer.

  11. Félix Dhumes permalink
    May 20, 2013

    Good morning.
    Sorry if my question is obvious, but I’m French and I speack English just a little.
    With this modification and this program, I have just, for example, to modify the void loop like this:

    void loop()
    {
    axisx = analogRead(1);
    map(axisx, 0, 1024, 0, 255);
    joySt.xAxis = axisx;
    }

    Sorry for my bad English, and have a good day.

    • May 20, 2013

      I’m not sure how map() works, but I don’t think that will work, as map() has no way to change the value of axisx.

      Possibly you need to pass the value you want map to translate as a pointer –

      map(&axisx, 0, 1024, 0, 255);

      But I don’t know if map works that way. It may also return the scaled value, rather then operating on a pointer to the mapped value.

    • Christian Demmler permalink
      April 5, 2014

      Do it simpler, just drop 2 bits off by shifting (“>>”) the data to the right by two bits (same as dividing it by 4)… So a 10-bit value of 753 (binary: 1011110001) becomes an 8-bit value of 188 (1011100, the 01 at the end get dropped).

      joySt.xAxis = analogRead(A0) >> 2;

      • April 5, 2014

        I was assuming he wanted the full 10-bit resolution.

        • Christian Demmler permalink
          April 6, 2014

          Well, I think that can pretty much not work with this code, since the HID descriptor in HID.cpp just assigns 8bits of transferred data per axis and there is nothing the code within loop() can do to change that. If you got a working example of providing the leojoy code with 10bit axis capabilities, I think that would be greatly appreciated not only by me – have been trying all weekend to make it work, but still ended up with a 10bit joystick (values 511 showing) that doesn’t seem to transfer any data using joySt.xAxis = random(1023);

          I guess the data I send simply doesn’t match what the HID descriptor says it should look like, but I couldn’t for the life of me and even using minimal examples (2 16bit-axes, 0 buttons) make it work.

          • Christian Demmler permalink
            April 6, 2014

            Scratch that. I just reinstalled the Arduino IDE and started off a clean slate. Turns out the compiler was using some version of HID.cpp that I wasn’t editing, and I had made some other stupid mistakes such as not realizing how array declaration (#define joyBytes) counted, therefore ending up a data array that was one size too small. So for all who want to try this and just want a working example:

            Change in HID.cpp, line 171 from:
            “0×75, 0×08, // REPORT_SIZE (8)” to
            “0×75, 0×10, // REPORT_SIZE (16)” (this will make your throttle and rudder axes transfer 16 bit information.

            HID.cpp, line 309 from:
            “#define joyBytes 13″ to
            “#define joyBytes 15″

            and then change HID.cpp, lines 325-335 (data[4] to data[12] declarations) to something like this:

            data{4] = (uint8_t)((joySt->throttle & 0xFF00) >> 8);
            data[5] = (uint8_t)(joySt->throttle & 0x00FF);
            data{6] = (uint8_t)((joySt->rudder & 0xFF00) >> 8);
            data[7] = (uint8_t)(joySt->rudder & 0x00FF);

            data[8] = (joySt->hatSw2 << 4) | joySt->hatSw1; // Pack hat-switch states into a single byte

            data[9] = joySt->xAxis; // X axis
            data[10] = joySt->yAxis; // Y axis
            data[11] = joySt->zAxis; // Z axis
            data[12] = joySt->xRotAxis; // rX axis
            data[13] = joySt->yRotAxis; // rY axis
            data[14] = joySt->zRotAxis; // rZ axis

            Then of course you need to adapt the declarations of the throttle+rudder axis in USBAPI.h, lines 63-64, to be 16bit:

            “uint16_t throttle;
            uint16_t rudder;”

            And finally, a matching definition to put 16bit values in there in the leojoy.ino main loop, lines 41-43:

            “joySt.rudder = random(65535);

            joySt.throttle = analogRead(A0); // to get the 10bit AD-readout from Pin A0."

            Hope that helps some of you guys!

          • Christian Demmler permalink
            April 6, 2014

            Well, sorry, there are some mistakes in the code I just posted (“data{” doesn work so well as “data[“), but to make up for it, here are complete working files with 2x16bit and 6x8bit axes (and one of them is already reading off your analog input).

            https://www.wetransfer.com/downloads/91a3f27c89a1ad03e7560c4dab57505420140406194527/a883df940fb45f6b49bd46f76048760120140406194527/678887

            Hope this helps some guys, this did essentially take me all weekend out of my own stupidity.

  12. Jens Eggert permalink
    May 28, 2013

    Hello,

    great upgrading for the USB-support!

    I replaced HID.cpp and USBAPI.h.

    But when i try to compile your code, i get following error:
    ‘JoyState_t’ does not name a type

    Do i have to refresh both files inside of the Arduino development environment or something like that?

    Jens

  13. Marshall permalink
    June 3, 2013

    I am having the same problem as Yianni. and re replacing the files, rebooting, trying with a fresh arduino 1.0.4 file did not help the problem. What version of arduino are you using for this sketch? also I am using an arduino mini, but must select the leonardo to get the sketch to compile at all. what would I need to do for this to work the same way with the mini as the leanardo? Thanks

  14. Marshall permalink
    June 3, 2013

    Edit: (I cant figure out how to edit a comment already posted, so i will just make a new one)
    I tried using arduino 1.0.1 and replacing the files in that. It worked! so it seems that the newer versions of arduino have a problem with the way the files were modified. so if you are having errors like this:
    C:\Users\Ark\AppData\Local\Temp\build5316580264749198580.tmp/sketch_nov19a.cpp:70: undefined reference to `Joystick’
    C:\Users\Ark\AppData\Local\Temp\build5316580264749198580.tmp/sketch_nov19a.cpp:70: undefined reference to `Joystick’
    C:\Users\Ark\AppData\Local\Temp\build5316580264749198580.tmp/sketch_nov19a.cpp:70: undefined reference to `Joystick_::setState(JoyState*)’

    Just revert to a older version of arduino. Arduino 1.0.1 is known to work.
    Hope this helps for anyone with this problem.

    • June 9, 2013

      When I revisit the project, I’ll see what has gone wrong, and fix the code in the post.

      I’m a bit distracted at the moment.

    • Nilz permalink
      August 13, 2013

      I cannot get it to work, not even with an older version of Arduino. Why did they change things? On my other pc it is working fine with the older version but not on this one where I actually need it to work. Is there another place where these files are stored?

    • Christian Demmler permalink
      April 4, 2014

      For me, this problem was solved by possibly either of these two things: a) overwriting the usbapi.h and hid.cpp files in the “robot” directory as well, and probably more relevant: selecting the proper model of Arduino (Leonardo) in Tools>Board. Hope that helps some of you guys!

  15. Terrence permalink
    June 11, 2013

    Love your code, really been having a lot of fun with it.

    I am having trouble trying to change the 8bit Axis to 10bit.


    data[4] = joySt->throttle & 0xFF;
    joySt->throttle >>= 8;
    data[5] = joySt->throttle & 0xFF;

    I did try this, but yeah… Didn’t work. Could you please shed some light on the subject

    • June 12, 2013

      Unfortunately, all you’re probably doing is overwriting one of the axes with the top two bits of your joystick position data.

      To make the code properly support higher-resolution joystick position reporting, you will have to modify the HID descriptor to properly report the axes resolution to the computer, and determine how you are planning on packing the 10-bit value into the HID report packet.

      The HID device specification like to have everything packed into the minimum number of bytes, and that often involves packing multiple non-mod-8 bit variables into the minimum number of bytes required. It does allow for padding bits, so it would probably be best, assuming you only have 1 10-bit axes, to simply pad that out to 16 bits in the descriptor.

      I’d like to sit down and write a overview of how USB HID descriptors work at some point. I wrote a bit about how they are constructed here.

      • Terrence permalink
        July 3, 2013

        Thanx Connor appreciate the response.
        I ended up going in to the descriptor and changing it up to support the 10 bit.
        Quite rewarding.

  16. Christoffer permalink
    June 14, 2013

    Hello.

    Love the example code, easy to try out and understand.

    Then I tried to make my own game controller using this code. Turns out not a single game I have tried seem to “hear” my leonardo. Windows sees the commands sent but I won’t get any game to understand.

    Mainly I wanted to control my car’s speed in GTA IV with more resolution than just on or off, like with a keyboard.

    I have tried messing with the HID.cpp and changed some values according to “USB HID usage table” linked above but with absent progress.

    I do not have much experience with programming so I am probably missing something obvious but please, point me in the right direction.

    Thanks!

    • June 15, 2013

      If the joystick shows up in the “Game Controllers” dialog, the problem is not with the Arduino code.

      One thing to note is that DirectX, as far as I can tell, only supports the first four axes, and the first [some number, not tested] of the buttons. If you are putting your throttle on one of the additional axes, it will probably not be detected by most games.

      Note that the axis named “Throttle” is axis seven, and as such will not work (I think) with any games that use DirectX for input (read: most windows games).

      Try mapping your throttle to one of the X/Y axes, and see if that works.

      • Christoffer permalink
        June 15, 2013

        I have spent some time searching for a solution for GTA IV and can’t find one. GTA IV should be able to hear my leonardo because it works with my controller in both Xinput- and DirectInput mode. I have tried to use “x360ce” (emmulates an xbox360 controller) but the program does not hear my arduino. Not sure what I should do…

        After that failure I tried the leonardo with Portal 2 and it worked pretty well. Was able to control movement and aim with high resolution (have not tested buttons yet). Note: Portal 2 listens to Axis X,Y,Z, Throttle and Rudder, not Rx,Ry or Rz.

        How can my leonardo get GTA IV’s attention, have any idea?

        Thank you for taking the time to help me.

        • June 15, 2013

          If it works with some games, and not with others, I’m afraid you’re out of my depth.

          I really have no experience and little knowledge about PC driver issues. As it is, I’m not that great at MCU programming. My main area of expertise is really hardware design.

          Sorry.

  17. Jens Eggert permalink
    June 19, 2013

    Hello again,

    with the older Arduino Version its is running perfectly!

    But is there any Way to change the Name of the Gamecontroller?
    In the descriptors I only could find the String-Descriptor for the whole HID.

    Regards
    Jens

  18. Terrence permalink
    July 3, 2013

    So I’m editing the HID.cpp file to add a collective control in.
    I get rudder and throttle to work like a charm.

    The Collective… Not so much.

    this is the line I’ve added in:
    0×09, 0xb5, // USAGE (Collective Control)

    It compiles no problem. Then I go to the Leonardo properties and it’s not there.
    Just a small open space where a collective is ment to be.

    Any one got any ideas why this is being difficult?
    Or some kind of document I can go look into for help?

  19. Peter permalink
    July 10, 2013

    If you still want a tool to view joystick outputs, try this:

    http://www.leobodnar.com/products/BU0836/DIView.zip

  20. matii permalink
    July 26, 2013

    Hi,i intersting but no have programmer and no can do thats :(
    but i need usb joustick were have only 32-64 button,
    Who can made to me thats,no need analog axis my game.

    later need 16 rotary encoder inbut usb hid device too.
    who make somechip to me ?
    and what chip model need can adding all possiple button(toggle switch) max have 64 button ? one chip ? but if no can add 64 button good have 32 button usb hid device some chip. usb port have many :)
    use 2 port and adding 2 chip then have 64 button ?
    good example have leo bodnar BUO386X but i need 1 chip were have only buttons and second chip only encoders.
    send mail to me if you can made to me some chip. first need 2 chip only buttons, later more if working good.
    and second made to me 16 encoder inbut chip,about 2 chip buy too first.

  21. July 31, 2013

    Hey!

    Your stuff works fine, thanks so much for that!
    -But- I ran into errors when I tried to manipulate buttons greater than button 6. I use your firmware in connection with the KEYPAD.h to read a keypad and a few potentiometers and use them as a joystick. I only handle single keypress events because it is sufficient for my application. However, when I try to assign button values like this
    ======================================
    uint32_t values[] = {
    0×1,0×2,0×4,0×8,0×10,0×20,0×40,0×80,0×100,0×200,0×400,0×800,0×1000,0×2000,0×4000,0×8000,0×10000,0×20000,0×40000,0×80000,0×100000,0×200000,0×400000,0×800000 };

    if (KeyPressed){
    joySt.buttons = values[KeyPressed-1];
    }
    Joystick.setState(&joySt);
    ======================================

    where KeyPressed is a char and holds the number of the key from 1-23. The first 6 Buttons work file. Button 7 triggers a joystick event, but is not set. The other buttons do not work. If I print out the values instead, they look OK, in BIN as well as in HEX.
    I know that there are such things as bitshifting that make my lookup-table unnecessary and I tried that before but this is what it’s become after debugging for some time and in my opinion not part of the issue. So do you have any idea what’s wrong there?
    Thanks in advance!

    Full Code: http://pastebin.com/J55QCqEN

    • March 24, 2014

      First, it should be as simple to select specific buttons as just saying (1 << (KeyPressed-1)) or _BV(KeyPressed-1) (to use the GNU mnemonic).

      Regarding the issues with the other buttons, I don’t really know what to tell you. All 32 buttons work in my implementation, so I’d suspect there are access or data-size issues some weird issues with variables being cast to smaller variables, and that is truncating the buttons.

      I really don’t know exactly what’s going on. It also could be something happening in the HID device descriptor causing the descriptor to not be parsed correctly, or a number of other things. The fact that the buttons that work aren’t a multiple of 8 (e.g. the problem is not aligned on byte-boundaries) is interesting, but I don’t know what exactly it should point at.

  22. Diego Páez permalink
    August 11, 2013

    Thanks was very help full. Great Job. Brigadão.

  23. September 12, 2013

    how can i read buttons and analog inputs?
    i’m not 100% sure how to do it
    i am modding an old usb steering wheel that died so i need:
    11 buttons
    3 potentiometers steering, throttle and brake

  24. hellclimber permalink
    October 5, 2013

    Does anybody here know where i can find the list of places where my arduino is looking for any .h or .cpp-files? I copied both HID.cpp and USBAPI.h to any place I could imagine, but got allways “does not name a type” and so on. Tried old and new arduino-versions.

    As long as the type is not accepted I do not know wether the inplementation is running or not.

  25. Jason permalink
    November 27, 2013

    Hi,

    Nice work and I got the codes running in my micro. But how to change the USB source codes to make the device appeared in the windows as just a Joystick, now it appears as keyboard, mouse and joystick combination, it’s quite annoying.

    Any help would be very thankful!

    Jason

  26. Kelvin permalink
    January 3, 2014

    Can you still use the keyboard functions as well with this? I haven’t yet got it to work sending keyboard commands at the same time. I am assuming it is one or the other?

    • Kelvin permalink
      January 5, 2014

      I have been going through the HID.CPP file and there is an error. Where you have the #if KBAM_ENABLED it should read #ifdef KBAM_ENABLED as it throws up an error when you un-comment #define KBAM_ENABLED (the same with the raw hid later as well).

      It seems though that I can either have the keyboard & mouse or the joystick at one time, I have not been able to get all working at the same time (I only really need keyboard and joystick).

      I am hitting some fundamental limit of HID that can’t have 3 types of controller from one descriptor?

      • March 24, 2014

        There should be no fundamental issue. The problem boils down to the fact that the HID descriptor includes it’s size in the contents, so the descriptor size for keyboard is different from keyboard + mouse, is different from joystick, is different from joystick + keyboard, etc…

        Basically, since I was only interested in a joystick, I just disabled the KBAM segment all-together. The #ifdefs would have to be much more complex to allow arbitrary device descriptor structures.

  27. January 4, 2014

    I’m starting to program in leonardo and am interested in axial as joystick controls, specifically for flight simulators, and I have 4 controls such as control of the propeller and mexcla of combuatible, I welcome your comments, thanks

  28. sergio permalink
    January 5, 2014

    Hello, good , I’m newbie in this world , I have a leonardo Arduino board , and I’m trying to connect pedals (accelerator , brake and clutch ) , and before modifying the ‘d want code prove it , but I copy your sketch and modify files USBAPI and HID , and I get the following errors :
    Arduino : 1.5.5 (Windows 8) , Plate ” Arduino Leonardo ”

    In file included from C: \ Program Files (x86 ) \ Arduino \ hardware \ arduino \ avr \ cores \ arduino / HardwareSerial.h : 118,
                     from C: \ Program Files (x86 ) \ Arduino \ hardware \ arduino \ avr \ cores \ arduino / Arduino.h : 197,
                     from ejemplo_internet_wheel.ino : 7:
    C: \ Program Files (x86 ) \ Arduino \ hardware \ arduino \ avr \ cores \ arduino / USBAPI.h : 1:1 : error: unterminated # ifndef
    ejemplo_internet_wheel : 7: error: ‘ JoyState_t ‘ does not name a type
    ejemplo_internet_wheel.ino : In function ‘void setup () ‘ :
    ejemplo_internet_wheel : 14 : error: ‘ JOYST ‘ was not Declared In this scope
    ejemplo_internet_wheel.ino : In function ‘void loop () ‘ :
    ejemplo_internet_wheel : 34 : error: ‘ JOYST ‘ was not Declared In this scope
    ejemplo_internet_wheel : 67 : error: ‘ Joystick’ was not Declared In this scope

      This report would have more Information with
      ” Show verbose output During compilation ”
      enabled in File > Preferences.

    Could give me a hand ?
    Greetings , Thanks before hand and forgive my English , I’m no good and I use the google translator

  29. mnt permalink
    January 9, 2014

    Worked instantly. Thanks :)

  30. wesman permalink
    January 11, 2014

    I purchased an Arduino Micro to create the perfect joystick. Most joystick conversions were based on the mega. considering that the micro has the same controller and in/outs as the Leonardo, I believe that this is the answer that I’ve been looking for. Thanks for the detailed explanation and well commented code changes, which are easy to follow. I finally understand how this whole thing is supposed to work.

  31. hdrman permalink
    February 1, 2014

    Trying to compile with Arduino 1.5.5
    Error:

    C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/USBAPI.h:31: error: ISO C++ forbids declaration of ‘ring_buffer’ with no type

    C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/USBAPI.h:31: error: expected ‘;’ before ‘*’ token

    Any ideas?

    • March 14, 2014

      Don’t use arduino 1.5?

      (it should work with 1.0.*, I think)

      It should be possible to patch to make it work with 1.5, but I don’t really feel like dealing with it at the moment. I would assume the arduino people changed the layout of some of their internal data-structures (which this changes), and that is why it’s no longer compiling.

  32. ptako permalink
    February 10, 2014

    I’ve got the same problem as hdrman also in Arduino 1.5.5. – r2

  33. ptako permalink
    February 10, 2014

    My error during compiling is:

    In file included from D:\arduino15\arduino-1.5.5-r2\hardware\arduino\sam\cores\arduino/Arduino.h:213,
    from Joy.ino:9:

    D:\arduino15\arduino-1.5.5-r2\hardware\arduino\sam\cores\arduino/USB/USBAPI.h:31: error: ISO C++ forbids declaration of ‘ring_buffer’ with no type

    D:\arduino15\arduino-1.5.5-r2\hardware\arduino\sam\cores\arduino/USB/USBAPI.h:31: error: expected ‘;’ before ‘*’ token

    D:\arduino15\arduino-1.5.5-r2\hardware\arduino\sam\cores\arduino/USB/USBAPI.h:44: error: conflicting declaration ‘Serial_ Serial’

    D:\arduino15\arduino-1.5.5-r2\hardware\arduino\sam\variants\arduino_due_x/variant.h:218: error: ‘Serial’ has a previous declaration as ‘UARTClass Serial’

  34. Amrit permalink
    February 27, 2014

    How can I modify this code to use Arduino Leo as Xinput device (XBOX compatible) rather than Dinput (PC/PS3)

    • March 14, 2014

      You’d need to figure out how Xinput devices enumerate, and change the HID descriptor and message structure to act like that.

      I don’t know how xbox controllers work, but it’s probably not too complex. The hard part is reverse-engineering the xbox descriptors.

  35. Dominik B permalink
    March 13, 2014

    Thanks work fine.

    How can I modify this code to use 2 joysticks with one Arduino Leonardo?

    Any help would be very thankful! ;)

    • March 14, 2014

      It may be possible to change the HID descriptor to enumerate as two joysticks, but I have not experimented with that.

      You would have to change the descriptor and packet structure.

  36. Gavin permalink
    March 16, 2014

    Looking for a little help to send 10bit data values ( 0-1024 instead of 0-255 )
    For higher resolution in the pitch, roll, rudder and throttle axis.
    Nice work by the way :)

    • March 17, 2014

      You will have to edit the HID descriptor to change the reported variable sizes. You’ll also have to change the HID packet structure, since it’s currently exploiting the fact that all the values it’s reporting are some multiple of 8 bits. You’d either have to add padding bits, or manually pack your 10-bit variables into the HID message.

      Protips: you can make a union of a struct containing your data, and a byte-array, and then use the union to allow easy access to the struct as a char array for transmitting. Also, the -fpack-struct command line argument for GCC may be relevant here, but there is no easy way to access it through the arduino interfaces (it’s a GCC compiler flag).

      Realistically, you may have more then enough CPU time to just manually pack your outgoing messages before sending them over the USB interface. You don’t need more then ~100 Hz update rate for human-interfaces, really.

  37. wyatt permalink
    March 17, 2014

    I got this working with a SNES controller :D Thank you for posting this and saving me a lot of trouble with figuring out the USB documentation.

  38. Jay permalink
    March 20, 2014

    thanks so much for your detailed instruction and code!!
    I made it to play games though XDD
    http://www.youtube.com/watch?v=r0uTzgX-CaU

  39. Jon permalink
    March 20, 2014

    Thanks for post this how-to! How can I add LED emulation like caps-lock and num-lock? Or, how can I have this show up as a joystick and a keyboard?

  40. March 24, 2014

    Hi, Thanks for the great tutorial works like a charm, I am trying to implement 10 bit and 16 bit values. I can understand the 8 bit part but i can’t seem to get my head around splitting values into two bytes and the getting them to recombine i either get nothing or bleed into other parts.
    Anyway this is my project: http://hackaday.io/project/599-Aerospace-Control-Interface-

    • March 24, 2014

      Sorry i missed your reply from my previous post, had a lot happen since then. I understand it may be worth writing straight to 16 bit array and having the arduino upscale to match I.E. mapping 0-1024 to 0- 65535. I may upgrade to this in the future and it will certainly be useful for larger control ranges I.E. limb flexor ranges on VR Controls.
      I’ve been using parts of code from here but the topic seems dead, code work beautifly but doesn’t help me understand the problem at hand, anyway the link:
      http://forum.freetronics.com/viewtopic.php?f=27&t=734&start=10

      And my project: http://forum.freetronics.com/viewtopic.php?f=27&t=734&start=10

      • March 24, 2014

        Well, it started out as a long comment, but wound up as a post. See if this helps.

      • March 24, 2014

        Also, if you have questions about how the actual descriptors work, I can probably help with that. I have been vaguely meaning to write a bit of a document on HID-descriptor basics at some point.

        Having someone bug me about it would probably make me actually do something.

        • March 25, 2014

          Thanks so much for the post, when I get chance and am a little less burnt out I will dive head first into this. I am looking to properly understand the flow. from writing the code for the microcontroller. How and why it packs the data for different data lengths. What parts of the descriptor tell the host side what it is, what to expect and how to serve that data up.
          I’m going to write this all up too in my understanding and view points from a different perspective, one with no educational or working background in programming or electronics.
          Feel free to contact me via email

  41. locodog permalink
    March 30, 2014

    Hi I’ve been working with a UNO to make a HUGE joystick (128 buttons, 48 * 8 bit analogue, 32 * 10 bit analogue, 128 bytes output! [it is actually a HID dj software controller] ) this is working great but I am stuck at pc to device communication, for LEDs and other software to device communication.
    I’m waffling, I’m thinking of upgrading the uno, so, could this code base,
    1; accept pc to device HID communication?
    2; handle 128 bytes output?

    • March 30, 2014

      1: Theoretically. You’d need to write some PC-end software, and rework the HID descriptor stuff in the arduino.

      1. I don’t see any reason it couldn’t. Apparently the maximum descriptor size is 255 bytes, and even then, you could break a larger descriptor up into separate packets.
      • locodog permalink
        March 31, 2014

        Cool, the pc side software is taken care of as the djing program can already do that, Although I’m still yet to find an example of a HID device to accept a pc’s output.

        Leonardo on to my shopping list.
        If only there was a 32u4 product that had 16 bit analogue on board (more a wish than a need)

  42. locodog permalink
    April 1, 2014

    Any plans for ading vibration?

  43. Richard permalink
    April 14, 2014

    I am using your solution for attaching rotary encoders. However I have the problem that the changes are not picked up at approx. > 10 Hz. When switching the buttons purely programmatically via delay(…), this occurs as well. Is there anything I can do via the USB setup (possibly in HID.cpp) to get the events read out more accurately? The problem lets me dial only rather slowly.

    • April 14, 2014

      Are you trying to feed the quadrature encoder outputs in as discrete buttons?

      That’s your problem there. If you want to pass quadrature counts up through a HID device, you need to decode your quadrature encoder waveforms in the microcontroller. Then, you just send the delta counts (e.g. + or – encoder tics) since the last interval for each update.

      This is how computer mice work, by the way.

      With proper MCU code, and interrupts, you should be able to manage > 50 Khz encoder rates.

      • Richard permalink
        April 14, 2014

        Yes I decode the phased signal whether the dial turns right or left in the Arduino and output the signal into two joystick buttons (1st for right dial 2nd for left dial). I’d agree with you that this wouldn’t be feasible with a driven rotary encoder, but it is a manual dial with 30 clicks per 360 deg rotation. Turning it manually gives maybe 20Hz output which I expected to behandled well (obviously not the case ;-)). The receiving application (Flight Simulator FSX with FSUIPC add-on) can only handle joystick buttons for my purpose (dialing instrument settings).

        Is there a limitation how often I can call Joystick.setState(..)? What would the limiting frequency be and is that due to the Arduino side or due to Windows?

        When I put the encoder input on the LED (pin 13), the LED flashes in sync with turning the knob so I’d expect that the Arduino is not the limiting factor.

        As far as I understand I would have to send delta counts not as joystick buttons counts, right? Which would be the correct device to handle this with?

        • April 14, 2014

          You’re hitting issues with USB latency. The USB interface only sends updates at somewhere between 30 Hz and a few hundred hertz, depending on a whole pile of messy drivers and system configuration on the PC end (USB is shit from a latency perspective).

          Frankly, I don’t really understand how you’re using an encoder with a standard button interface. Encoders output a quadrature signal, which is fundamentally a two-bit signal. I don’t really understand how you’re funneling it through a 1-bit (e.g. a button) interface, unless you’re just using it for counting pulses, and ignoring the direction of rotation (unless each rotation direction is mapped to a different button?).

          In any event, yes, you’d have to send delta-counts as an axis value, rather then a button. It would just act like a joystick, where the speed of rotation is proportional to the “joystick” deflection.

          • Richard permalink
            April 14, 2014

            I map each direction to a different button as you said. Problem is, that the consuming application can deal only with buttons for my use case. Is really latency on PC side the issue. I wasn’t able to find any statement on that on the net. Do you have a source?

            From the USB code coming along with the Arduino, I deduce that it is set up for 64 byte frames at 1 ms interval (D_ENDPOINT(USB_ENDPOINT_IN (HID_ENDPOINT_INT),USB_ENDPOINT_TYPE_INTERRUPT,0×40,0×01 in HID.cpp). USB_SEND(..) in USBCore.cpp is worth a look as well as it stalls when sending of data is blocked. More interesting is the fact, that the method returns the number of bytes sent which isn’t used for error detection in HID_SendReport(..). I’ll have a look into this maybe.

  44. Richard permalink
    April 14, 2014

    Seems definitely to be the PC end to be the problem. I changed HID.cpp to return the number of bytes transmitted, which suggests none get lost. Also transfer time is in submillisecond range. I measured the frequency I do updates which show that it is in the 20Hz range. So, you were right, PC end is the problem. Thanks for your feedback ;-)

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