En esta guía aprenderás cómo interfazar un joystick PS2 genérico con una Feather 32u4 para que funcione como un gamepad USB en Windows y Steam. Cubriremos el hardware, el cableado, el firmware en Arduino IDE y la configuración en Steam.
Motivación
Olvidé mi joystick de Xbox 360 en casa, pero tengo un joystick PS2 que compré hace años para un proyecto retro. Con una Feather 32u4 y un poco de código, podemos convertirlo en un gamepad USB compatible con cualquier PC.
¿Por qué usar una Feather 32u4 para HID?
La Feather 32u4 (basada en ATmega32u4) tiene USB nativo, lo que permite emular dispositivos HID (mouse, teclado, gamepad) sin hardware adicional. Al conectar un joystick PS2, podemos remapear sus señales y enviarlas como un reporte HID estándar para PC. Esto ofrece:
- Baja latencia: USB es más rápido que un módulo Bluetooth.
- Compatibilidad inmediata: Windows detecta un gamepad HID sin drivers extra.
- Flexibilidad: personalización de botones y ejes.
Materiales necesarios
- Feather 32u4 (o equivalente con USB-HID nativo).
- Joystick PS2 (ej. clon del kit retro Tang Nano 20K).
- Cables dupont macho-macho para conexiones GPIO.
- Cable USB (datos + alimentación).
- PC con Windows y Steam instalado.
- Arduino IDE v2.x con librería HID-Project instalada.
- Opcional: adaptador de joystick PS2 a DIP si tu joystick no tiene pines accesibles.
1. Cableado y alimentación
Pines del joystick PS2
Señal PS2 | Descripción |
---|---|
CLK | Reloj (Clock) |
CMD | Comando (MOSI) |
ATT | Atenuación (SS) |
DATA | Datos (MISO) |
VCC | Alimentación (+3.3 V) |
GND | Tierra (0 V) |
Pines en la Feather 32u4
Pin Feather | Señal PS2 | Modo |
---|---|---|
D6 | CLK | OUTPUT (HIGH) |
D5 | CMD | OUTPUT (HIGH) |
D2 | ATT | OUTPUT (HIGH) |
D3 | DATA | INPUT_PULLUP |
VUSB | VCC | +3.3 V |
GND | GND | 0 V |
En mi caso uso un adaptador de joystick a DIP que conecta los pines del joystick PS2 a la Feather 32u4. Si no tienes adaptador, suelda cables directamente al joystick.
2. Prueba de comunicación PS2 sin librerías
Antes de integrar HID, verifica que el joystick responde al protocolo PS2. Este sketch envía la secuencia estándar de 9 bytes y muestra la respuesta en el 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); // Espera a que el joystick arranque
Serial.println("🔍 Prueba PS2 iniciada...");
}
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;
}
Sube este sketch.
Abre el Serial Monitor a 57600 baud.
Pulsa el botón ANALOG en el joystick hasta que se encienda el LED rojo.
Debes ver algo como:
RAW: FF 73 5A 00 00 80 80 80 80
73
indica modo analógico (41
sería modo digital).- Bytes 5–8 son valores LX, LY, RX, RY.
Si no cambia de 00 00 ...
, revisa el cableado, que DATA
esté en INPUT_PULLUP
y que el joystick reciba 3.3 V.
3. Configuración del Arduino IDE y librerías HID
Para enviar los datos del joystick como un gamepad USB, usamos la librería HID-Project de NicoHood.
- Ve a Sketch → Include Library → Manage Libraries.
- Busca HID-Project e instala la versión más reciente (por ejemplo 2.8.4).
- Selecciona placa Adafruit Feather 32u4 y el puerto correcto.
En HID-Project, la clase para gamepad se llama Gamepad
. Sus métodos principales:
Gamepad.begin()
Gamepad.press(n)
/Gamepad.release(n)
Gamepad.xAxis(value)
,Gamepad.yAxis(value)
,Gamepad.zAxis(value)
,Gamepad.rzAxis(value)
Gamepad.write()
para enviar el informe HID.
4. Firmware completo para convertir PS2 → Gamepad USB
Este sketch combina la lectura PS2 (bit‐banging) y la emulación de un Gamepad USB. Copia todo en un nuevo sketch .ino
:
#include <HID-Project.h>
#include <HID-Settings.h>
#define PS2_CLK 6 // D6 → CLK
#define PS2_CMD 5 // D5 → CMD (salida)
#define PS2_ATT 2 // D2 → ATT (salida)
#define PS2_DAT 3 // D3 → DATA (entrada con pull-up)
void setup() {
Serial.begin(57600); // Opcional para debug
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: mostrar crudo
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();
// Decodificar botones (bytes 3 y 4)
byte d_lo = resp[3] ^ 0xFF;
byte d_hi = resp[4] ^ 0xFF;
// Botones digitales
if (d_hi & 0x40) Gamepad.press(1); else Gamepad.release(1); // Cross/X → botón 1
if (d_hi & 0x80) Gamepad.press(2); else Gamepad.release(2); // Circle/O → botón 2
if (d_hi & 0x20) Gamepad.press(3); else Gamepad.release(3); // Square/□ → botón 3
if (d_hi & 0x10) Gamepad.press(4); else Gamepad.release(4); // Triangle/△ → botón 4
if (d_hi & 0x01) Gamepad.press(5); else Gamepad.release(5); // L2 digital → botón 5
if (d_hi & 0x02) Gamepad.press(6); else Gamepad.release(6); // R2 digital → botón 6
if (d_hi & 0x04) Gamepad.press(7); else Gamepad.release(7); // L1 → botón 7
if (d_hi & 0x08) Gamepad.press(8); else Gamepad.release(8); // R1 → botón 8
if (d_lo & 0x01) Gamepad.press(9); else Gamepad.release(9); // Select → botón 9
if (d_lo & 0x02) Gamepad.press(10); else Gamepad.release(10); // L3 → botón 10
if (d_lo & 0x04) Gamepad.press(11); else Gamepad.release(11); // R3 → botón 11
if (d_lo & 0x08) Gamepad.press(12); else Gamepad.release(12); // Start → botón 12
if (d_lo & 0x10) Gamepad.press(13); else Gamepad.release(13); // D-Pad Up → botón 13
if (d_lo & 0x20) Gamepad.press(14); else Gamepad.release(14); // D-Pad Right → botón 14
if (d_lo & 0x40) Gamepad.press(15); else Gamepad.release(15); // D-Pad Down → botón 15
if (d_lo & 0x80) Gamepad.press(16); else Gamepad.release(16); // D-Pad Left → botón 16
// Ejes analógicos (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(); // Enviar informe HID
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. Verificación en Windows
- En Arduino IDE v2, selecciona Board → Adafruit Feather 32u4 y el puerto COM correcto.
- Sube el sketch.
- Conecta el joystick PS2 a la Feather (3.3 V→VUSB, GND→GND, CLK→D6, CMD→D5, ATT→D2, DATA→D3).
- Pulsa el botón ANALOG en el joystick hasta que se encienda el LED rojo.
- Abre joy.cpl (Win + R →
joy.cpl
). - Debes ver un dispositivo denominado “HID Game Controller” (o similar).
- En Propiedades, prueba que los ejes y botones respondan al presionar.
6. Configuración en Steam
Una vez Windows reconoce el joystick como HID, configura en Steam:
Abre Steam → Configuración → Controlador → Configuración general de controlador.
Activa Soporte de configuración para mandos genéricos.
Conecta la Feather.
Haz clic en Iniciar configuración junto a “Adafruit Feather 32u4”.
Cuando pida el botón central (Share/Capture), haz clic en Cancelar o pulsa Esc para omitir.
Mapea botones:
- Cross (X) → Botón A.
- Circle (O) → Botón B.
- Square (□) → Botón X.
- Triangle (△) → Botón Y.
- L2/R2 → Tratarlos como botones digitales.
- L1/R1 → LB/RB.
- Select → “Back” (opcional).
- Start → “Start/Menu”.
- D-Pad → D-Pad Up/Right/Down/Left.
- Stick derecho (si existe), o omítelo con Skip.
Ajusta deadzones:
- Stick izquierdo: 10%.
- Stick derecho: 15% (si aplica).
Guarda la configuración.
Si tienes problemas al mapear, prepara este JSON en el portapapeles y pégalo al pulsar Paste from Clipboard:
{
"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
}
}
Nota: Esto es lo que tuve que hacer yo para que Steam reconociera correctamente los botones y ejes del joystick PS2.
- Copia todo el bloque JSON al portapapeles.
- Al mapear en Steam, elige Paste from Clipboard y confirma.
- Guarda y prueba en un juego.
Conclusión
Con estos pasos, has convertido un joystick PS2 en un gamepad USB funcional usando una Feather 32u4. Ahora puedes disfrutar de tus juegos retro o modernos con un control personalizado. ¡Espero que esta guía te sea útil y te inspire a experimentar más con proyectos de hardware y gaming!