Sí, Hockey.

GRUPO 2

Formado por los integrantes:

  • Sara García Rodríguez
  • Eva Gómez Fernández
  • David Enrique Orna Alcobendas
  • Alicia Pina Zapata

Este proyecto llamado Sí, Hockey, se trata del montaje de una mesa de Air Hockey completamente funcional. La idea principal del proyecto era la construcción de un tablero con un sistema de detección de goles y que estos goles tuvieran una respuesta por el sistema en forma de un contador y luces interactivas. Además, se tenía que contar con un sistema de ventilación capaz de generar aire suficienta para lograr el deslizamiento del disco.

Para la construcción de Sí, Hockey ha sido necesario el uso de distintos materiales, tanto proporcionados por la universidad como comprados por nosotros.

En primer lugar, en la siguiente tabla se enumeran los materiales utilizados para la construcción del tablero del air hockey:

MaterialCantidadPrecio (€)
Metacrilato1 unidad32.99
Cortes de contrachapado3 tablas26.67
Listones de madera4 unidades9.96
Imprimación1 bote11.99
Pintura acrílica3 botes24.99
Tornillos60 unidades17.34
Cubre-tornillos40 unidades3.98
Cola de Carpintero1 bote3.19
Protector de esquinas4 piezas4.7
Fieltro1 plancha1.29
TOTAL136.90

Una vez listados los materiales anteriores, se pasa a enumerar los materiales que se han necesitado para el montaje de los circuitos a lo largo del desarrollo del proyecto:

MaterialCantidadPrecio (€)
Arduino UNO2 placas
4-digit 7-segment display1 pieza
Cableado20 metros7.00
Emisores láser2 unidades7.49
Receptores láser2 unidades6.99
Botón / Interruptor1 unidad9.89
Ventiladores4 unidades18.38
Pilas16 unidades9.39
Portapilas1 unidad6.99
Batería portátil1 unidad20.99
Pila de 9V1 unidad1.99
Cable USB1 unidad6.57
LEDs12 unidades
Zumbador / Buzzer1 unidad
Protoboard1 unidad
TOTAL97,67
* sin incluir los precios de los materiales proporcionados por la Universidad

Además de los materiales anteriores, se utilizaron materiales y herramientas que teníamos como taladro, destornillador eléctrico y manual, soldador de estaño, cinta aislante…

La construcción se separó en dos partes desarrolladas de manera simultánea: el montaje del tablero y el ensamblado de los circuitos.

Para el desarrollo del tablero de juego se comenzó haciendo un diseño a papel del mismo con las medidas necesarias, siendo estas:

  • Lado largo: 65cm x 20cm
  • Lado corto: 40cm x 20cm
  • Tabla de ventiladores: 65cm x 40cm

Se pasó entonces a cortar los tablones de madera a las medidas estudiadas y, una vez cortadas, se decidió de qué manera distribuir los 4 ventiladores en la tabla. Pensamos que la idea óptima era colocarlos haciendo un zigzag para que el aire quedase distribuido de la manera más uniforme posible.

Tras esto, se agujereó la tabla utilizando un taladro para poder poner los ventiladores y se atornillaron en sus debidas posiciones.

Se cortaron los listones que darían soporte al metacrilato, se hizo el agujero de las porterías de cada equipo y se procedió al montaje del tablero. Para el montaje, se taladró y atornilló, formando las cuatro paredes y poniendo los listones a modo de «somier» donde reposa el metacrilato. La tabla de los ventiladores se situó de forma que tuviera 3cm entre los listones y la tabla ya que, de esta manera, podían caber los ventiladores de 2,5cm de alto. Con esto se finalizó la etapa de carpintería.

Mientras unos montaron el tablero, otros iban probando circuitos de manera individual. El primero que se probó fue el de los láser y receptores, ya que al no ser de la caja proporcionada por la universidad no conocíamos su funcionamiento. Para ello se hizo un circuito y código simple que detectase cuando algo pasaba entre el láser y receptor. Una vez confirmado su correcto funcionamiento, se llevó a cabo el correspondiente al contador de puntos, primero de manera independiente y luego junto al anterior para mostrar esos cambios en el 4-digit 7-segment display.

Fue aquí cuando vimos necesario la utilización de otra placa arduino debido a la falta de pins. Para estudiar la manera en la que comunicarlos, se conectaron pins de un arduino a otro de manera que, mediante un código desarrollad,o uno mandase una señal y el otro la recibiera.

Quisimos además introducir un botón que se ocupase tanto de encender los ventiladores como avisar al Arduino padre de que se iniciara el juego. De esta manera se podría mejorar el sistemal, haciendolo más empotrado e independiente a tener que estar dándole alimentación constante.

Cuando pudimos estimar que la comunicación podía funcionar correctamente unimos todos los circuitos y vimos cómo podíamos mandar la detección de un cambio en los receptores de láser del Arduino maestro al Arduino esclavo, que se encarga de mostrar la puntuación en el contador. Cuando se finalizó esta tarea, solo quedaba que se notificase estos goles en forma de luz y música. Las pruebas individuales de estos fueron más sencillas.

Cuando ya se tenían probados los circuitos por separado, se integraron con el tablero y se dio comienzo al desarrollo del código final, añadiendo detalles como música personalizada (compuesta nota a nota), parpadeos de leds y efectos en el contador cuando el juego llegase a su fin.

El circuito quedó escondido bajo el tablero de los ventiladores siendo el resultado el presentado a continuación.

Finalmente, se imprimieron en 3D los elementos de juego y se montaron con cartón las porterías y la zona donde se colocaría el contador de puntuaciones. Decidimos los colores del juego de manera que se pudiera diferenciar los equipos y los elementos comunes (disco, marcador…) siendo estos colores amarillo, azul y rojo, pintamos los elementos y pegamos las porterías al tablero de juego utilizando silicona caliente.

Con esto concluyó el montaje total de nuestro proyecto.

A continuación, se presentan las funciones principales referentes a cada Arduino para que el sistema funcionara de forma correcta.

Arduino hijo

Esta placa se encarga de enviar las acciones al marcador (7-segment 4-digits display) según lo que recibe del Arduino padre.

#include "SevSeg.h"
SevSeg sevseg;
 
// Valores:gol,juego,inicio y fin
int val1;
int lastVal1;
int val2;
int lastVal2;
int goal_count_y;
int goal_count_b;
int readgoal1;
int readgoal2;
int newgame;
int playing;
int restart;
int last_restart; 
int finish_game;
int last_finish_game;

// Pins
const int RESTARTSIGNAL=A4;
const int FINISHSIGNAL=A5;
const int GOALYELLOW=A2;
const int GOALBLUE=A3;

void setup(){
  // Pins mode setup
  pinMode(GOALYELLOW,INPUT);
  pinMode(GOALBLUE,INPUT);
  pinMode(RESTARTSIGNAL,INPUT);
  // Inicialización variables
  lastVal1=LOW;
  lastVal2=LOW;
  last_restart=LOW;
  playing=false;
  last_finish_game=LOW;
  restart=LOW;
  // Inicialización de los goles a 0
  goal_count_y=0;
  goal_count_b=0;

  // Inicialización del marcador
  byte numDigits = 4;
  byte digitPins[] = {10, 11, 12, 13};
  byte segmentPins[] = {9, 2, 3, 5, 6, 8, 7, 4};
  bool resistorsOnSegments = true; 
  byte hardwareConfig = COMMON_CATHODE; 
  sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments);
  sevseg.setBrightness(90);
  sevseg.refreshDisplay();
  sevseg.setNumber(0, -1);
}

Tras la definición de las variables y constantes necesarias, la función setup() inicializa las mismas. Se indican de entrada los pines que recibe de la otra placa y se inicializan las variables de la lógica del código (las que almacenan el ultimo valor de las señales a baja y el contador de goles a 0). Además, se definen dos arrays numéricos para el control del marcador y se establecen los valores del seven-segment.

void start(){
  // Animación del marcador
  for(int i=0; i<10; i++){
    sevseg.refreshDisplay();
    sevseg.setNumber(0, 4);
    delay(200);
  }
  // Iniciar goles
  goal_count_y=0;
  goal_count_b=0;
  // Jugando
  playing=true;
}

En la función start(), se muestran en el marcador los 0 de manera parpadeante (con 200ms de intervalo), como secuencia de inicio de juego. Asimismo, se establecen los contadores de goles a 0, se indica en el serial que se ha comenzado y el booleano de que se esta jugando actualmente se pone a verdadero.

void finish(){
  // Si se ha producido una victoria, el marcador muestra efectos visuales que indican la misma
  if(goal_count_y==3 || goal_count_b==3){
    for(int i=0; i<15; i++){
        sevseg.refreshDisplay();
        int cont= goal_count_y*1000+goal_count_b*10;
        sevseg.setNumber(cont, 4);
        delay(250);
      }
  }
  goal_count_y=0;
  goal_count_b=0;
  playing=false;  
  sevseg.refreshDisplay();
}

En la función finish(), si se ha alcanzado la puntuación máxima (3) en alguno de los dos jugadores, se muestra el marcador final de forma parpadeante a modo de secuencia de fin de partida. En cualquier caso, se reinician los contadores y se pone a falso el booleano de juego.

void loop(){
  restart=digitalRead(RESTARTSIGNAL);
  finish_game=digitalRead(FINISHSIGNAL);
 // Se acaba de terminar el juego
  if(last_finish_game==LOW && finish_game==HIGH) {
    finish();
  }
  // Se acaba de inicializar el juego
  if(last_restart==LOW && restart==HIGH){
    start();
  }

  if(playing){
    val1=digitalRead(GOALYELLOW);
    val2=digitalRead(GOALBLUE);
   // Gol en la portería amarilla
    if(val1==HIGH && lastVal1==LOW){ 
      goal_count_b++;
    }
    // Gol en la portería azul
    if(val2==HIGH && lastVal2==LOW){
      goal_count_y++;
    }
    lastVal1=val1;
    lastVal2=val2;

   // Actualización del Contador
    Int cont= goal_count_y*1000 +goal_count_b*10;
    sevseg.refreshDisplay();
    sevseg.setNumber(cont, 4);
  }
  else{
    // Marcador "apagado"
    sevseg.refreshDisplay();
    sevseg.setNumber(0, -1);
  }

  //Guardan los valores 
  last_restart=restart;
  last_finish_game=finish_game;
}

La función loop() que está ejecutándose de forma constante durante el juego, controla el funcionamiento de la placa. Guarda en unas variables si hay que empezar la partida de nuevo o se ha terminado (según las señales recibidas del Arduino padre) y solo cuando esas variables cambian (con respecto a lo que tuviera guardado en su homónimo con el prefijo last), se llama a las funciones de inicio o final, respectivamente.

Si está en proceso de juego, se lee el valor de la puntuación de cada jugador de la señal recibida de la otra placa, y si esta ha cambiado (comprobándose de manera análoga a lo de inicio y fin), se considera que ha habido gol. En función del jugador que haya marcado, se modifica su contador. Se sobrescribe la variable que guarda el ultimo valor recibido en la señal de gol y se escribe el resultado actual en el display.

Si no se está jugando, se apaga el contador, no mostrando nada. Finalmente, se sobrescriben las variables que guardaban el último valor de inicio y fin.

Arduino Padre

Esta placa se encarga del grosso de la lógica de juego, controlando la mayoría de los componentes y enviando las señales correspondientes a la otra placa.

//Sonidos
#define NOTE_E5 659
#define NOTE_FS5 740
#define NOTE_G5 784
#define NOTE_F5 698
#define NOTE_A5 880
#define NOTE_B5 988
#define NOTE_C6 1047
#define NOTE_D6 1175

const int G3 = 196;
const int Gb3 = 208;     
const int Ab3 = 233;
const int C = 262;
const int D = 294;
const int Db = 311;
const int E = 330;
const int F = 349;
const int G = 392;
const int Gb = 415;
const int Ab = 440;
const int C5 = 523;
const int D5 = 587;
const int Db5 = 622;
const int E5 = 659;
const int G5 = 784;
const int Gb5 = 831;
const int Ab5 = 932;
const int C6 = 1046;

// Pins
const int YELLOW_LEDS=2;
const int BLUE_LEDS=12;
const int YELLOW_LASER=5;
const int YELLOW_RECEIVER=4;
const int BLUE_LASER=7;
const int BLUE_RECEIVER=6;
const int GOAL_YELLOW=8;
const int GOAL_BLUE=9;
const int START_GAME=10;
const int FINISH_GAME=11;
const int SCOREBOARD_LEDS=13;
const int BUTTON = A4;
const int BUZZER = 3;	

// barrera para pulsación del botón 
const int BARRIER_ON = 500;

// valores de los lasers
int val_yellow; 
int lastVal_yellow;
int val_blue;
int lastVal_blue;

// anterior valor del botón de inicio
int last_button;

// contador de los puntos de cada jugador (blue y yellow)
int goal_count_b;
int goal_count_y;

En la definición de constantes se han incluido tanto las relacionadas con sonidos (notas musicales) que se utilizaran para emitir por el zumbador, como los pines que se han usado para cada componente. También se establece el valor BARRIER_ON, que es la barrera que deberá superar la lectura analógica del botón que inicia el juego para que se considere pulsado. Por otro lado, se establecen variables para el control de los láseres, el anterior valor del botón y las puntuaciones.

void start_game(){
  goal_count_b=0;
  goal_count_y=0;
    // Se encienden los laser
  digitalWrite(YELLOW_LASER,HIGH);
  digitalWrite(BLUE_LASER,HIGH);
   // Comunicación al Arduino hijo
  digitalWrite(START_GAME,HIGH);

  // Música y luces
  digitalWrite(YELLOW_LEDS, HIGH);
  tone(BUZZER, NOTE_G5);
  delay(100);
  tone(BUZZER, NOTE_E5);
  delay(100);
  tone(BUZZER, NOTE_G5);
  delay(100);
  tone(BUZZER, NOTE_A5);
  delay(100);
  noTone(BUZZER);
  
  digitalWrite(YELLOW_LEDS, LOW);
  delay(200);
  digitalWrite(BLUE_LEDS, HIGH);

  tone(BUZZER, NOTE_A5);
  delay(100);
  tone(BUZZER, NOTE_G5);
  delay(100);
  tone(BUZZER, NOTE_A5);
  delay(100);
  tone(BUZZER, NOTE_B5);
  delay(100);
  noTone(BUZZER);

  digitalWrite(BLUE_LEDS, LOW);
  delay(200);
  digitalWrite(YELLOW_LEDS, HIGH);
  tone(BUZZER, NOTE_B5);
  delay(100);
  tone(BUZZER, NOTE_A5);
  delay(100);
  tone(BUZZER, NOTE_B5);
  delay(100);
  tone(BUZZER, NOTE_C6);
  delay(100);
  noTone(BUZZER);
  digitalWrite(YELLOW_LEDS, LOW);
  delay(200);
  digitalWrite(BLUE_LEDS, HIGH);
  digitalWrite(YELLOW_LEDS, HIGH);
  tone(BUZZER, NOTE_C6);
  delay(100);
  
  digitalWrite(BLUE_LEDS, LOW);
  digitalWrite(YELLOW_LEDS, LOW);
  noTone(BUZZER);
  delay(100);
  
  digitalWrite(BLUE_LEDS, HIGH);
  digitalWrite(YELLOW_LEDS, HIGH);
  tone(BUZZER, NOTE_B5);
  delay(100);
  digitalWrite(BLUE_LEDS, LOW);
  digitalWrite(YELLOW_LEDS, LOW);
  tone(BUZZER, NOTE_C6);
  delay(100);
  noTone(BUZZER);
  delay(100);
  
  digitalWrite(BLUE_LEDS, HIGH);
  digitalWrite(YELLOW_LEDS, HIGH);
  tone(BUZZER, NOTE_C6);
  delay(100);
  tone(BUZZER, NOTE_D6);
  delay(400);
  noTone(BUZZER);
  delay(100);
  
  digitalWrite(BLUE_LEDS, LOW);
  digitalWrite(YELLOW_LEDS, LOW);
  digitalWrite(SCOREBOARD_LEDS, HIGH);
  Serial.write("Empieza el juego");
  digitalWrite(START_GAME,LOW);
}

La función start_game() activa la emisión de láser, inicializa a 0 las variables de conteo de goles y manda a la placa del hijo la señal de comienzo de juego. Además, se encarga de la secuencia de inicio de sonido y luces intermitentes y enciende los LEDs fijos del marcador.

void finish_game(){
  // Se apagan los laser y se manda al arduino hijo la señal de fin de partida
  digitalWrite(YELLOW_LASER,LOW);
  digitalWrite(BLUE_LASER,LOW);
  digitalWrite(FINISH_GAME,HIGH);
  delay(1000);
  digitalWrite(FINISH_GAME,LOW);
  digitalWrite(SCOREBOARD_LEDS, LOW);
  goal_count_b=0;
  goal_count_y=0; 
}

La función de fin de juego apaga los laser, manda la señal de final a la placa hijo durante un intervalo de tiempo, apaga los LEDs del marcador y reinicia las variables de conteo.

void win(int led){  
  digitalWrite(FINISH_GAME,HIGH);
  winEffects(led);
  delay(500);
  digitalWrite(FINISH_GAME,LOW);
  goal_count_b=0;
  goal_count_y=0; 
  digitalWrite(SCOREBOARD_LEDS, LOW);
  delay(2000);
  start_game();
}

La función win() reinicia la partida cuando uno de los jugadores gana. Para ello, envía la señal de fin a la otra placa, llama a los efectos visuales y de sonido y reinicia las variables de conteo y apaga las luces del marcador. A continuación, llama al inicio de juego.

void goalEffects(int led){
  for(int i=0; i<3; i++) {
    digitalWrite(led, HIGH);
    tone(BUZZER, NOTE_E5);
    delay(100);
    tone(BUZZER, NOTE_FS5);
    delay(100);
    digitalWrite(led, LOW);
    tone(BUZZER, NOTE_G5);
    delay(100);
    tone(BUZZER, NOTE_E5);
    delay(100);
    noTone(BUZZER);
  }
  delay(4000);
}

La función de los efectos de gol intercala sonidos con luces en la portería en la que se ha marcado. Recibe como parámetro el pin del grupo de LEDs sobre el que se tiene que realizar la acción.

void winEffects(int led) {
  digitalWrite(led, HIGH);
  tone(BUZZER, G3); 
  delay(100); 
  tone(BUZZER, C); 
  delay(100);
  tone(BUZZER, E); 
  delay(100);
  digitalWrite(led, LOW);
  tone(BUZZER, G);
  delay(100);
  tone(BUZZER, C5);
  delay(100);
  tone(BUZZER, E5);
  delay(100);
  digitalWrite(led, HIGH);
  tone(BUZZER, G5);
  delay(250);
  digitalWrite(led, LOW);
  tone(BUZZER, E5);
  delay(250);
  digitalWrite(led, HIGH);
  noTone(BUZZER);
  digitalWrite(led, LOW);
  delay(250);
  digitalWrite(led, HIGH);
  tone(BUZZER, Gb3);
  delay(100);
  tone(BUZZER, C);
  delay(100);
  tone(BUZZER, Db);
  delay(100);
  digitalWrite(led, LOW);
  tone(BUZZER, Gb);
  delay(100);
  tone(BUZZER, C5);
  delay(100);
  tone(BUZZER, Db5);
  delay(100);
  tone(BUZZER, Gb5);
  delay(250);
  digitalWrite(led, HIGH);
  tone(BUZZER, Db5);
  delay(250);
  digitalWrite(led, LOW);
  noTone(BUZZER);
  delay(250);
  digitalWrite(led, HIGH);
  tone(BUZZER, Ab3);
  delay(100);
  digitalWrite(led, HIGH);
  tone(BUZZER, D);
  delay(100);
  tone(BUZZER, F);
  delay(100);
  tone(BUZZER, Ab);
  delay(100);
  digitalWrite(led, LOW);
  tone(BUZZER, D5);
  delay(100);
  tone(BUZZER, Ab5);
  digitalWrite(led, HIGH);
  delay(250);
  digitalWrite(led, LOW);
  noTone(BUZZER);
  delay(50);
  digitalWrite(led, HIGH);
  tone(BUZZER, Ab5);
  delay(100);
  digitalWrite(led, LOW);
  noTone(BUZZER);
  delay(50);
  digitalWrite(led, HIGH);
  tone(BUZZER, Ab5);
  delay(100);
  digitalWrite(led, LOW);
  noTone(BUZZER);
  delay(50);
  digitalWrite(led, HIGH);
  tone(BUZZER, C6);
  delay(250);
  digitalWrite(led, LOW);
  noTone(BUZZER);
}
  

Esta función muestra los efectos de fin de partida intercalando luces y sonido en una secuencia intermitente. Recibe como parámetro el pin de los leds del color ganador sobre el que aplica los efectos de luz.

void setup() {
  // Pin modes setup
  pinMode(YELLOW_LEDS,OUTPUT);
  pinMode(BLUE_LEDS,OUTPUT);
  pinMode(BUTTON,INPUT);
  pinMode(YELLOW_LASER,OUTPUT);
  pinMode(YELLOW_RECEIVER,INPUT);
  pinMode(BLUE_LASER,OUTPUT);
  pinMode(BLUE_RECEIVER,INPUT);
  pinMode(GOAL_YELLOW,OUTPUT);
  pinMode(GOAL_BLUE,OUTPUT);
  pinMode(START_GAME,OUTPUT);
  pinMode(FINISH_GAME,OUTPUT);
  pinMode(SCOREBOARD_LEDS,OUTPUT);
  pinMode(BUZZER, OUTPUT);

  // Señales al Arduino hijo
  digitalWrite(GOAL_YELLOW,LOW);
  digitalWrite(GOAL_BLUE,LOW);
  digitalWrite(FINISH_GAME,LOW);
  digitalWrite(START_GAME,LOW);

  // Inicialización de los leds
  digitalWrite(SCOREBOARD_LEDS, LOW);
  digitalWrite(YELLOW_LASER,LOW);
  digitalWrite(BLUE_LASER,LOW);

  // Inicialización de los anteriores
  last_button=LOW;
  lastVal_yellow=HIGH;
  lastVal_blue=HIGH;

  // Inicialización de los goles a 0
  goal_count_b=0;
  goal_count_y=0;
}

En setup() se inicializan los pines que se van a utilizar indicando si son de salida (leds, emisores láser, conexiones al Arduino hijo) o de entrada (botón y receptores laser). Se mandan las señales bajas a la otra placa para que detecte un cambio cuando se activen dichos pines, se mantienen los leds inicialmente apagados y se inicializan las variables de conteo de gol y de ultimo valor de botón y luces.

void loop() {
  // Comprobar estado del botón
  int button_value = analogRead(BUTTON);
  int button_signal=LOW;
  if(button_value>BARRIER_ON) 
    button_signal = HIGH;
  if(button_signal==HIGH){
    // Se acaba de pulsar el botón
    if(last_button==LOW){
      start_game(); 
    }
     val_yellow=
       digitalRead(YELLOW_RECEIVER);
    val_blue=
      digitalRead(BLUE_RECEIVER);
    // Gol en la portería amarilla
    if(val_yellow==HIGH && lastVal_yellow==LOW){
      // Comunicación al hijo
      digitalWrite(GOAL_YELLOW,HIGH);
      if(goal_count_b<2)   
       // marca pero no gana       
goalEffects(YELLOW_LEDS);
      else delay(1000);
      digitalWrite(GOAL_YELLOW,LOW);
      goal_count_b=goal_count_b+1;
    }
    // Gol en la portería azul
    if(val_blue==HIGH && lastVal_blue==LOW){
     // Comunicación al hijo
      digitalWrite(GOAL_BLUE,HIGH);
      if(goal_count_y<2) 
       // marca pero no gana
goalEffects(BLUE_LEDS); 
      else delay(1000);
      digitalWrite(GOAL_BLUE,LOW);
      goal_count_y=goal_count_y+1;
    }

    // Gana el jugador 1 (yellow)
    if(goal_count_y==3){
      win(YELLOW_LEDS);
    }
    // Gana el jugador 2 (blue)
    if(goal_count_b==3){
      win(BLUE_LEDS);
    }
    lastVal_yellow= val_yellow;
    lastVal_blue= val_blue;
  }
  else{
    // Se acaba de despulsar el botón de encendido
    if(last_button==HIGH) {
      finish_game();
    }
  }

  // Se guarda el valor actual del botón (pulsado o no)
  last_button=button_signal;
}

En el loop() del código, función que se estará ejecutando indefinidamente durante el uso de Sí,Hockey, se comprueba si el botón esta pulsado. Para ello, se lee el valor analógico del botón de inicio y si supera la barrera establecida, se sabe que está pulsado y se inicia el juego. Se recogen en dos variables lo que detectan los receptores láser de cada portería y en función de ello se comprueba si ha habido gol.

Si lo ha habido, es decir, si hay un cambio de señal con respecto a lo que había guardado en la variable last, se manda la señal al Arduino hijo de que ha habido gol en esa portería. Además, si aún no se ha alcanzado el máximo de goles (3), se emiten los efectos de gol. Se reinicia la señal que va a la otra placa sobre esta portería a baja (para evitar que detecte más de un gol) después de haberle estado mandando la señal de gol durante 1 segundo. Finalmente, se aumenta la variable contador de este jugador. Esto es análogo para la otra portería.

En caso de que un jugador llegue a los 3 goles, se llama a la función win para esa portería. Asimismo, se guarda tras cada iteración la ultima señal de los receptores láser para, en la siguiente vuelta del loop, comprobar si ha habido un cambio en la misma, y por tanto, un gol. Si el botón se ha dejado de pulsar, se llama a la función de fin de juego. Por último, en todas las iteraciones se guarda en una variable el ultimo valor detectado en el pin del botón.

Se presentan a continuación los circuitos por separado de cada componente con el fin de hacer más sencilla su comprensión. Nótese que en el proyecto real todos forman parte de un mismo circuito completo, pues están todos unidos al Arduino padre salvo el contador que va al Arduino hijo. Ambos Arduinos, a su vez, están interconectados.

Ventiladores y botón

Los cuatro ventiladores se conectan, por un lado, al extremo de tierra del porta-pilas de 12V y por otro, al botón (cuyo extremo opuesto va al voltaje de las pilas) que cerrará el circuito cuando este pulsado, activándolos. El botón a su vez se conecta al Arduino: a tierra a través de una resistencia y al pin analógico A4 a través de un potenciómetro (para salvar la diferencia de voltaje entre el porta-pilas de 12V y el pin de Arduino de 5V). El Arduino recibirá la señal de que el botón esta pulsado a través de dicho pin e iniciará la partida. El potenciómetro, además de conectar el pin con el botón, tiene una toma a tierra.

Marcador

El marcador de goles, implementado mediante el 7-segment 4-digit display, está conectado al Arduino hijo. Las distintas conexiones se toman de la documentación de este componente. Además de su unión a los pines digitales 2 al 9 (inclusive), cuatro de los extremos del display se unirán a través de resistencias al Arduino (pines del 10 al 13, inclusive).

Láseres

Tanto los emisores como los receptores laser cuentan con tres terminaciones: una a tierra, la otra a los 5V de la placa y la restante al pin correspondiente. Los pines de los emisores (5 y 7) son de salida, pues es la placa la que les indica activarse; mientras que los de los receptores (4 y 6) son de entrada, para que la placa detecte un cambio en la detección del laser cuando haya un gol. Se han utilizado dos pares emisor-receptor, uno para cada portería.

Conexión entre Arduinos

La conexión se realiza entre los pines analógicos (A2, A3, A4 y A5) del Arduino hijo y los digitales (8, 9, 10 y 11) del padre. Se han necesitado cuatro conexiones: una para indicar el inicio de partida, otra para fin y dos para la puntuación de cada uno de los jugadores. En todas las conexiones, es el padre quien envía una señal al hijo indicándole el cambio, para que este genere las correspondientes acciones en el marcador.

LEDs

Los LEDs que forman parte del área de juego utilizan solo 2 pines (2 y 12) del Arduino padre, uno para cada equipo, pues no se iluminarán de forma individualizada si no como conjunto, haciéndose distinción entre cada portería para señalizar el lugar del gol. Los LEDs del mismo color conforman un circuito en paralelo haciendo uso de una única resistencia, una toma a tierra y la ya mencionada conexión a la placa.

Buzzer

El zumbador o buzzer tiene un circuito muy simple conectándose por un extremo a tierra y por el otro al pin 3 de la placa para que se le envíen las señales de emisión de sonido cuando así se requiera.

LEDs marcador

Los LEDs del marcador se conectan ambos al mismo pin (13), pues se iluminarán siempre que el botón este pulsado y se apagarán cuando no lo esté. En su toma a tierra están conectados a una única resistencia.

Antes de ver los casos de uso del juego es necesario explicar y detallar el funcionamiento del mismo, para ello se ha desarrollado el siguiente diagrama de estados simplificado de manera que sea fácil entender el flujo de uso de Sí, Hockey:

Todo comienza con el sistema apagado esperando a que se pulse el botón de encendido. Cuando se presiona el botón se inician los ventiladores y se da inicio al juego, en este estado suena la melodía de comienzo y los LEDs parpadean a la vez siguiendo su ritmo, además el contador también hace una animación para dejar claro que el juego ha comenzado. Cuando se apagan los LEDs y acaba la música pasamos al estado de «Jugando» en este estado se está comprobando continuamente cuando se marca un gol en una de las dos porterías, en el momento que un jugador marca punto suena la melodía de gol y se encienden los leds de la portería donde se ha marcado el punto mostrando de una manera visual que ese gol ha sido efectivo, se actualiza el marcador y se pasa a revisar la puntuación, en el caso de que uno de los jugadores haya marcado su tercer gol se pasa al último estado, el de victoria, en este estado se vuelve a mostrar de una manera lumínica que el jugador ha ganado iluminando los LEDs de su lado del campo, el contador hace un efecto de fin de juego parpadeando ambas puntuaciones y se pone a 0 volviendo al estado de comienzo de juego.

Destacar que desde cualquier estado se puede apagar el sistema pulsando el botón de nuevo, no se ha incluido en el diagrama ya que podía quedar confuso al tener que añadir muchas conexiones.

En resumen, los casos de uso son:

  • Encender sistema: el usuario enciende el juego presionando el botón.
  • Iniciar juego: Una vez encendido el sistema, se inicia el juego automáticamente, con la melodía de inicio, parpadeo de LEDs y animación del contador.
  • Jugar: Los jugadores interactúan marcando goles en las porterías. El sistema detecta cuando se marca un gol, reproduce la melodía correspondiente, enciende los LEDs de la portería correspondiente, actualiza el marcador y verifica si algún jugador ha alcanzado tres goles.
  • Finalizar juego por victoria: Si un jugador alcanza tres goles, el sistema entra en el estado de victoria. Se muestran efectos lumínicos indicando el ganador, se realiza un efecto visual en el contador y se reinicia el marcador, volviendo al estado inicial.
  • Apagar sistema: El usuario apaga el sistema, deteniendo la actividad del juego y apagando los componentes.

Por último, en esta sección se presenta un vídeo demostración de una partida de Sí, Hockey.

Durante el desarrollo de este proyecto, nos hemos encontrado muchos y diversos inconvenientes que han derivado en dedicarle más horas de las esperadas y en cambios de última hora.

Falta de pines

El principal problema del proyecto fue la falta de pines en la placa Arduino, pues eran muchos los necesarios para conectar todo, más aún teniendo en cuenta que solo el marcador (4-digit 7-segment display) utiliza 12 pines digitales. Por ello, se optó por utilizar un multiplexor, un registro de desplazamiento o un Arduino “mega”, de más pines; pero estas opciones fueron descartadas. Aprovechando la disponibilidad de una placa Arduino que uno de nosotros tenía, decidimos utilizar dos placas interconectadas para duplicar los pines.

Comunicación entre Arduinos

La solución al problema anterior trajo consigo un nuevo inconveniente: la comunicación entre las dos placas. Tras investigar mucho sobre ello, se asentó la idea de utilizar uno de “esclavo”, en nuestro caso, “hijo”; y otro principal, “padre”. Tras probar todas las conexiones posibles, se dio con la combinación más adecuada: usar los analógicos del Arduino hijo (pues sus digitales estaban ocupadas por el marcador) y los digitales del Arduino padre (mas sencillos de utilizar en código).

Botón de inicio

Otro de los problemas fue el uso de un mismo botón para conectar el circuito de los ventiladores (alimentados por una pila de 12V) y el circuito de los Arduinos (cuyos pines no soportan más de 5V). Para solventarlo sin que el voltaje de la pila afecte al Arduino, se utilizó un potenciómetro para regular la corriente que pasa.

Alimentación

La alimentación tanto de los ventiladores como de las placas Arduino supuso otro problema. Los ventiladores hacían uso de un porta-pilas de ocho baterías de 1.5V cada una que, llegado un punto del proceso, dejaron de funcionar. Para solucionarlo, tuvimos que comprar nuevas pilas y reemplazar a las anteriores, que solo con el montaje ya se habían consumido.

Por su parte, la placa Arduino no se podía alimentar con la pila de 9V por ser insuficiente para todos los componentes. Además, la necesidad de enchufar las dos placas simultáneamente obligó a buscar otra fuente de alimentación. Tras varios quebraderos de cabeza barajando la posibilidad de conectarlo a la corriente eléctrica, dimos con la solución: una batería portátil (powerbank). Para ello, compramos un cable USB para la placa Arduino extra y conectamos ambas a esta fuente de alimentación, consiguiendo el voltaje necesario.

Problemas de carpintería

En cuestión de montaje, la elección de materiales fue un problema. En primer lugar, la elección entre usar cartón (más manejable y barato) o contrachapado (más resistente y estable) acabó en utilizar ambos. Por un lado, la estructura general con contrachapado (lo que conllevó mayor trabajo de atornillado y taladrado) y los otros componentes (porterías y estructura del marcador) en cartón.

Por otro lado, hacerse con la placa de metacrilato supuso un problema ya que no podían cortarla a nuestra medida, por no hablar de agujerearla. Nos vimos finalmente obligados a devolver la placa inicialmente comprada, y pedir una específicamente hecha a nuestro diseño, mucho más cara.

Problemas menores

De manera adicional, surgieron otros inconvenientes. El diseño inicial planteado para dos ventiladores se quedó corto en comparación con la necesidad de aire para el deslizamiento del disco, por lo que tuvimos que ampliarlo a cuatro ventiladores. Además, los emisores laser fallaban constantemente, ya que, si no estaban directamente apuntando a los receptores, no se detectaba el obstáculo (disco al pasar simulando el gol); solventar esto llevó mucho tiempo e intentos.

A esto se sumó que el circuito de los LEDs del marcador, destinados a indicar qué número correspondía a qué jugador según el color, falló. Después de numerosos intentos por arreglarlo, resultó imposible dado que el problema residía en uno de los empalmes de su circuito, inaccesibles llegado un punto del desarrollo del proyecto. A pesar de ello, se mantuvieron, precisamente porque retirarlos suponía deshacer parte de la estructura.

Por último, aunque inicialmente buscábamos evitar soldar los empalmes, la inconsistencia de estos (unidos con cinta adhesiva), especialmente los que se conectaban a los láseres (más inestables), puso de manifiesto la necesidad de soldar. Debido a la inexperiencia del equipo con el soldado y la dificultad que suponía, solo los empalmes más inestables (láseres) o primordiales (ventiladores) fueron soldados.

A modo de conclusión, podemos decir que hemos logrado cumplir con los objetivos establecidos en un primer momento. Se ha conseguido diseñar montar un sistema empotrado funcional que cumple con los requisitos que se pedían. Además, se cuenta con un diseño robusto y fiel en cuanto a hardware y flexible y abierto a futuros escenarios en cuanto a software, por ejemplo, para desarrollos futuros sin necesidad de modificar el hardware se podrían añadir diferentes modos de juego y efectos de sonido o luz que hubiéramos añadido de no ser por la falta de tiempo, pero que tendremos en cuenta para próximas actualizaciones.

Por otra parte, se ha conseguido un buen resultado favoreciendo y fortaleciendo conocimientos de la asignatura. Además, cabe destacar que ninguno de los cuatro integrantes habíamos hecho proyectos de tan gran nivel con Arduino, siendo de gran satisfacción el haber logrado el montaje de un sistema empotrado que funciona a tiempo real con la interacción del usuario. Esta satisfacción es aún mayor teniendo en cuenta que se han empleado cerca de 70 horas en finalizar el proyecto en su totalidad, contando fases de carpintería, testeo y montaje de circuitos y desarrollo completo del código.

También te podría gustar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *