FAST HID Joystick converter for 2 digital joysticks

Hi,
if you want to connect some oldshool digital joysticks like the competition pro to your pc, ps3, raspberry pi or whatever here's a simple and fast solution. It's also perfect to build your retro commodore amiga/c64/atari/sega...

This code transforms the arduino leonardo board into a HID joystick converter for two digital joysticks.

put this into your HID.cpp to create 2 HID Gamepads:

//   Commodore /  Atari Joystick 1

   0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
   0x09, 0x05,                    // USAGE (Game Pad)
   0xa1, 0x01,                    // COLLECTION (Application)
   0x85, 0x03,                    //   REPORT_ID (3)
   0x09, 0x01,                    //   USAGE (Pointer)
   0xa1, 0x00,                    //   COLLECTION (Physical)
   0x09, 0x30,                    //     USAGE (X)
   0x09, 0x31,                    //     USAGE (Y)
   0x15, 0xff,                    //     LOGICAL_MINIMUM (-1)
   0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
   0x95, 0x02,                    //     REPORT_COUNT (2)
   0x75, 0x02,                    //     REPORT_SIZE (2) ; two bits to represent each axis
   0x81, 0x02,                    //     INPUT (Data,Var,Abs)
   0xc0,                          //   END_COLLECTION
   0x05, 0x09,                    //   USAGE_PAGE (Button)
   0x19, 0x01,                    //   USAGE_MINIMUM (Button 1)
   0x29, 0x01,                    //   USAGE_MAXIMUM (Button 1)
   0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
   0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
   0x95, 0x01,                    //   REPORT_COUNT (1) ; 1 button
   0x75, 0x01,                    //   REPORT_SIZE (1)
   0x81, 0x02,                    //   INPUT (Data,Var,Abs)
   0x95, 0x02,                    //   REPORT_COUNT (2) ; to pad out the bits into a number divisible by 8
   0x81, 0x03,                    //   INPUT (Const,Var,Abs)
    0x05, 0x09,                    //   USAGE_PAGE (Button)
   0x19, 0x02,                    //   USAGE_MINIMUM (Button 2)
   0x29, 0x02,                    //   USAGE_MAXIMUM (Button 2)
   0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
   0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
   0x95, 0x01,                    //   REPORT_COUNT (1) ; 1 button
   0x75, 0x01,                    //   REPORT_SIZE (1)
   0x81, 0x02,                    //   INPUT (Data,Var,Abs)
   0xc0,                          // END_COLLECTION
 
//   Commodore /  Atari Joystick 2

   0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
   0x09, 0x05,                    // USAGE (Game Pad)
   0xa1, 0x01,                    // COLLECTION (Application)
   0x85, 0x04,                    //   REPORT_ID (4)
    0xa1, 0x00,                    //   COLLECTION (Physical)   
   0x05, 0x09,                    //   USAGE_PAGE (Button)
   0x19, 0x01,                    //   USAGE_MINIMUM (Button 1)
   0x29, 0x02,                    //   USAGE_MAXIMUM (Button 2)
   0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
   0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
   0x95, 0x02,                    //   REPORT_COUNT (2) ; 2 buttons
   0x75, 0x01,                    //   REPORT_SIZE (1)
   0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x95, 0x02,                    //   REPORT_COUNT (2) ; to pad out the bits into a number divisible by 8
   0x81, 0x03,                    //   INPUT (Const,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
   0x09, 0x30,                    //     USAGE (X)
   0x09, 0x31,                    //     USAGE (Y)
   0x15, 0xff,                    //     LOGICAL_MINIMUM (-1)
   0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
   0x95, 0x02,                    //     REPORT_COUNT (2)
   0x75, 0x02,                    //     REPORT_SIZE (2) ; two bits to represent each axis
   0x81, 0x02,                    //     INPUT (Data,Var,Abs)
   0xc0,                          //   END_COLLECTION
   0xc0                           // END_COLLECTION

merge this into your existing sketch or use it as a standalone solution:

    uint8_t Joy;
    uint8_t MemoJoy1 ;
    uint8_t MemoJoy2 ;

    void setup() {

      // Joystick 1
      pinMode(0, INPUT_PULLUP);                  // configure as DigIn  with pull up
      pinMode(1, INPUT_PULLUP);
      pinMode(2, INPUT_PULLUP);
      pinMode(3, INPUT_PULLUP);
      pinMode(4, INPUT_PULLUP);
      pinMode(5, INPUT_PULLUP);
     
      // Joystick 2
      pinMode(A0, INPUT_PULLUP);
      pinMode(A1, INPUT_PULLUP);
      pinMode(A2, INPUT_PULLUP);
      pinMode(A3, INPUT_PULLUP);
      pinMode(A4, INPUT_PULLUP);
      pinMode(A5, INPUT_PULLUP);

    }

    void loop()
    {         // Joystick 1
          Joy = ~PIND & 0b10011111;     // read Port D, invert logic, mask relevant bits
          if (Joy != MemoJoy1) {        // state changed?
            HID_SendReport(3, &Joy, 1); // send HID report
            MemoJoy1 = Joy;             // save last state
          }
         // Joystick 2
          Joy = ~PINF & 0b11110011;
          if (Joy != MemoJoy2) {
            HID_SendReport(4, &Joy, 1);
            MemoJoy2 = Joy;
          } 
       }

Joystick 1 must be hooked up to digital inputs 0...5, joystck 2 to analog inputs A0...A5.
This is the pin assignment:

    Joystick 1

    IO DB9  Function

    1   1   Forward
    0   2   Back
    2   3   Left
    3   4   Right
        5   not connected
    4   6   Button1
    5V  7   +5V  ( only needed if the joystick contains circuits like autofire,.. )
    GND 8   Ground
    6   9   Button2 (optional)

    Joystick 2

    IO DB9  Function

    A0  1   Forward
    A1  2   Back
    A2  3   Left
    A3  4   Right
        5   not connected
    A5  6   Button1
    5V  7   +5V
    GND 8   Ground
    A4  9   Button2 (optional)

Together with the arduino keyboard interface AMIGA 500/1000/2000 Keyboard Interface - Exhibition / Gallery - Arduino Forum
you have the complete keyrah functionality for an Amiga 500/1000/2000.

Olaf

IMG_2753.jpg

IMG_2763.jpg

update: 2nd button added

interesting but i ask now,i know can do but how (im newbie)
leonardo or uno who have better thats ?
i need 32 button interface hid interface
8 analog inbut and 16 encoder inbut.
how make hid interface to have all thats ?
ino to in arduino, what more need ?
i understand leonardo have hid ready board no need change bootloader, and uno no have hid compatiple need bootloader new write.

and hid device can adding 32 button, 16 encoder,8 analog inbut.
but how :frowning:
i need only buttons and encoders my computer.
analog can take old joystick.mean usb board if need,

And implemented for Atari ST/TT/Falcon keyboards. Only works with joystick in port 1. Have to figure out how to read the data from the mouse/joystick0 port..

// Wiring
// ST --- Arduino
// 1   -  GND
// ( blank )
// 3   - FDD LED (Not used)
// 4   -  +5V
// 5   -  0
// 6   -  1
// 7   -  2
// 8   -  GND

void setup() {
  Serial1.begin(7812);
  delay(200);
  reset_st_keyboard();
  delay(200);
  while (Serial1.available() > 0) Serial1.read();
}

void reset_st_keyboard(void)
{
  Serial1.print(0x80);
  Serial1.print(1);
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
  delay(20);
  digitalWrite(2, LOW);
  delay(20);
  digitalWrite(2, HIGH);
}

int joy0 = 0;
int joy1 = 0;
int joy1button = 0b0;
int key;
int lastkey;

uint8_t joyCodes[] =
{
  0b0,
  0b1, // N
  0b11, // S
  0b00,
  0b1100, // W
  0b1101, // NW
  0b1111, // NE
  0b00,
  0b100, // E
  0b101, // NE
  0b111 // SE
};

void loop() {
  while (Serial1.available() > 0) {
    key = Serial1.read();
    switch ( lastkey ) {
    case 0xFF: // Joy 1
      Serial.println(joy1button, BIN);
      joy1 = joyCodes[key] | joy1button ;
      HID_SendReport(3, &joy1, 1);
      break;
    case 0xF9: // Joy1 button
      joy1button = 0b110000;
      joy1 = joy1 | 0b110000;
      HID_SendReport(3, &joy1, 1);

      break;
    case 0xF8: // Joy1 button
      joy1button = 0b0;
      joy1 = joy1 ^ 0b110000;
      HID_SendReport(3, &joy1, 1);
      break;
    default:
    }
    lastkey = key;

  }

}

First, thanks very much for sharing!

I tried running this in Linux (Mint 17). Instead of seeing two joysticks, I see one with 4-axes. That is, running jstest js0 (there is no js1) gives

Driver version is 2.1.0.
Joystick (Arduino LLC Arduino Leonardo) has 4 axes (X, Y, Z, Rx)
and 7 buttons (BtnX, BtnY, BtnZ, BtnTL, LeftBtn, RightBtn, MiddleBtn).
Testing ... (interrupt to exit)
Axes: 0: 0 1: 0 2: 0 3: 0 Buttons: 0:off 1:off 2:off 3:off 4:off 5:off 6:off

Any ideas?

thank for your sketch

same problem with ubuntu 14.04 that merged the 2 joystick, found a bug report here Bug #118384 “Two-gamepads-in-one are treated like one gamepad” : Bugs : joystick package : Ubuntu

any suggestion?

best

Cool Stuff.

I coded both to my Leonardo: Keyboard and Joysticks.
Keyboard works an Joysticks seem to work cause LED is flashing while Joystick is moves or fire button is pressed.

So long, so good, but:

My Windows 7 only recognizes a keyboard at device category , no gamepad. I don´t know, how to setup the WINUAE. It´s a little bit confusing.

Maybe someone can help me.