In this guide, you’ll learn how to interface a generic PS2 joystick with a Feather 32u4, so it works as a USB gamepad on Windows and Steam. We’ll cover the hardware, wiring, Arduino IDE firmware, and Steam setup. Joystick

Motivation

I left my Xbox 360 controller at home, but I have a PS2 joystick I bought years ago for a retro project. With a Feather 32u4 and a bit of code, we can turn it into a USB gamepad compatible with any PC.

Why Use a Feather 32u4 for HID?

The Feather 32u4 (based on the ATmega32u4) has native USB, allowing you to emulate HID devices (mouse, keyboard, gamepad) without extra hardware. By attaching a PS2 joystick, we can remap its signals and send them as a standard HID report to the PC. This offers:

  • Low latency: USB is faster than a Bluetooth module.
  • Immediate compatibility: Windows recognizes a HID gamepad without extra drivers.
  • Flexibility: Full customization of buttons and axes.

Required Materials

  • Feather 32u4 (or any board with native USB-HID).
  • PS2 joystick (e.g. retro Tang Nano 20K kit clone).
  • Dupont male-to-male wires for GPIO connections.
  • USB cable (data + power).
  • A PC with Windows and Steam installed.
  • Arduino IDE v2.x with HID-Project library installed.
  • Optional: PS2-joystick-to-DIP adapter if your joystick has no easily accessible pins.

1. Wiring and Power Supply

PS2 Joystick Pins

PS2 SignalDescription
CLKClock
CMDCommand (MOSI)
ATTAttention / SS
DATAData (MISO)
VCCPower (+3.3 V)
GNDGround (0 V)

Feather 32u4 Pins

Feather PinPS2 SignalMode
D6CLKOUTPUT (HIGH)
D5CMDOUTPUT (HIGH)
D2ATTOUTPUT (HIGH)
D3DATAINPUT_PULLUP
VUSBVCC+3.3 V
GNDGND0 V

In my setup, I use a PS2-to-DIP adapter that connects directly to the Feather 32u4. If you don’t have this adapter, solder wires straight to the joystick’s pins.

2. Testing PS2 Communication without Libraries

Before integrating HID, confirm the joystick responds to the PS2 protocol. This sketch sends the standard 9-byte command and prints the response over Serial:

#define PS2_CLK  6
#define PS2_CMD  5
#define PS2_ATT  2
#define PS2_DAT  3

void setup() {
  Serial.begin(57600);
  pinMode(PS2_CLK, OUTPUT);
  pinMode(PS2_CMD, OUTPUT);
  pinMode(PS2_ATT, OUTPUT);
  pinMode(PS2_DAT, INPUT_PULLUP);
  digitalWrite(PS2_CLK, HIGH);
  digitalWrite(PS2_CMD, HIGH);
  digitalWrite(PS2_ATT, HIGH);
  delay(300);  // Wait for joystick to initialize
  Serial.println("🔍 PS2 test started...");
}

void loop() {
  byte cmd[9] = {0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  byte resp[9] = {0};

  digitalWrite(PS2_ATT, LOW);
  for (int i = 0; i < 9; i++) {
    resp[i] = shiftInOut(cmd[i]);
  }
  digitalWrite(PS2_ATT, HIGH);

  Serial.print("RAW: ");
  for (int i = 0; i < 9; i++) {
    if (resp[i] < 0x10) Serial.print("0");
    Serial.print(resp[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
  delay(200);
}

byte shiftInOut(byte out) {
  byte in = 0;
  for (int i = 0; i < 8; i++) {
    digitalWrite(PS2_CMD, (out & (1 << i)) ? HIGH : LOW);
    digitalWrite(PS2_CLK, LOW);
    delayMicroseconds(20);
    if (digitalRead(PS2_DAT)) in |= (1 << i);
    digitalWrite(PS2_CLK, HIGH);
    delayMicroseconds(20);
  }
  return in;
}
  1. Upload this sketch.

  2. Open the Serial Monitor at 57600 baud.

  3. Press the ANALOG button on the joystick until its red LED lights up.

  4. You should see something like:

    RAW: FF 73 5A 00 00 80 80 80 80
    
    • 73 indicates analog mode (41 would mean digital mode).
    • Bytes 5–8 are LX, LY, RX, RY values.

If it remains 00 00 ..., check wiring, ensure DATA is INPUT_PULLUP, and confirm the joystick is powered with 3.3 V.

3. Arduino IDE Setup and HID Libraries

To send joystick data as a USB gamepad, we use NicoHood’s HID-Project library.

  1. Go to Sketch → Include Library → Manage Libraries.
  2. Search for HID-Project and install the latest version (e.g., 2.8.4).
  3. Choose Adafruit Feather 32u4 as the board and select the correct COM port.

In HID-Project, the gamepad class is called Gamepad. Key methods include:

  • Gamepad.begin()
  • Gamepad.press(n) / Gamepad.release(n)
  • Gamepad.xAxis(value), Gamepad.yAxis(value), Gamepad.zAxis(value), Gamepad.rzAxis(value)
  • Gamepad.write() to send the HID report.

4. Complete Firmware to Convert PS2 → USB Gamepad

This sketch combines PS2 bit‑banged reading with USB gamepad emulation. Copy it into a new .ino file:

#include <HID-Project.h>
#include <HID-Settings.h>

#define PS2_CLK  6   // D6 → CLK
#define PS2_CMD  5   // D5 → CMD (output)
#define PS2_ATT  2   // D2 → ATT (output)
#define PS2_DAT  3   // D3 → DATA (input with pull-up)

void setup() {
  Serial.begin(57600);  // Optional for debugging
  pinMode(PS2_CLK, OUTPUT);
  pinMode(PS2_CMD, OUTPUT);
  pinMode(PS2_ATT, OUTPUT);
  pinMode(PS2_DAT, INPUT_PULLUP);
  digitalWrite(PS2_CLK, HIGH);
  digitalWrite(PS2_CMD, HIGH);
  digitalWrite(PS2_ATT, HIGH);
  delay(300);
  Gamepad.begin();
}

void loop() {
  byte cmd[9] = {0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  byte resp[9];

  digitalWrite(PS2_ATT, LOW);
  for (int i = 0; i < 9; i++) resp[i] = shiftInOut(cmd[i]);
  digitalWrite(PS2_ATT, HIGH);

  if (resp[0] == 0x00 && resp[1] == 0x00) {
    delay(200);
    return;
  }

  // Debug: print raw data
  Serial.print("RAW: ");
  for (int i = 0; i < 9; i++) {
    if (resp[i] < 0x10) Serial.print("0");
    Serial.print(resp[i], HEX);
    Serial.print(" ");
  }
  Serial.println();

  // Decode buttons (bytes 3 and 4)
  byte d_lo = resp[3] ^ 0xFF;
  byte d_hi = resp[4] ^ 0xFF;

  // Digital buttons
  if (d_hi & 0x40) Gamepad.press(1); else Gamepad.release(1); // Cross/X → button 1
  if (d_hi & 0x80) Gamepad.press(2); else Gamepad.release(2); // Circle/O → button 2
  if (d_hi & 0x20) Gamepad.press(3); else Gamepad.release(3); // Square/□ → button 3
  if (d_hi & 0x10) Gamepad.press(4); else Gamepad.release(4); // Triangle/△ → button 4

  if (d_hi & 0x01) Gamepad.press(5); else Gamepad.release(5); // L2 digital → button 5
  if (d_hi & 0x02) Gamepad.press(6); else Gamepad.release(6); // R2 digital → button 6
  if (d_hi & 0x04) Gamepad.press(7); else Gamepad.release(7); // L1 → button 7
  if (d_hi & 0x08) Gamepad.press(8); else Gamepad.release(8); // R1 → button 8

  if (d_lo & 0x01) Gamepad.press(9); else Gamepad.release(9);   // Select → button 9
  if (d_lo & 0x02) Gamepad.press(10); else Gamepad.release(10); // L3 → button 10
  if (d_lo & 0x04) Gamepad.press(11); else Gamepad.release(11); // R3 → button 11
  if (d_lo & 0x08) Gamepad.press(12); else Gamepad.release(12); // Start → button 12

  if (d_lo & 0x10) Gamepad.press(13); else Gamepad.release(13); // D-Pad Up → button 13
  if (d_lo & 0x20) Gamepad.press(14); else Gamepad.release(14); // D-Pad Right → button 14
  if (d_lo & 0x40) Gamepad.press(15); else Gamepad.release(15); // D-Pad Down → button 15
  if (d_lo & 0x80) Gamepad.press(16); else Gamepad.release(16); // D-Pad Left → button 16

  // Analog sticks (bytes 5–8)
  int8_t lx = map(resp[5], 0, 255, -127, 127);
  int8_t ly = map(resp[6], 0, 255, -127, 127);
  int8_t rx = map(resp[7], 0, 255, -127, 127);
  int8_t ry = map(resp[8], 0, 255, -127, 127);

  Gamepad.xAxis(lx);
  Gamepad.yAxis(ly);
  Gamepad.zAxis(rx);
  Gamepad.rzAxis(ry);

  Gamepad.write();  // Send HID report
  delay(20);
}

byte shiftInOut(byte out) {
  byte in = 0;
  for (int i = 0; i < 8; i++) {
    digitalWrite(PS2_CMD, (out & (1 << i)) ? HIGH : LOW);
    digitalWrite(PS2_CLK, LOW);
    delayMicroseconds(20);
    if (digitalRead(PS2_DAT)) in |= (1 << i);
    digitalWrite(PS2_CLK, HIGH);
    delayMicroseconds(20);
  }
  return in;
}

5. Verification on Windows

  1. In Arduino IDE v2, select Board → Adafruit Feather 32u4 and the correct COM port.
  2. Upload the sketch.
  3. Connect the PS2 joystick to the Feather (3.3 V→VUSB, GND→GND, CLK→D6, CMD→D5, ATT→D2, DATA→D3).
  4. Press the ANALOG button on the joystick until its red LED lights up.
  5. Open joy.cpl (Win + R → joy.cpl).
  6. You should see a device named “HID Game Controller” (or similar).
  7. In Properties, verify that axes and buttons respond correctly.

6. Steam Configuration

Once Windows recognizes the joystick as HID, set up in Steam:

  1. Open Steam → Settings → Controller → General Controller Settings.

  2. Enable Generic Gamepad Configuration Support. alt text

  3. Connect the Feather.

  4. Click Start Configuration next to “Adafruit Feather 32u4”.

  5. When prompted for the center button (Share/Capture), click Cancel or press Esc to skip.

  6. Map buttons as follows:

    • Cross (X) → Button A
    • Circle (O) → Button B
    • Square (□) → Button X
    • Triangle (△) → Button Y
    • L2/R2 → Treat as digital buttons
    • L1/R1 → LB/RB
    • Select → “Back” (optional)
    • Start → “Start/Menu”
    • D-Pad → D-Pad Up/Right/Down/Left
    • Right stick (if present), or skip if not.
  7. Adjust deadzones:

    • Left stick: 10%
    • Right stick: 15% (if used)
  8. Save the configuration.

If mapping fails, copy this JSON to your clipboard and paste it via Paste from Clipboard: alt text

{
  "name": "PS2 Feather Default",
  "appid": 0,
  "creator": "PS2FeatherUser",
  "version": 2,
  "mode": "GLOBAL",
  "mappings": {
    "buttons": {
      "BUTTON_1": ["CROSS"],
      "BUTTON_2": ["CIRCLE"],
      "BUTTON_3": ["SQUARE"],
      "BUTTON_4": ["TRIANGLE"],
      "BUTTON_5": ["L2"],
      "BUTTON_6": ["R2"],
      "BUTTON_7": ["L1"],
      "BUTTON_8": ["R1"],
      "BUTTON_9": ["SELECT"],
      "BUTTON_10": ["L3"],
      "BUTTON_11": ["R3"],
      "BUTTON_12": ["START"],
      "BUTTON_13": ["DPAD_UP"],
      "BUTTON_14": ["DPAD_RIGHT"],
      "BUTTON_15": ["DPAD_DOWN"],
      "BUTTON_16": ["DPAD_LEFT"]
    },
    "axes": {
      "AXIS_0": ["LX"],
      "AXIS_1": ["LY"],
      "AXIS_2": ["RX"],
      "AXIS_3": ["RY"]
    }
  },
  "deadzones": {
    "AXIS_0": 0.10,
    "AXIS_1": 0.10,
    "AXIS_2": 0.15,
    "AXIS_3": 0.15
  }
}
  1. Copy the entire JSON block to your clipboard.
  2. In Steam mapping, choose Paste from Clipboard and confirm.
  3. Save and test in a game.

Conclusion

By following these steps, you’ve turned a PS2 joystick into a working USB gamepad using a Feather 32u4. Now you can enjoy retro or modern games with a fully customizable controller. I hope this guide helps you and inspires more hardware and gaming projects!