LOCOFÓN: Teléfono bop-it

Para este trabajo se ha decidido explotar al máximo la cantidad de componentes que se nos proporciona en el kit y, para ello, se ha tomado la decisión de hacer algo sencillo pero que bien cumple con esto último. El proyecto en cuestión consiste en un juguete que sigue la lógica del típico “Simón Dice”; es decir, el juguete pedirá diversas tareas que requerirán que el usuario interactúe con los componentes en un tiempo límite y este, en caso de hacerlo en correcta manera, sumará puntuación reduciendo el tiempo gradualmente y, en caso contrario, se perderá la partida.

Así pues, y a modo de resumen, el proyecto usa componentes para generar tareas sencillas de entrada y lectura, e incitan al jugador a conseguir la mayor puntuación posible.

Circuito

En primer lugar, se diseña la electrónica en la web “WokWi” con el fin de modificar todo con facilidad y probar código sin necesidad de montar el circuito en las sesiones de prueba.

Así pues, y teniendo como objetivo el poder tratar de manera separada cada componente por programación, se procura diseñar distintos circuitos cerrados por cada componente, en lugar de un único circuito en serie. 

Como se puede apreciar en el diagrama mostrado más abajo, se han utilizado gran cantidad de componentes y, con ellos, la mayor parte de los pines disponibles en la placa. Los componentes utilizados son los siguientes:

  • Sensor ultrasonido
  • Zumbador
  • LCD I2C
  • LED RGB (Junto con tres resistencias para evitar excesos de potencia)
  • x3 Botones
  • Potenciómetro
  • Fotorresistor
  • Joystick

Una vez diseñado y testeado todo digitalmente, se procede a su construcción electrónica.

Como se puede observar, por motivos funcionales y con el fin de ajustar el circuito a la carcasa, se tienen diversos cambios con respecto al diagrama inicial. Estos son:

  • Se decide separar los botones y el led en una placa pequeña separada con el fin de elevar estos para ser accesibles una vez cerrado el prototipo
  • Se conecta el joystick directamente al arduino UNO para poderlo sacar de la placa protoboard y acercarlo a la pared de la carcasa para que sea accesible por el usuario.

Código

El código consiste de de dos funciones principales, setup() y loop(), y cinco funciones secundarias que gestionan las distintas órdenes del juego Joystick(), Potenciometro(), Ultrasonic(long d), RGBLed() y LuzSensor().
En el Setup se inicializan los valores de todos los componentes y pines, de las variables necesarias para el juego (tiempoInicioOrden, numComponente, puntuación…), y se hace la secuencia de inicio del componente, en la cual el lcd muestra preparados..listos..ya!.
En cada paso del Loop se actualizan los valores de todos los pines, se actualiza el temporizador y se muestra por pantalla, se comprueba la condición de victoria, y se comprueba la condición de derrota.
En todas las secciones de los componente se utiliza el mismo sistema, que consiste en un “setup” mediante el uso de un booleano que solo se ejecuta una vez por componente, que muestra por pantalla la orden, y en cada componente gestiona la orden, por ejemplo, en los botones y el joystick asigna el número aleatorio que decide la orden, y en el potenciómetro, distingue en qué posición inicia el componente.
Tras esto, comprueba en cada paso del loop si se ha cumplido la condición que toque dependiendo de la orden y el componente

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

//Crear el objeto lcd  dirección  0x3F y 16 columnas x 2 filas
LiquidCrystal_I2C lcd(0x27,16,2);  //

// Joystick
const int pinJoyX = A0;
const int pinJoyY = A1;
int Xvalue = 0;
int Yvalue = 0;

// Ultrasonnic sensor
const int Trigger = 3;  
const int Echo = 2;  
long t; //timepo que demora en llegar el eco
long d; //distancia en centimetros
// Buzzer
const int buzzerPin = 10;

// Potentiometer
const int potenPin = A2;

// RGB buttons
const int gButPin = 6;
const int bButPin = 5;
const int rButPin = 4;
int bButtonState = 0;
int rButtonState = 0;
int gButtonState = 0;

const int gLedPin = 8;
const int bLedPin = 9;
const int rLedPin = 7;

//Sensor de luz
const int luzPin = A3;
int luzValue = 0;

// VARIABLES DEL PROGRAMA

// Tiempo
float intervaloMax = 7;
float contadorIntervalo = 0;
float loopTime;
unsigned long tiempoInicioOrden; // Momento en que comenzó la orden actual
unsigned long intervaloMaxMs = (unsigned long)(intervaloMax * 1000);
unsigned long currentMillis;
// Seleccion de componente
int numComponente;

// Gestion del juego
bool ordenCompletada = false;
int puntuacion = 0;
bool hasPerdido = false;
bool compSetup = true;
int joyRand = 0;
bool potVal = true;
int randRGB;



void setup() {
  Serial.begin(9600);
  //LDC
  // Inicializar el LCD
  lcd.init();
  randomSeed(analogRead(pinJoyX) + millis());
  //Encender la luz de fondo.
  lcd.backlight();
  //Ultrasonic sensor
  pinMode(Trigger, OUTPUT); //pin como salida
  pinMode(Echo, INPUT);  //pin como entrada

  // BUZZER
  pinMode(buzzerPin, OUTPUT);

  //Botones RGB
  pinMode(rButPin, INPUT);
  pinMode(gButPin, INPUT);
  pinMode(bButPin, INPUT);

  //Led RGB
  pinMode(rLedPin, OUTPUT);
  pinMode(gLedPin, OUTPUT);
  pinMode(bLedPin, OUTPUT);

  //Sensor de luz
  pinMode(luzPin, INPUT);
  Serial.begin(9600);

  
  //hace un sonidito
  lcd.clear();
  lcd.print("Preparado...");
  tone(buzzerPin, 8000);
  delay(1000);
  noTone(buzzerPin);
  lcd.clear();
  lcd.print("Listo...");
  tone(buzzerPin, 8000);
  delay(1000);
  noTone(buzzerPin);
  lcd.clear();
  lcd.print("Ya!");
  tone(buzzerPin, 10000);
  delay(1000);
  noTone(buzzerPin);
  lcd.clear();
  //comienza
  tiempoInicioOrden = millis();
  numComponente = random(1, 6); //Del 1 al 5. Sí, el 6 no entra
}

void loop() {
  currentMillis = millis();
  // INICIALIZACION DE VALORES DEL COMPONENTE
  unsigned long startTime = millis();
  // Joystick
  Xvalue = analogRead(pinJoyX);
  delay(100);            //es necesaria una pequeña pausa entre lecturas analógicas
  Yvalue = analogRead(pinJoyY);
  // Ultrasonic sensor
  digitalWrite(Trigger, LOW);
  delayMicroseconds(2);
  digitalWrite(Trigger, HIGH);
  delayMicroseconds(10);          //Enviamos un pulso de 10us
  digitalWrite(Trigger, LOW);
  t = pulseIn(Echo, HIGH); //obtenemos el ancho del pulso
  d = t*.0343/2;             //escalamos el tiempo a una distancia en cm
  //BOTONES RGB
  rButtonState = digitalRead(rButPin);
  gButtonState = digitalRead(gButPin);
  bButtonState = digitalRead(bButPin);
  //SENSOR DE LUZ
  luzValue = analogRead(luzPin);
  //Serial.println(luzValue);
  //MOSTRAR TEMPO
  lcd.setCursor(14,1);
  lcd.print(intervaloMaxMs-((currentMillis - tiempoInicioOrden)));
  lcd.setCursor(1,0);
  // INICIALIZACION DE VALORES DEL PROGRAMA

  if(ordenCompletada){

  // Sonido buzzer correcto
  tone(buzzerPin, 10000);
  delay(100);
  noTone(buzzerPin);

  // Subir puntos
  puntuacion++;
  lcd.clear();
  lcd.print("puntuacion");
  lcd.setCursor(12,0);
  lcd.print(puntuacion);
  lcd.setCursor(1,0);
  delay(1000);
  lcd.clear();
  //Reducir timepo máximo
  intervaloMax -= 0.5;
  intervaloMax = max(1, intervaloMax);
  intervaloMaxMs = (unsigned long)(intervaloMax * 1000);

  // Generar nueva orden
  numComponente = random(1, 6); //Del 1 al 5. Sí, el 6 no entra
  // Pasar a siguiente orden
  ordenCompletada = false;
  contadorIntervalo = 0;
  tiempoInicioOrden = millis();
  currentMillis = millis();
  compSetup = true;
  lcd.clear();

  }
    if(currentMillis - tiempoInicioOrden > intervaloMaxMs || hasPerdido){

    // PERDER JUEGO
    digitalWrite(rLedPin, LOW);
    digitalWrite(gLedPin, LOW);
    digitalWrite(bLedPin, LOW);
    lcd.clear();
    lcd.print("has perdido");
    lcd.setCursor(0, 1);
    lcd.print("puntuacion");
    lcd.setCursor(12,1);
    lcd.print(puntuacion);
    lcd.setCursor(1,0);
    // Sonido malo
    tone(buzzerPin, 2000);
    delay(2000);
    noTone(buzzerPin);
    // Pantalla LCD perdido
    puntuacion = 0;
    // Mostrar puntuacion
  lcd.clear();
  lcd.print("Reiniciando en");
  lcd.setCursor(0,1);
  lcd.print("3");
  delay(1000);
  lcd.setCursor(2,1);
  lcd.print("2");
  delay(1000);
  lcd.setCursor(4,1);
  lcd.print("1");
  delay(1000);


  lcd.clear();
  lcd.print("Preparado...");
  tone(buzzerPin, 8000);
  delay(1000);
  noTone(buzzerPin);
  lcd.clear();
  lcd.print("Listo...");
  tone(buzzerPin, 8000);
  delay(1000);
  noTone(buzzerPin);
  lcd.clear();
  lcd.print("Ya!");
  tone(buzzerPin, 10000);
  delay(1000);
  noTone(buzzerPin);
  lcd.clear();
  delay(100);
  //comienza
  intervaloMax = 7;
  intervaloMaxMs = (unsigned long)(intervaloMax * 1000);
  tiempoInicioOrden = millis();
  numComponente = random(1, 6); //Del 1 al 5. Sí,   el 6 no entra

compSetup = true;
  }

    switch(numComponente){
      case 1: //Joystick
        
        JoyStick();
        
        break;

      case 2: //Potenciometro
        
        Potenciometro();

        break;

      case 3: //Ultrasonidos

        Ultrasonic(d);

        break;

      case 4: //Botones RGB
        
        RGBLed();
        break;

      case 5: //Sensor de luz
        
        LuzSensor();
        break;

    }


}

void JoyStick(){
  if(compSetup == true){
          // Mostrar texto 
          lcd.print("Mueve el joystick");
          lcd.setCursor(0, 1);
          joyRand = random()%4 + 1;
          switch(joyRand){
            case 1:
              lcd.print("Arriba");
              break;
            case 2:
              lcd.print("Abajo");
              break;
            case 3:
              lcd.print("Derecha");
              break;
            case 4:
              lcd.print("Izquierda");
              break;
            default:
              break;
          }
        }
  Serial.print(Xvalue);
  Serial.println(Yvalue);
  switch(joyRand){
    case 1:
      if(Xvalue < 100){
        ordenCompletada = true;
      }
      break;
    case 2:
      if(Xvalue > 900){
        ordenCompletada = true;
      }
      break;
    case 3:
      if(Yvalue < 100){
        ordenCompletada = true;
      }
      break;
    case 4:
      if(Yvalue > 900){
        ordenCompletada = true;
      }
      break;
  }
  compSetup = false;
}
  
void Potenciometro(){
  if(compSetup == true){
    lcd.print("Volumen");
    if(analogRead(potenPin) > 500){
      potVal = true;
    } else {
      potVal = false;
    }
  }
  Serial.print(potVal);
  if(potVal){
    if(analogRead(potenPin) < 100){
      ordenCompletada = true;
    }
  } else {
    if(analogRead(potenPin) > 900){
      ordenCompletada = true;
    }
  }

  compSetup = false;
}


void Ultrasonic(long d){
    if(compSetup == true){
      lcd.print("Distancia");
    }
    Serial.println(d);
    if(d<20 && d>15){
      ordenCompletada = true;
    }
    compSetup=false;
  }

void RGBLed(){
  if(compSetup == true){
      lcd.print("Botones");
        randRGB = random()%3 + 1;
        switch(randRGB){
            case 1:
              digitalWrite(rLedPin, HIGH);
              break;
            case 2:
              digitalWrite(gLedPin, HIGH);
              break;
            case 3:
              digitalWrite(bLedPin, HIGH);
              break;
            default:
              break;
          }
        
      }
    switch(randRGB){
            case 1:
              if(rButtonState == HIGH){
                  ordenCompletada = true;
                  digitalWrite(rLedPin, LOW);
              }
              break;
            case 2:
              if(gButtonState == HIGH){
                  ordenCompletada = true;
                  digitalWrite(gLedPin, LOW);
              }
              break;
            case 3:
              if(bButtonState == HIGH){
                  ordenCompletada = true;
                  digitalWrite(bLedPin, LOW);
              }
              break;
            default:
              break;
          }
    compSetup=false;
}

void LuzSensor(){
  if(compSetup == true){
      lcd.print("Sensor de luz");
    }
  Serial.println(luzValue);
  if(luzValue > 200){
    ordenCompletada = true;
  }
  compSetup=false;
}

Empotrado

A la hora de hacer el empotrado, se comenzó haciendo varios bocetos hasta encontrar la idea final para el diseño.


Finalmente, teniendo en cuenta el tamaño de los componentes, que obligaba a que el prototipo fuese de un tamaño medianamente grande o alargado, y sabiendo que el público objetivo principal serían niños, se decidió tomar un diseño que fuese divertido y a la vez compacto.
Para ello, según la colocación de los componentes pensada, el conector de la pila tendría que sobresalir por arriba, lo que podía asimilarse con la antena de un teléfono antiguo. De este modo, la idea final acabó siendo que la temática del juego fuese la interacción con los componentes del arduino como si de los botones de un teléfono se tratase.
Así pues, se realizó un sketch final del prototipo del teléfono, con la colocación de los componentes y las medidas tomadas para posteriormente ser modelado.

Para el modelado en 3D del prototipo se utilizó la aplicación Blender. A pesar de que puede que no sea el mejor programa de modelado 3D para hacer piezas, es el que mejor controlamos y más fácil nos resulta. 

Activando ciertas opciones dentro del programa, se pueden ver las medidas del modelo, por lo que es muy útil a la hora de ser algo más precisos, además de modificar el grosor de todas las paredes a la vez del modelo según se necesite, sin tener que ir cara a cara cambiando la medidas. Como se puede observar, el modelo contiene todos los espacios abiertos, de diferentes formas y tamaños, para los distintos componentes, y la forma da como resultado un teléfono antiguo, que cumple con el objetivo estético del empotrado.

El resultado final del empotrado es el siguiente:
Como se puede observar, a pesar de las complicaciones técnicas, presupuestarias y de diseño, se consigue un acabado funcional y bastante compacto, al que posteriormente se añaden decoraciones con pintura con el fin de acercar el prototipo en mayor medida a la estética deseada como es la de un teléfono antiguo.

Costes de los materiales

Haciendo un cálculo estimado de los costes del empotrado, se comprueba que el precio estimado ha de estar entre 40 y 50 euros, dependiendo del lugar donde se obtengan los componentes.

No obstante, los gastos personales han sido mínimos, pues además de todos los componentes ofrecidos por el centro, los que se han empleado de manera externa han sido cedidos por conocidos que trabajan con este tipo de tecnologías. Incluso en cuanto a la impresión, se ha contado con la ayuda de la asociación universitaria “La Caverna del Dragón”, que han permitido el uso de su impresora 3D.

El único gasto real ha sido para adquirir la pila que alimenta el prototipo. 

COMPONENTEPRECIO
Microcontrolador Elegoo Uno R3 x16,00€
830-Tie Points Breadboard x1 3,50€
Mini Breadboard x11,09€
Joystick x14,89€
Ultrasonic Sensor x11,40€
Buzzer x10,90€
LCD1602 x15,99€
I2C x15,00€
RGB Led x10,10€
Fotoresistor x11,10€
Botones x30,69€
Potenciómetro x10,70€
Jumpers Macho-Macho x200,90€
Jumpers Macho-Hembra x50,23€
Resistencias x30,06€
Pila 9V x12,30€
Conector de pila x11,50€
Filamento de impresión x110,00€
TOTAL46,35€

Casos de uso

El sistema desarrollado presenta un juego, parecido al clásico “Bop-it”, donde el juguete manda una instrucción que el jugador debe realizar antes de que se acabe un límite de tiempo. Estas instrucciones se presentan en la pantalla LCD. A continuación, se describen los principales casos de uso del sistema:

Caso de uso 1: Inicio de partida
El usuario enciende el dispositivo conectando la fuente eléctrica. Tras ello, el LCD se encenderá y se iniciará una cuenta atrás para que el jugador se prepare para comenzar el juego.

Caso de uso 2: Generación de orden aleatoria
Una vez iniciada la partida, el sistema selecciona de manera aleatoria uno de los componentes disponibles (botones, joystick, potenciómetro, sensor de luz, sensor ultrasónico o LED RGB). Esta selección decide cual es la tarea que el usuario deberá realizar a continuación para seguir jugando.

Caso de uso 3: Interacción mediante botones
El sistema solicita al usuario pulsar el botón del color concreto entre los disponibles (rojo, azul y verde). En el LCD mostrará la instrucción y el color será indicado por el color del LED debajo de los botones. El jugador debe identificar y presionar el botón correcto.

Caso de uso 4: Interacción mediante joystick
El juego pide al usuario mover el joystick en una dirección específica (arriba, abajo, izquierda o derecha). Se comprueba continuamente la posición del joystick hasta que el jugador lo mueve en la dirección adecuada.

Caso de uso 5: Interacción mediante potenciómetro
El sistema solicita al usuario modificar la posición del potenciómetro, el cual simboliza una rueda de volumen con la estética y temática del teléfono. El movimiento deberá ser de un extremo a otro, dependiendo de donde se haya quedado en la acción anterior.

Caso de uso 6: Interacción mediante sensor ultrasónico
El sistema requiere que el usuario acerque o aleje la mano del sensor ultrasónico hasta alcanzar una distancia determinada. El sistema mide la distancia detectada y lo dará como válido cuando sea la distancia justa.

Caso de uso 7: Interacción mediante sensor de luz
El sistema pide al usuario tapar el agujero del modelo 3D donde se ubica el fotorresistor, provocando que entre menos luz. El sistema evalúa el cambio de luminosidad para determinar si la orden ha sido cumplida correctamente.

Caso de uso 8: Acierto de la orden
Si el usuario cumple correctamente la orden que se le pide dentro del tiempo límite, el sistema incrementa la puntuación. Además, se irá reduciendo ligeramente el tiempo disponible para la siguiente orden para aumentar progresivamente la dificultad del juego, hasta llegar a un tiempo límite de 2 segundos para realizar cada tarea.

Caso de uso 9: Error o agotamiento del tiempo
Si el usuario no realiza la acción correcta en el tiempo límite, el sistema considera la partida como perdida. Se muestra el resultado final en el LCD y el juego se reinicia, dando unos segundos al jugador para que asimile la derrota y vuelva a prepararse para la siguiente partida.

Demostración de uso

La demostración del uso se encuentra en el siguiente enlace de drive:

https://drive.google.com/file/d/1BbAeavCJIsj8CynCcXDfWkleBtfgeoXS/view?usp=sharing

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 *