miércoles, 26 de marzo de 2025

Dispositivo de reflejos con Arduino: programación, electrónica y aprendizaje activo

Como parte del proyecto NorbaIDC, los alumnos han desarrollado un dispositivo capaz de medir los reflejos de los jugadores, aplicando conocimientos de programación, electrónica y diseño interactivo. A pesar de su aparente sencillez, este trabajo encierra un alto valor pedagógico y se ha convertido en un excelente ejemplo del tipo de aprendizaje competencial que promovemos en el proyecto.

¿Cómo funciona el dispositivo?

El sistema está basado en una placa Arduino, y hace uso de diversos componentes electrónicos:

  • Pantalla OLED para mostrar instrucciones y resultados.

  • Altavoz con módulo DFPlayer Mini para emitir sonidos de aviso.

  • LED indicador que se apaga como señal de inicio.

  • Cuatro pulsadores, uno por jugador.

  • Potenciómetro para controlar el volumen de audio.





El funcionamiento es sencillo:

  1. Se muestra un mensaje inicial en pantalla invitando a comenzar.

  2. Tras una espera aleatoria, se apaga el LED (señal visual) y suena un audio (señal sonora).

  3. Los jugadores deben pulsar su botón lo más rápido posible.

  4. El sistema mide el tiempo de reacción de cada jugador en milisegundos y lo muestra ordenado en pantalla.

  5. Si nadie pulsa, se informa también en pantalla y por voz.

Este comportamiento, completamente programado por los alumnos, incluye detección simultánea de eventos, medición de tiempos, control condicional, gestión de entradas analógicas y digitales, y salida de datos tanto visual como auditiva.

/************************************************* * Código creado para el control del dispositivo * *************************************************/ unsigned long tiempoLed; unsigned long tiempoinicio = 0; unsigned long tiempo1 = 0; unsigned long tiempo2 = 0; unsigned long tiempo3 = 0; unsigned long tiempo4 = 0; const int pinVolumen = A0; int volumen; int boton1pin = 4; int boton2pin = 5; int boton3pin = 6; int boton4pin = 7; int ledrojopin = 9; #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <SoftwareSerial.h> #include <DFRobotDFPlayerMini.h> #define ANCHO 128 // reemplaza ocurrencia de ANCHO por 128 #define ALTO 64 // reemplaza ocurrencia de ALTO por 64 #define OLED_RESET 4 // necesario por la libreria pero no usado Adafruit_SSD1306 oled(ANCHO, ALTO, &Wire, OLED_RESET); // crea objeto SoftwareSerial mySerial(11, 12); // RX en 11, TX en 12 DFRobotDFPlayerMini myDFPlayer; /* Conexión de OLED Arduino UNO y NANO: SCL (ó SCK): A5; SDA: A4 Arduino MEGA: SCL (ó SCK): 21; SDA: 20 */ struct Numero { int valor; // Número original int id; // Identificador interno (1 para n1, 2 para n2, etc.) const char* alias; // Alias a mostrar (J1, J2, etc.) }; void setup() { // Para DFPLAYER Serial.begin(115200); mySerial.begin(9600); // Inicializa DFPlayer Mini if (!myDFPlayer.begin(mySerial)) { //Serial.println("Error con el DFPlayer Mini"); delay(100); //while (true) // Detiene el código si no se puede inicializar } myDFPlayer.volume(30); // Ajusta el volumen (0-30) // Serial.println("DFPlayer Mini listo para reproducir"); // Para Oled Wire.begin(); // inicializa bus I2C oled.begin(SSD1306_SWITCHCAPVCC, 0x3C); // inicializa pantalla con direccion 0x3C oled.ssd1306_command(SSD1306_SETCONTRAST); // Ajustar el contraste (brillo) oled.ssd1306_command(70); // Ajustar a un nivel bajo (prueba entre 0 y 255) oled.clearDisplay(); // limpia pantalla oled.setTextColor(WHITE); // establece color al unico disponible (pantalla monocromo) oled.display(); digitalWrite(ledrojopin, LOW); pinMode(boton1pin, INPUT_PULLUP); pinMode(boton2pin, INPUT_PULLUP); pinMode(boton3pin, INPUT_PULLUP); pinMode(boton4pin, INPUT_PULLUP); } void loop() { int pinVolumen = analogRead(A0); volumen = map(pinVolumen, 0, 675, 0, 30); myDFPlayer.volume(volumen); oled.clearDisplay(); oled.display(); oled.setTextSize(2); oled.setCursor(0, 0); // ubica cursor en inicio de coordenadas 0,0 oled.print("TOCA"); oled.setCursor(0, 17); oled.print("CUALQUIER"); oled.setCursor(0, 34); oled.print("BOTON PARA"); //oled.setCursor(0, 51); // ubica cursor en inicio de coordenadas 0,0 oled.print("JUGAR "); oled.display(); myDFPlayer.volume(volumen); myDFPlayer.play(7); while ((digitalRead(boton1pin) == HIGH) && (digitalRead(boton2pin) == HIGH) && (digitalRead(boton3pin) == HIGH) && (digitalRead(boton4pin) == HIGH)) tiempoLed = (random(5, 11)) * 1000; analogWrite(ledrojopin, 3); oled.clearDisplay(); oled.display(); oled.setTextSize(2); oled.setCursor(0, 0); // ubica cursor en inicio de coordenadas 0,0 oled.print(" PULSA"); oled.setCursor(0, 17); oled.print(" CUANDO SE"); oled.setCursor(0, 34); oled.print(" APAGUE"); oled.setCursor(0, 51); // ubica cursor en inicio de coordenadas 0,0 oled.print(" EL LED "); oled.display(); myDFPlayer.volume(volumen); myDFPlayer.play(5); delay(tiempoLed); //¡SE APAGA EL LED! analogWrite(ledrojopin, 0); tiempo1 = 0; tiempo2 = 0; tiempo3 = 0; tiempo4 = 0; tiempoinicio = millis(); while (tiempo1 == 0 || tiempo2 == 0 || tiempo3 == 0 || tiempo4 == 0) { if (millis() - tiempoinicio > 3000) { break; } if (digitalRead(boton1pin) == LOW) { if (tiempo1 == 0) { tiempo1 = millis() - tiempoinicio; } } if (digitalRead(boton2pin) == LOW) { if (tiempo2 == 0) { tiempo2 = millis() - tiempoinicio; } } if (digitalRead(boton3pin) == LOW) { if (tiempo3 == 0) { tiempo3 = millis() - tiempoinicio; } } if (digitalRead(boton4pin) == LOW) { if (tiempo4 == 0) { tiempo4 = millis() - tiempoinicio; } } } if (tiempo1 <= 2) { tiempo1 = 3000; } if (tiempo2 <= 2) { tiempo2 = 3000; } if (tiempo3 <= 2) { tiempo3 = 3000; } if (tiempo4 <= 2) { tiempo4 = 3000; } Numero numeros[] = { { tiempo1, 1, "J1" }, { tiempo2, 2, "J2" }, { tiempo3, 3, "J3" }, { tiempo4, 4, "J4" } }; // Algoritmo de burbuja para ordenar según el valor for (int i = 0; i < 3; i++) { for (int j = 0; j < 3 - i; j++) { if (numeros[j].valor > numeros[j + 1].valor) { Numero temp = numeros[j]; numeros[j] = numeros[j + 1]; numeros[j + 1] = temp; } } } // Asignar valores ordenados int A = numeros[0].valor; int B = numeros[1].valor; int C = numeros[2].valor; int D = numeros[3].valor; oled.clearDisplay(); oled.display(); oled.setTextSize(2); if (A != 3000) { oled.setCursor(0, 0); // ubica cursor en inicio de coordenadas 0,0 oled.print("1 "); oled.setCursor(25, 0); oled.print(numeros[0].alias); oled.setCursor(60, 0); oled.print(A); oled.setCursor(110, 0); // ubica cursor en inicio de coordenadas 0,0 oled.print("m"); int pinVolumen = analogRead(A0); volumen = map(pinVolumen, 0, 675, 0, 30); myDFPlayer.volume(volumen); myDFPlayer.play(numeros[0].id); } else { oled.setCursor(0, 0); // ubica cursor en inicio de coordenadas 0,0 oled.print(numeros[0].alias); oled.setCursor(25, 0); oled.print(" no puls"); } if (B != 3000) { oled.setCursor(0, 17); // ubica cursor en inicio de coordenadas 0,0 oled.print("2 "); oled.setCursor(25, 17); oled.print(numeros[1].alias); oled.setCursor(60, 17); oled.print(B); oled.setCursor(110, 17); // ubica cursor en inicio de coordenadas 0,0 oled.print("m"); } else { oled.setCursor(0, 17); // ubica cursor en inicio de coordenadas 0,0 oled.print(numeros[1].alias); oled.setCursor(25, 17); oled.print(" no puls"); } if (C != 3000) { oled.setCursor(0, 34); // ubica cursor en inicio de coordenadas 0,0 oled.print("3 "); oled.setCursor(25, 34); oled.print(numeros[2].alias); oled.setCursor(60, 34); oled.print(C); oled.setCursor(110, 34); // ubica cursor en inicio de coordenadas 0,0 oled.print("m"); } else { oled.setCursor(0, 34); // ubica cursor en inicio de coordenadas 0,0 oled.print(numeros[2].alias); oled.setCursor(25, 34); oled.print(" no puls"); } if (D != 3000) { oled.setCursor(0, 51); // ubica cursor en inicio de coordenadas 0,0 oled.print("4 "); oled.setCursor(25, 51); oled.print(numeros[3].alias); oled.setCursor(60, 51); oled.print(D); oled.setCursor(110, 51); // ubica cursor en inicio de coordenadas 0,0 oled.print("m"); } else { oled.setCursor(0, 51); // ubica cursor en inicio de coordenadas 0,0 oled.print(numeros[3].alias); oled.setCursor(25, 51); oled.print(" no puls"); } if (A == 3000 && B == 3000 && C == 3000 && D == 3000) { oled.clearDisplay(); oled.display(); oled.setCursor(0, 10); oled.print(" NO HA"); oled.setCursor(0, 30); oled.print(" PULSADO"); oled.setCursor(0, 50); oled.print(" NADIE"); int pinVolumen = analogRead(A0); volumen = map(pinVolumen, 0, 675, 0, 30); myDFPlayer.volume(volumen); myDFPlayer.play(6); } oled.display(); digitalWrite(ledrojopin, LOW); delay(6000); oled.clearDisplay(); oled.display(); }

¿Qué han aprendido los alumnos con este proyecto?

Este trabajo va mucho más allá de montar un circuito:

  • Han aprendido a estructurar un programa completo en C, incluyendo la definición y uso de estructuras (struct), ordenamiento de datos (burbujas), manejo de múltiples entradas simultáneas, control de librerías externas (OLED, DFPlayer).

  • Han comprendido cómo se interactúa con hardware real: uso de resistencias de pull-up, lectura analógica para mapear valores de un potenciómetro, escritura PWM en un LED, sincronización entre software y señales físicas.

  • Han desarrollado habilidades de diseño centrado en el usuario: cómo mostrar información clara en pantalla, cómo diseñar una interfaz simple pero efectiva, cómo proporcionar retroalimentación sonora adecuada.

Relación con los objetivos de NorbaIDC

El dispositivo responde directamente a los principios fundamentales del proyecto:

  • Aprendizaje activo y significativo: El alumnado no ha seguido instrucciones cerradas, sino que ha comprendido y adaptado cada parte del diseño.

  • Trabajo cooperativo y por proyectos: Ha sido necesario organizarse, planificar tareas y colaborar para llegar a una solución funcional.

  • Desarrollo competencial: Además de conocimientos técnicos, han ejercitado habilidades clave como la resolución de problemas, la perseverancia, la creatividad o la comunicación.

¿Por qué es mejor que una clase teórica?

Este tipo de experiencias tienen claras ventajas frente al modelo tradicional:

  • Los conocimientos se consolidan mejor al aplicarse en un contexto real.

  • El error se convierte en una fuente de aprendizaje, no en algo que penaliza.

  • Se fomenta la motivación intrínseca: aprender deja de ser una obligación para convertirse en un reto.

  • Los alumnos ven de forma tangible el impacto de lo que hacen: sus decisiones en código afectan al comportamiento del dispositivo.


Conclusión

Proyectos como este no solo hacen que los alumnos aprendan más, sino que aprendan mejor. Desde cómo organizar su lógica hasta cómo presentar los resultados al usuario, han pasado por todo el ciclo de desarrollo de un producto funcional. Y lo han hecho desde cero, tomando decisiones, probando, equivocándose y mejorando.

Este es el tipo de aprendizaje que buscamos con NorbaIDC: auténtico, práctico, autónomo y con sentido.


Profesora: Rosalía González García
Alumnos de 4º de ESO


No hay comentarios:

Publicar un comentario