He estado haciendo muchas pruebas en el diseño del pulsador. He partido de un pulsador de 24mm que he modificado para adaptar el sensor, muelle y demás.
El problema ha sido el rozamiento y el guiado.

Al final he llegado a un diseño entre pulsador arcade y pedal. A falta de mejorar o no las conexiones, ya lo he terminado.
También es importante el material y la orientación de la impresión. Puesto que si no se realiza bien, el material se puede partir.
He usado PETG para la base y he estado probando con PLA en el pulsador con buenos resultados. A falta de últimas pruebas ya ya veo el final de este mini proyecto.

https://vm.tiktok.com/ZNRCdyoyw/
En cuanto a la programación, he seguido usando la librería xinput pero añadiendo autocalibración, efecto pedal..
#include <XInput.h> // Librería para emular mando Xbox por USB
// ================= CONFIG =================
// Resolución del ADC (10 bits: 0–1023)
const int ADC_Max = 1023;
// ===== Pines de triggers (sensores Hall) =====
const int Pin_TriggerL = A2;
const int Pin_TriggerR = A3;
// ===== Pines de botones =====
const int Pin_ButtonA = 0;
const int Pin_ButtonB = 1;
const int Pin_ButtonX = 2;
const int Pin_ButtonY = 3;
const int Pin_ButtonLB = 4;
const int Pin_ButtonRB = 5;
const int Pin_ButtonBack = 6;
const int Pin_ButtonStart = 7;
const int Pin_ButtonL3 = 8;
const int Pin_ButtonR3 = 9;
// ===== D-Pad =====
const int Pin_DpadUp = 10;
const int Pin_DpadDown = 11;
const int Pin_DpadLeft = 12;
const int Pin_DpadRight = 13;
// ===== FILTRO (suaviza la señal analógica) =====
// Valores filtrados (estado interno)
float filteredL = 0;
float filteredR = 0;
// Factor de suavizado:
// 0.1 = muy suave (más retardo)
// 0.3 = más rápido (menos suavizado)
const float alpha = 0.2;
// ===== AUTO-CALIBRACIÓN =====
// Inicializamos extremos "imposibles"
// Se irán ajustando automáticamente en uso
int minL = 1023;
int maxL = 0;
int minR = 1023;
int maxR = 0;
// ================= SETUP =================
void setup() {
Serial.begin(115200); // Comunicación serie para debug
// Configura el rango de los triggers (0–1023)
XInput.setTriggerRange(0, ADC_Max);
// Desactiva envío automático (control manual con XInput.send())
XInput.setAutoSend(false);
// Configura todos los botones como entrada con pull-up interno
// -> estado normal = HIGH
// -> pulsado = LOW
pinMode(Pin_ButtonA, INPUT_PULLUP);
pinMode(Pin_ButtonB, INPUT_PULLUP);
pinMode(Pin_ButtonX, INPUT_PULLUP);
pinMode(Pin_ButtonY, INPUT_PULLUP);
pinMode(Pin_ButtonLB, INPUT_PULLUP);
pinMode(Pin_ButtonRB, INPUT_PULLUP);
pinMode(Pin_ButtonBack, INPUT_PULLUP);
pinMode(Pin_ButtonStart, INPUT_PULLUP);
pinMode(Pin_ButtonL3, INPUT_PULLUP);
pinMode(Pin_ButtonR3, INPUT_PULLUP);
pinMode(Pin_DpadUp, INPUT_PULLUP);
pinMode(Pin_DpadDown, INPUT_PULLUP);
pinMode(Pin_DpadLeft, INPUT_PULLUP);
pinMode(Pin_DpadRight, INPUT_PULLUP);
delay(1000); // Espera para estabilizar sensores Hall
// Inicializa los filtros con el valor actual
// Evita saltos bruscos al arrancar
filteredL = analogRead(Pin_TriggerL);
filteredR = analogRead(Pin_TriggerR);
// Inicia el dispositivo XInput
XInput.begin();
}
// ================= LOOP =================
void loop() {
// ===== BOTONES =====
// Se invierte la lectura (!)
// porque INPUT_PULLUP: LOW = pulsado
XInput.setButton(BUTTON_A, !digitalRead(Pin_ButtonA));
XInput.setButton(BUTTON_B, !digitalRead(Pin_ButtonB));
XInput.setButton(BUTTON_X, !digitalRead(Pin_ButtonX));
XInput.setButton(BUTTON_Y, !digitalRead(Pin_ButtonY));
XInput.setButton(BUTTON_LB, !digitalRead(Pin_ButtonLB));
XInput.setButton(BUTTON_RB, !digitalRead(Pin_ButtonRB));
XInput.setButton(BUTTON_BACK, !digitalRead(Pin_ButtonBack));
XInput.setButton(BUTTON_START, !digitalRead(Pin_ButtonStart));
XInput.setButton(BUTTON_L3, !digitalRead(Pin_ButtonL3));
XInput.setButton(BUTTON_R3, !digitalRead(Pin_ButtonR3));
// ===== D-PAD =====
// Envía las 4 direcciones a la vez
XInput.setDpad(
!digitalRead(Pin_DpadUp),
!digitalRead(Pin_DpadDown),
!digitalRead(Pin_DpadLeft),
!digitalRead(Pin_DpadRight)
);
// ===== TRIGGERS =====
// Lectura directa del sensor Hall (sin procesar)
int rawL = analogRead(Pin_TriggerL);
int rawR = analogRead(Pin_TriggerR);
// ---- FILTRO (suaviza vibraciones y ruido) ----
filteredL = alpha * rawL + (1 - alpha) * filteredL;
filteredR = alpha * rawR + (1 - alpha) * filteredR;
// ---- AUTO-CALIBRACIÓN ----
// Detecta automáticamente mínimo y máximo reales
if (filteredL < minL) minL = filteredL;
if (filteredL > maxL) maxL = filteredL;
if (filteredR < minR) minR = filteredR;
if (filteredR > maxR) maxR = filteredR;
// ---- NORMALIZACIÓN ----
// Convierte el rango real -> 0.0 a 1.0
float normL = 0;
float normR = 0;
// Evita división por cero
if (maxL > minL + 5) {
normL = (filteredL - minL) / (float)(maxL - minL);
}
if (maxR > minR + 5) {
normR = (filteredR - minR) / (float)(maxR - minR);
}
// Limita valores al rango válido
normL = constrain(normL, 0.0, 1.0);
normR = constrain(normR, 0.0, 1.0);
// ---- CURVA TIPO GATILLO ----
// Hace el inicio suave y el final más progresivo
// Ajustable: 1.5–2.2 según sensaciones
normL = pow(normL, 1.8);
normR = pow(normR, 1.8);
// ---- ESCALADO FINAL ----
// Convierte 0.0–1.0 -> 0–1023
int triggerLeft = normL * 1023;
int triggerRight = normR * 1023;
// ---- ENVÍO A XINPUT ----
XInput.setTrigger(TRIGGER_LEFT, triggerLeft);
XInput.setTrigger(TRIGGER_RIGHT, triggerRight);
// ===== DEBUG OPCIONAL =====
/*
Serial.print("L raw:");
Serial.print(rawL);
Serial.print(" min:");
Serial.print(minL);
Serial.print(" max:");
Serial.print(maxL);
Serial.print(" out:");
Serial.print(triggerLeft);
Serial.print(" | R raw:");
Serial.print(rawR);
Serial.print(" min:");
Serial.print(minR);
Serial.print(" max:");
Serial.print(maxR);
Serial.print(" out:");
Serial.println(triggerRight);
*/
// Envía todos los datos al PC
XInput.send();
}
SrRubio
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.