/* ================================================================ Colibrí V1.0 - Programa para CONSOLAS (Nintendo Switch) ================================================================ Colibrí es un mando de videojuegos que se maneja con la boca. Es un proyecto de Dame Alas 3D (damealas3d.com). Qué hace este programa: - La placa se presenta como un mando de Xbox 360 por cable (librería XInput). - Para jugar en la Switch, la placa va al adaptador Brook Wingman NS Lite y el adaptador, a la consola. El adaptador hace falta: la placa sola no se conecta a la consola. - El reparto de botones está pensado para Super Smash Bros. Placa del mando: Pro Micro (chip ATmega32U4, 5 V, 16 MHz). Qué necesitas para cargarlo con el Arduino IDE: 1. Librería «XInput», de David Madison. Se instala desde el Gestor de bibliotecas (versión actual: 1.2.6). 2. Paquete de placas XInput. Es obligatorio. En Archivo > Preferencias, en «URLs adicionales de gestor de placas», añade esta dirección: https://raw.githubusercontent.com/dmadison/ArduinoXInput_Boards/master/package_dmadison_xinput_index.json Luego, en el Gestor de placas, instala «XInput AVR Boards» y «XInput SparkFun Boards». Ojo: su autor lo permite solo para uso no comercial (educativo o de desarrollo). 3. Placa a elegir: «SparkFun Pro Micro w/ XInput», con el procesador «ATmega32U4 (5V, 16 MHz)». Es la que usamos nosotros. (Otra opción, sin probar: «Arduino Leonardo w/ XInput».) Nunca elijas 3,3 V / 8 MHz. Con la placa «Arduino Leonardo» normal también compila, pero la placa NO funciona como mando (sale el aviso «Non-XInput...»). 4. Doble reset al subir. Con XInput la placa no tiene puerto serie (COM), así que el IDE no la puede reiniciar solo. Hazlo cada vez que cargues este programa, y también cuando quieras quitarlo para volver a cargar el programa de PC. - En Archivo > Preferencias, en «Mostrar salida verbosa durante», marca «Carga». - Pulsa «Cargar», la flecha («Subir» en el IDE 1.8). Cuando salga «Uploading...» («Actualizando...» en el IDE 2 en español), reinicia la placa dos veces seguidas, rápido (con su botón de reset o uniendo el pin RST con GND dos veces). - Ha ido bien si sale «avrdude done. Thank you.», aunque el IDE diga que no encuentra la placa. Consejo del autor de XInput: carga un programa XInput solo si sabes reiniciar la placa así. 5. En la Switch, activa la comunicación por cable del mando Pro («Pro Controller Wired Communication», como lo llama Brook). Para probarlo antes en Windows: joy.cpl o https://html5gamepad.com/ Qué hace cada entrada (acción en Smash con sus controles por defecto): Entrada Xbox 360 Switch Acción Tubo central, soplar B A Ataque Tubo central, aspirar A B Especial Tubo izquierdo, soplar Y X Salto Tubo izquierdo, aspirar LB L Agarre Tubo derecho, soplar START + Pausa Tubo derecho, aspirar Botón Xbox HOME Menú HOME Labio (botón «chapa») RB R Agarre Joystick Stick izq. Stick Moverse Clic del joystick L3 L3 Clic del stick El paso de Xbox a Switch lo hace el adaptador Brook (según su tabla). Licencia: GPL-3.0-or-later. Copyright (C) 2026 Dame Alas 3D. Puedes usar, cambiar y compartir este código con la misma licencia (GNU GPL versión 3 o posterior). Se entrega sin ninguna garantía. Las librerías y el paquete de placas tienen su propia licencia. SPDX-License-Identifier: GPL-3.0-or-later */ // Librería XInput: hace que la placa se presente como un mando de Xbox 360. #include // ---- Pines (pin: patita de la placa) ---- const int pinBoton = 3; // Botón del labio («chapa») const int pinSensorAireCentro = A0; // Sensor de aire del tubo central const int pinSensorAireIzq = A3; // Sensor de aire del tubo izquierdo const int pinSensorAireDer = A8; // Sensor de aire del tubo derecho (A8 = patita 8) const int pinJoystickX = A1; // Joystick: izquierda-derecha const int pinJoystickY = A2; // Joystick: arriba-abajo const int pinBotonStick = 4; // Clic del joystick (al apretarlo) // ---- Umbrales de los sensores de aire ---- // analogRead() da un número de 0 a 1023 (de 0 V a 5 V). // En reposo, el sensor da unos 2,5 V: lectura de unos 512. // En este programa, soplar sube la lectura y aspirar la baja. // Los umbrales dejan margen alrededor de 512 para que las // pequeñas variaciones en reposo no pulsen botones. const int UMBRAL_SOPLO = 600; // Más de 600 = soplo const int UMBRAL_ASPIRACION = 400; // Menos de 400 = aspiración // setup(): se ejecuta una sola vez, al enchufar o reiniciar la placa. void setup() { // INPUT_PULLUP: una resistencia interna deja la patita en HIGH. // El botón la une a GND (0 V) al apretarlo: pulsado = LOW. pinMode(pinBoton, INPUT_PULLUP); pinMode(pinBotonStick, INPUT_PULLUP); // El joystick da de 0 a 1023. La librería lo pasa con map() // (cambio de escala) al rango del mando de Xbox: de -32768 a 32767. XInput.setJoystickRange(0, 1023); // No enviar cada cambio suelto: todo sale junto con // XInput.send(), al final de loop(). XInput.setAutoSend(false); // Arranque de la librería. En la versión 1.2.6 no hace nada, // pero se deja, como en los ejemplos de la librería. XInput.begin(); } // loop(): se repite sin parar. En cada vuelta lee todo y envía lo que cambie. void loop() { // Labio: Agarre (R de la Switch = RB de Xbox; en Smash, R agarra). // El ! invierte la lectura: LOW (pulsado) se convierte en «apretado». XInput.setButton(BUTTON_RB, !digitalRead(pinBoton)); // Tubo central: soplar = Ataque (A de la Switch = B de Xbox) // aspirar = Especial (B de la Switch = A de Xbox) // Las letras se cruzan porque el adaptador empareja los botones // por su sitio en el mando, no por su letra. int presionCentro = analogRead(pinSensorAireCentro); // de 0 a 1023 XInput.setButton(BUTTON_B, presionCentro > UMBRAL_SOPLO); // soplo XInput.setButton(BUTTON_A, presionCentro < UMBRAL_ASPIRACION); // aspiración // Tubo izquierdo: soplar = Salto (X de la Switch = Y de Xbox) // aspirar = Agarre (L de la Switch = LB de Xbox) int presionIzq = analogRead(pinSensorAireIzq); XInput.setButton(BUTTON_Y, presionIzq > UMBRAL_SOPLO); XInput.setButton(BUTTON_LB, presionIzq < UMBRAL_ASPIRACION); // Tubo derecho: soplar = Pausa (+ de la Switch = START de Xbox) // aspirar = Menú HOME (HOME de la Switch = botón Xbox) int presionDer = analogRead(pinSensorAireDer); XInput.setButton(BUTTON_START, presionDer > UMBRAL_SOPLO); XInput.setButton(BUTTON_LOGO, presionDer < UMBRAL_ASPIRACION); // Joystick: lee sus dos ejes (de 0 a 1023; centro, unos 512). int valorX = analogRead(pinJoystickX); int valorY = analogRead(pinJoystickY); // Zona muerta: si el joystick está casi en el centro (de 491 a 529), // se toma como centro (512). Así el personaje no se mueve solo // por pequeñas variaciones de la lectura. if (valorX > 490 && valorX < 530) valorX = 512; if (valorY > 490 && valorY < 530) valorY = 512; // Lo envía como stick izquierdo del mando. // 1023 - valorY invierte el eje Y (arriba <-> abajo): en XInput, // arriba es positivo, y muchos joysticks dan el eje Y al revés. XInput.setJoystick(JOY_LEFT, valorX, 1023 - valorY); // Clic del joystick = L3 (clic del stick izquierdo). Pulsado = LOW. XInput.setButton(BUTTON_L3, !digitalRead(pinBotonStick)); // Envía todo en un solo paquete. Si nada ha cambiado, no envía nada. // No hay delay(): el bucle se repite lo más rápido que puede. XInput.send(); }