Tags

    Arduino UNO Big Joystick HID firmware

    I've extended the joystick firmware to present a much larger joystick to the host PC. The new firmware has:
    • 8 analog axis supporting values from -32768 to 32767.
    • 40 buttons
    This should be handy for building large control systems like the flight controls for a flight simulator, Firefly console, Viper console, or other large control system.

    Background

    A little background for anyone new to creating USB devices with the Arduino UNO or MEGA2560.

    The USB interface on the UNO and MEGA2560 boards is implemented in a small AVR. For R1 and R2 boards that AVR is an Atmega8U2, and for R3 boards it is an Atmega16U2. These chips are programmed with firmware that presents a USB Serial interface to the host PC, and they connect to the Arduino's main Atmega MCU via its TTL Serial port (digital pins 0 and 1).

    The 8U2 and 16U2 can be reprogrammed using DFU (Device Firmware Upgrade mode). This means that the USB Serial firmware can be replaced with other USB firmware to turn the Arduino all sorts of USB devices, like a mouse, a keyboard, a joystick, or a MIDI device.

    More info on DFU mode:

    Big Joystick Firmware


    Serial Report Format

    Byte Type Description
    0,1 int16_t Analog Axis 0 (X) value. -32768 to 32787
    2,3 int16_t Analog Axis 1 (Y) value. -32768 to 32787
    4,5 int16_t Analog Axis 2 value. -32768 to 32787
    6,7 int16_t Analog Axis 3 value. -32768 to 32787
    8,9 int16_t Analog Axis 4 value. -32768 to 32787
    10,11 int16_t Analog Axis 5 value. -32768 to 32787
    12,13 int16_t Analog Axis 6 value. -32768 to 32787
    14,15 int16_t Analog Axis 7 value. -32768 to 32787
    16 uint8_t Buttons 0-7. Bit 0 = button 0, bit 7 = button 7
    17 uint8_t Buttons 8-15. Bit 0 = button 8, bit 7 = button 15
    18 uint8_t Buttons 16-23. Bit 0 = button 16, bit 7 = button 23
    19 uint8_t Buttons 24-31. Bit 0 = button 24, bit 7 = button 31
    20 uint8_t Buttons 32-39. Bit 0 = button 32, bit 7 = button 40

    This table describes the format of the 21 byte report that the Arduino sends to the 8U2/16U2. This allows the Arduino to emulate a large joystick, sending axis positions and the value of 40 buttons to the Host PC.

    HID Descriptor


    This is the HID descriptor for the joystick. You can learn more about them from the USB HID Usage Tables.
     
     
     
    0x05, 0x01, /* Usage Page (Generic Desktop) */
    0x09, 0x04, /* Usage (Joystick) */

    0xa1, 0x01, /* Collection (Application) */
    0x09, 0x01, /* Usage (Pointer) */

    /* 8 axes, signed 16 bit resolution, range -32768 to 32767 (16 bytes) */
    0xa1, 0x00, /* Collection (Physical) */
    0x05, 0x01, /* Usage Page (Generic Desktop) */
    0x09, 0x30, /* Usage (X) */
    0x09, 0x31, /* Usage (Y) */
    0x09, 0x32, /* Usage (Analog1) */
    0x09, 0x33, /* Usage (Analog2) */
    0x09, 0x34, /* Usage (Analog3) */
    0x09, 0x35, /* Usage (Analog4) */
    0x09, 0x36, /* Usage (Analog5) */
    0x09, 0x37, /* Usage (Analog6) */
    0x16, 0x00, 0x80, /* Logical Minimum (-32768) */
    0x26, 0xff, 0x7f, /* Logical Maximum (32767) */
    0x75, 16, /* Report Size (16) */
    0x95, 8, /* Report Count (8) */
    0x81, 0x82, /* Input (Data, Variable, Absolute, Volatile) */
    0xc0, /* End Collection */

    /* 40 buttons, value 0=off, 1=on (5 bytes) */
    0x05, 0x09, /* Usage Page (Button) */
    0x19, 1, /* Usage Minimum (Button 1) */
    0x29, 40, /* Usage Maximum (Button 40) */
    0x15, 0x00, /* Logical Minimum (0) */
    0x25, 0x01, /* Logical Maximum (1) */
    0x75, 1, /* Report Size (1) */
    0x95, 40, /* Report Count (40) */
    0x81, 0x02, /* Input (Data, Variable, Absolute) */
    0xc0 /* End Collection */

    Example Sketch


    Here's a sketch demonstrating how to send joystick data to the host.

        /* Arduino USB Joystick HID demo */
    /* Author: Darran Hunt
    * Released into the public domain.
    */
    #define NUM_BUTTONS 40
    #define NUM_AXES 8 // 8 axes, X, Y, Z, etc
        typedef struct joyReport_t {
    int16_t axis[NUM_AXES];
    uint8_t button[(NUM_BUTTONS+7)/8]; // 8 buttons per byte
    } joyReport_t;
    joyReport_t joyReport;
    void setup()
    {
    Serial.begin(115200);
    delay(200);
    for (uint8_t ind=0; ind<8; ind++) {
    joyReport.axis[ind] = ind*1000;
    }
    for (uint8_t ind=0; ind<sizeof(joyReport.button); ind++) {
    joyReport.button[ind] = 0;
    }
    }
    // Send an HID report to the USB interface
    void sendJoyReport(struct joyReport_t *report)
    {
    Serial.write((uint8_t *)report, sizeof(joyReport_t));
    }
    // turn a button on
    void setButton(joyReport_t *joy, uint8_t button)
    {
    uint8_t index = button/8;
    uint8_t bit = button - 8*index;
    joy->button[index] |= 1 << bit;
    }
    // turn a button off
    void clearButton(joyReport_t *joy, uint8_t button)
    {
    uint8_t index = button/8;
    uint8_t bit = button - 8*index;
    joy->button[index] &= ~(1 << bit);
    }
    uint8_t button=0; // current button
    bool press = true; // turn buttons on?
        /* Turn each button on in sequence 1 - 40, then off 1 - 40
    * add values to each axis each loop
    */
    void loop()
    {
    // Turn on a different button each time
    if (press) {
    setButton(&joyReport, button);
    } else {
    clearButton(&joyReport, button);
    }
    /* Move all of the axes */ for (uint8_t ind=0; ind<8; ind++) { joyReport.axis[ind] += 10 * (ind+1); } sendJoyReport(&joyReport); button++; if (button >= 40) { button = 0; press = !press; } delay(100); }

    Upload the sketch to the Arduino first, then flash Arduino-big-joystick.hex to the 8U2/16U2. Unplug the Arduino and plug it back in, and will look like a joystick to the host and the host will receive the joystick updates from the sketch.

    If you want to upload another sketch, flash Arduino-usbserial.hex to the 8U2/16U2 first, unplug the Arduino and plug it back in. You can now upload the sketch.

    Downloads


    Demo sketch, joystick firmware, usbserial firmware.

    Download file "big_joystick_demo.ino"
    Download file "Arduino-big-joystick.hex"
    Download file "Arduino-usbserial.hex"

    Here's a python test application for checking that your Arduino joystick is behaving correctly. It requires pygame and ocempgui.

    Download file "joy.py"

    Source code is available on Github. See my earlier blog posts for directions on how to build the firmware.

    Loading the firmware


    For the Atmega8u2 (UNO R1, R2, MEGA2560 R1, R2):
    • Put the UNO into DFU mode.
    • dfu-prgrammer at90usb82 erase
    • dfu-programmer at90usb82 flash --debug 1 Arduino-big-joystick.hex
    • dfu-programmer at90usb82 reset
    • Unplug the UNO's USB cable for a few seconds and plug it back in
    For the ATMEGA16U2 (UNO R3, MEGA2560 R3)
    • Put the UNO into DFU mode.
    • dfu-prgrammer atmega16u2 erase
    • dfu-programmer atmega16u2 flash --debug 1 Arduino-big-joystick.hex
    • dfu-programmer atmega16u2 reset
    • Unplug the UNO's USB cable for a few seconds and plug it back in
    Use the same steps to to load Arduino-usbserial.hex when you want to restore the normal functionality to your UNO (e.g. to load a new sketch).

    Note: You will need version 0.5.5 of dfu-programmer to program the ATMEGA16U2. Get the latest version here.

    Comments

    &lt;a|g&gt; (unauthenticated)
    Jan 31, 2013

    Darran,

    Is the source available for this? I could not find it on the Github link that you posted.

    Thanks

    darran
    Jan 31, 2013

    Here's a browse link to the big joystick source: https://github.com/harlequin-tech/arduino-usb/tree/master/firmwares/arduino-big-joystick.

    Alex (unauthenticated)
    Feb 26, 2013

    Hi Darran,

    Thanks for such useful project! What need to be modified in order to add another 40 buttons and have 80 in total?

    Thanks!

    Dom (unauthenticated)
    Feb 27, 2013

    Hi Darren

    I've made a custom keyboard interface and wanting to add two joystick interfaces now. How possible (and easy) is it to make multple devices run from one Arduino 2560?

    Alex (unauthenticated)
    Feb 27, 2013

    Hey Dom, you may want to check this: https://code.google.com/p/unojoy/downloads/list

    Dom (unauthenticated)
    Feb 28, 2013

    I've seen the double joystick but haven't figured out which part of the code handles the usb device part. Admittedly I don't have a full handle on the USB HID bootstrap. So fare I've found nobody who has created code that allows more than one type of device running from an Arduino UNO. I'm thinking the 4kb limit may have something to do with it but it would be awesome to have a hex file which included a keyboard, joysticks and midi (and mouse I guess) all controlled through one Arduino Mega through one usb port. I have a feeling the 16mhz is also a limiting factor here.

    darran
    Mar 3, 2013

    Hi Alex,

    you can increase the number of buttons by modifying the descriptor called JoystickReport in Descriptors.c to add more buttons, and modifying the USB_JoystickReport_Data_t structure in Arduino-joystick.h to make room for the additional buttons.

    darran
    Mar 3, 2013

    Hi Dom,

    if you have a newer Arduino 2560 with the 16u2 then you can fit in additional devices in the descriptor and add code to send data for them. With the older 8u2 its unlikely it will have the code space to support them.

    Andy (unauthenticated)
    Mar 8, 2013

    Hi Darran, thanks for your hard work! I am using your firmware on Sparkfun's 32U4 breakout board (not an Arduino). After some minor modifications to the makefile (changed MCU) I got my board to enumerate in Windows and show up as a game controller. I am using the Big Joystick demo and my question is, how can I write to the joystick axes? I have several potentiometers hooked up to various ADC ports and I would like each of them to control a joystick axis.

    Thank you!

    Ender117 (unauthenticated)
    Mar 24, 2013

    Hello Darran,

    Thank you for all of these great resources. I have flashed the firmware successfully, but I am confused about how to assign pins to the potentiometers and buttons that I have. I don't see any pin assignments in your Arduino BigJoystick sketch. Should I be writing those myself? If you have time, could you point me in the right direction on how to do that? Thank you so much.

    Gyom (unauthenticated)
    Mar 25, 2013

    Hi Darran, great work!

    I have a question tho. If you use the Arduino as a joystick, can you still send back data to it via a serial connection? I mean, would you still be able to make the Arduino light up led and other device from commands sent from the PC at the sametime it "emulate" a joystick in Windows?

    Thanks!

    darran
    Apr 21, 2013

    Hi Gyom,

    the 8u2/16u2 firmware replaces the serial function with the new function. This mean when you flash the joystick or keyboard firmware, the Arduino appears to be a joystick or keyboard and not a serial port.

    i-make-robots (unauthenticated)
    Apr 22, 2013

    Thank you for this excellent How To. I've just build a 6DOF joystick for Kerbal Space Program and Minecraft. Maybe even Descent? I really hope with this I can get it to work. I'll let you know!

    Ender117 (unauthenticated)
    Apr 24, 2013

    Hello again Darran. Can you provide some indication on how to assign pins using your code?

    Dan (unauthenticated)
    Apr 25, 2013

    Hi, Darran. Great tutorial. Thank you for writing it and doing all that hard work on your own. I'm trying to follow your example to flash my MEGA 2560 from OSX Lion. All I get is "dfu-programmer: no device present." Any idea what I'm doing wrong?

    darran
    Yesterday at 9:16 PM

    Dan, try the latest version of dfu-programmer (http://dfu-programmer.sourceforge.net). It might also be that your mega is not in DFU mode, this page may help http://arduino.cc/en/Hacking/DFUProgramming8U2 (its the same two pins on the 2560).