STEMGAME ESP32 LCD INTEGRADO

 

ESQUEMA FRITZING


Permite usar un teclado flexible 1x4 o 4 botones físicos, si usa teclado flexible (2 - 1 - 4 - 3 - GND)


*PCB sin botones físicos - Requiere flex.


CODIGO ARDUINO

//STEMGAME ESP32 CON PANTALLA INTEGRADA
//CONFIGURACION DE BOTONES PULLUP CON USO DE RESISTENCIA INTERNA
//NUMEROS ENTEROS - NUMEROS DECIMALES - QUIMICA - BIOLOGIA

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>

#define TFT_CS        15
#define TFT_RST        4
#define TFT_DC         2
#define TFT_BL         32   // Pin para controlar el backlight

// Definición de pines
#define BUTTON_A 25
#define BUTTON_B 27
#define BUTTON_C 14
#define BUTTON_D 13
#define BUZZER 16
const int botones[] = {BUTTON_A, BUTTON_B, BUTTON_C, BUTTON_D};

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

// ========== VARIABLES GLOBALES ==========
int juegoActual = 0; // 0: Menú, 1: Enteros, 2: Decimales, 3: Química, 4: Biología
bool juegoSeleccionado = false;

// ========== VARIABLES PARA JUEGOS MATEMÁTICOS ==========
// Variables para el juego de números enteros
float scoreEnteros = 0;
int currentQuestionEnteros = 0;
int totalQuestionsEnteros = 10;
unsigned long startTimeEnteros = 0;
unsigned long totalTimeEnteros = 0;
bool juegoEnProgresoEnteros = false;

struct Question {
  int num1;
  int num2;
  char operation;
  int correctAnswer;
  int options[4];
};

Question questionsEnteros[10];

// Variables para el juego de números decimales
float puntuacionDecimales = 0;
int preguntaActualDecimales = 0;
unsigned long tiempoInicioDecimales = 0;
unsigned long tiempoFinalDecimales = 0;
bool midiendoTiempoDecimales = false;

float num1Decimales, num2Decimales, resultadoDecimales;
char opcionesDecimales[4][10];
int respuestaCorrectaDecimales;

// ========== VARIABLES PARA QUIZZES ==========
int pregunta_actual = 0;
int puntuacion = 0;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 200;
int preguntas_seleccionadas[10];

// Datos de Química
const char* preguntas_quimica[] = {
  "Simbolo quimico del agua?",       // A (H2O)
  "Elemento mas abundante?",         // B (Silicio)
  "Que es un atomo?",                // A (Unidad basica)
  "Formula del Dioxido de carbono?", // C (CO2)
  "Que gas liberan las plantas durante la fotosintesis",      // B (Organizacion)
  "pH de solucion neutra?",          // A (7)
  "Que es un ion?",                  // B (Atomo cargado)
  "Gas noble mas ligero?",           // A (Helio)
  "Reaccion quimica?",               // C (Cambio composicion)
  "Elemento mas ligero?",            // A (Hidrogeno)
  "Que es compuesto quimico?",       // B (Sustancia pura)
  "Simbolo del oro?",                // B (Au)
  "Que es catalizador?",             // A (Acelera reacciones)
  "Formula del amoniaco?",           // B (NH3)
  "Que es isotopo?",                 // C (Atomo con neutrones extra)
  "Elemento mas reactivo?",          // A (Fluor)
  "Donde se da enlace covalente?",   // B (Entre no metales)
  "Formula acido sulfurico?",        // C (H2SO4)
  "Que es un acido?",                // A (Donador protones)
  "Simbolo del hierro?",             // A (Fe)
  "Metal alcalino?",                 // A (Grupo 1)
  "Formula del metano?",             // A (CH4)
  "Que es polimero?",                // B (Macromolecula)
  "Elemento mas pesado?",            // A (Uranio)
  "Enlace ionico?",                  // C (Entre iones)
  "Formula del ozono?",              // B (O3)
  "Que es gas noble?",               // A (Grupo 18)
  "Simbolo del sodio?",              // A (Na)
  "Que es anion?",                   // B (Ion negativo)
  "Formula agua oxigenada?"          // A (H2O2)
};

const char* respuestas_quimica[][3] = {
  {"A. H2O", "B. CO2", "C. NaCl"},
  {"A. Oxigeno", "B. Silicio", "C. Hierro"},
  {"A. Unidad basica", "B. Molecula", "C. Compuesto"},
  {"A. H2O", "B. O2", "C. CO2"},
  {"A. Dioxido de carbono (CO₂)", "B. Oxigeno (O₂)", "C. Nitrogeno (N₂)"},
  {"A. 7", "B. 5", "C. 10"},
  {"A. Atom neutro", "B. Atom cargado", "C. Molecula"},
  {"A. Helio", "B. Neon", "C. Argon"},
  {"A. Cambio fisico", "B. Cambio estado", "C. Cambio composicion"},
  {"A. Hidrogeno", "B. Helio", "C. Litio"},
  {"A. Mezcla", "B. Sust pura", "C. Elemento"},
  {"A. Ag", "B. Au", "C. Fe"},
  {"A. Acelera", "B. Inhibe", "C. No afecta"},
  {"A. NH4", "B. NH3", "C. NO2"},
  {"A. Enlace", "B. Molecula", "C. Atom +neutr"},
  {"A. Fluor", "B. Sodio", "C. Oro"},
  {"A. Entre metales", "B. Entre no metales", "C. Entre iones"},
  {"A. H2SO3", "B. H2CO3", "C. H2SO4"},
  {"A. Donador H+", "B. Aceptor H+", "C. Neutro"},
  {"A. Fe", "B. Au", "C. Ag"},
  {"A. Grupo 1", "B. Grupo 2", "C. Grupo 17"},
  {"A. CH4", "B. C2H6", "C. C3H8"},
  {"A. Ion", "B. Macromolec", "C. Enlace"},
  {"A. Uranio", "B. Oro", "C. Plomo"},
  {"A. No metales", "B. Metales", "C. Iones"},
  {"A. O2", "B. O3", "C. CO2"},
  {"A. Grupo 18", "B. Grupo 1", "C. Grupo 2"},
  {"A. Na", "B. K", "C. Li"},
  {"A. Ion +", "B. Ion -", "C. Atom neutro"},
  {"A. H2O2", "B. H2O", "C. HO"}
};

const int respuestas_correctas_quimica[] = {0,1,0,2,1,0,1,0,2,0,1,1,0,1,2,0,1,2,0,0,0,0,1,0,2,1,0,0,1,0};

// Datos de Biología
const char* preguntas_biologia[] = {
  "Organelo celular de la respiracion?",       // B (Mitocondria)
  "Molecula portadora de energia?",            // A (ATP)
  "Que es el ADN?",                            // C (Material genetico)
  "Funcion de los ribosomas?",                 // A (Sintesis proteica)
  "Tejido que transporta nutrientes?",        // B (Floema)
  "Organismo unicelular procariota?",          // C (Bacteria)
  "Proceso de division celular?",              // A (Mitosis)
  "Hormona del crecimiento vegetal?",          // B (Auxina)
  "Capa protectora de la piel?",               // A (Epidermis)
  "Organismo productor en ecologia?",          // A (Planta)
  "Parte del cerebro para memoria?",           // B (Hipocampo)
  "Enzima que digiere carbohidratos?",         // A (Amilasa)
  "Celula reproductora femenina?",             // B (Ovulo)
  "Proceso de fotosintesis ocurre en?",        // C (Cloroplasto)
  "Sistema que incluye el corazon?",           // A (Circulatorio)
  "Grupo sanguineo donante universal?",        // B (O negativo)
  "Organo filtrador de sangre?",               // A (Rinon)
  "Vitaminas liposolubles?",                   // C (A, D, E, K)
  "Que son los gametos?",                      // A
  "Hueso mas largo del cuerpo?",               // B (Femur)
  "Glandula productora de insulina?",          // A (Pancreas)
  "Proceso de paso de agua por membrana?",     // B (Osmosis)
  "Estructura que produce polen?",             // C (Antera)
  "Numero de cromosomas humanos?",             // A (46)
  "Organismo descomponedor?",                  // B (Hongo)
  "Hormona del estres?",                       // A (Cortisol)
  "Biomolecula formada por aminoacidos?",  // C
  "Organo del olfato?",                        // A (Nariz)
  "Ciencia que estudia los tejidos?",          // B (Histologia)
  "Proceso de formacion de gametos?"           // A (Gametogenesis)
};

const char* respuestas_biologia[][3] = {
  {"A. Nucleo", "B. Mitocondria", "C. Lisosoma"},
  {"A. ATP", "B. ADN", "C. ARN"},
  {"A. Proteina", "B. Carbohidrato", "C. Material genetico"},
  {"A. Sintesis proteica", "B. Produccion ATP", "C. Digestión"},
  {"A. Xilema", "B. Floema", "C. Epidermis"},
  {"A. Levadura", "B. Alga", "C. Bacteria"},
  {"A. Mitosis", "B. Meiosis", "C. Fotosintesis"},
  {"A. Giberelina", "B. Auxina", "C. Citocinina"},
  {"A. Epidermis", "B. Dermis", "C. Hipodermis"},
  {"A. Planta", "B. Hongo", "C. Animal"},
  {"A. Cerebelo", "B. Hipocampo", "C. Talamo"},
  {"A. Amilasa", "B. Lipasa", "C. Proteasa"},
  {"A. Esperma", "B. Ovulo", "C. Zigoto"},
  {"A. Mitocondria", "B. Nucleo", "C. Cloroplasto"},
  {"A. Circulatorio", "B. Nervioso", "C. Digestivo"},
  {"A. AB positivo", "B. O negativo", "C. B positivo"},
  {"A. Rinon", "B. Higado", "C. Pulmon"},
  {"A. B, C", "B. Complejo B", "C. A, D, E, K"},
  {"A. Celula reproductiva", "B. Semillas secas", "C. Parasitos"},
  {"A. Tibia", "B. Femur", "C. Humero"},
  {"A. Pancreas", "B. Higado", "C. Tiroides"},
  {"A. Difusion", "B. Osmosis", "C. Transp activo"},
  {"A. Estigma", "B. Estilo", "C. Antera"},
  {"A. 46", "B. 23", "C. 48"},
  {"A. Bacteria", "B. Hongo", "C. Planta"},
  {"A. Cortisol", "B. Adrenalina", "C. Insulina"},
  {"A. Carbohidratos", "B. Lipidos", "C. Proteinas"},
  {"A. Nariz", "B. Lengua", "C. Oido"},
  {"A. Citologia", "B. Histologia", "C. Embriologia"},
  {"A. Gametogenesis", "B. Fecundacion", "C. Ovulacion"}
};

const int respuestas_correctas_biologia[] = {1,0,2,0,1,2,0,1,0,0,1,0,1,2,0,1,0,2,0,1,0,1,2,0,1,0,2,0,1,0};

// ========== CONFIGURACIÓN INICIAL ==========
void setup() {
  Serial.begin(9600);
 
  // Configurar pines con PULLUP
  for(int i = 0; i < 4; i++) {
    pinMode(botones[i], INPUT_PULLUP);
  }
  pinMode(BUZZER, OUTPUT);
  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, HIGH); // Encender backlight (HIGH para encender, LOW para apagar)
 
  randomSeed(analogRead(12));

  // Inicializar pantalla ST7789
  tft.init(170, 320);
  tft.setRotation(3);
  tft.invertDisplay(true);
  tft.fillScreen(ST77XX_BLACK);

  mostrarMenuPrincipal();
}

// ========== BUCLE PRINCIPAL ==========
void loop() {
  if (!juegoSeleccionado) {
    int opcion = esperarSeleccionMenu();
    juegoSeleccionado = true;
    juegoActual = opcion + 1;
   
    switch(juegoActual) {
      case 1: iniciarJuegoEnteros(); break;
      case 2: iniciarJuegoDecimales(); break;
      case 3: iniciarQuizQuimica(); break;
      case 4: iniciarQuizBiologia(); break;
    }
  } else {
    switch(juegoActual) {
      case 1: loopEnteros(); break;
      case 2: loopDecimales(); break;
      case 3: loopQuizQuimica(); break;
      case 4: loopQuizBiologia(); break;
    }
  }
}

// ========== FUNCIONES DEL MENÚ ==========
void mostrarMenuPrincipal() {
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW); // Título en amarillo
  centerText("SELECCIONE JUEGO", 5);
 
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_CYAN); // Opciones en cian
  tft.setCursor(0, 30);
  tft.println("A: Numeros Enteros");
  tft.println("B: Numeros Decimales");
  tft.println("C: Quiz de Quimica");
  tft.println("D: Quiz de Biologia");
}

int esperarSeleccionMenu() {
  while(true) {
    if (debounce(BUTTON_A)) return 0;
    if (debounce(BUTTON_B)) return 1;
    if (debounce(BUTTON_C)) return 2;
    if (debounce(BUTTON_D)) return 3;
    delay(10);
  }
}

void reiniciarMenu() {
  juegoSeleccionado = false;
  juegoActual = 0;
  mostrarMenuPrincipal();
}

// ========== FUNCIONES COMUNES ==========
bool debounce(int button) {
  static unsigned long lastDebounceTime = 0;
  // Cambiado a LOW porque ahora son pull-up (presionado = LOW)
  if (digitalRead(button) == LOW && millis() - lastDebounceTime > 200) {
    lastDebounceTime = millis();
    return true;
  }
  return false;
}

void centerText(const char *text, int y) {
  int16_t x1, y1;
  uint16_t w, h;
  tft.getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
  int x = (tft.width() - w) / 2;
  tft.setCursor(x, y);
  tft.print(text);
}

void playFunTone() {
  tone(BUZZER, 1000, 200);
  delay(200);
  tone(BUZZER, 1500, 200);
  delay(200);
  tone(BUZZER, 2000, 200);
  delay(200);
  noTone(BUZZER);
}

void playWrongTone() {
  tone(BUZZER, 500, 500);
  delay(500);
  noTone(BUZZER);
}

// ========== JUEGO DE NÚMEROS ENTEROS ==========
void generarPreguntasEnteros() {
  for (int i = 0; i < totalQuestionsEnteros; i++) {
    questionsEnteros[i].num1 = random(-10, 10);
    questionsEnteros[i].operation = random(0, 4);

    if (questionsEnteros[i].operation == 3) {
      do {
        questionsEnteros[i].num2 = random(-10, 10);
      } while (questionsEnteros[i].num2 == 0 || questionsEnteros[i].num1 % questionsEnteros[i].num2 != 0);
    } else {
      questionsEnteros[i].num2 = random(-10, 10);
    }

    switch (questionsEnteros[i].operation) {
      case 0: questionsEnteros[i].correctAnswer = questionsEnteros[i].num1 + questionsEnteros[i].num2; break;
      case 1: questionsEnteros[i].correctAnswer = questionsEnteros[i].num1 - questionsEnteros[i].num2; break;
      case 2: questionsEnteros[i].correctAnswer = questionsEnteros[i].num1 * questionsEnteros[i].num2; break;
      case 3: questionsEnteros[i].correctAnswer = questionsEnteros[i].num1 / questionsEnteros[i].num2; break;
    }

    bool answerSet[4] = {false, false, false, false};
    for (int j = 0; j < 4; j++) {
      if (j == 0) {
        questionsEnteros[i].options[j] = questionsEnteros[i].correctAnswer;
        answerSet[j] = true;
      } else {
        int wrongAnswer;
        if (j == 1) {
          switch (questionsEnteros[i].operation) {
            case 0: wrongAnswer = questionsEnteros[i].num1 - questionsEnteros[i].num2; break;
            case 1: wrongAnswer = questionsEnteros[i].num1 + questionsEnteros[i].num2; break;
            case 2: wrongAnswer = questionsEnteros[i].num1 / questionsEnteros[i].num2; break;
            case 3: wrongAnswer = questionsEnteros[i].num1 * questionsEnteros[i].num2; break;
          }
        } else {
          do {
            wrongAnswer = random(-20, 20);
          } while (wrongAnswer == questionsEnteros[i].correctAnswer);
        }

        for (int k = 0; k < 4; k++) {
          if (!answerSet[k]) {
            questionsEnteros[i].options[k] = wrongAnswer;
            answerSet[k] = true;
            break;
          }
        }
      }
    }

    for (int j = 3; j > 0; j--) {
      int randomIndex = random(0, j + 1);
      int temp = questionsEnteros[i].options[j];
      questionsEnteros[i].options[j] = questionsEnteros[i].options[randomIndex];
      questionsEnteros[i].options[randomIndex] = temp;
    }
  }
}

void iniciarJuegoEnteros() {
  scoreEnteros = 0;
  currentQuestionEnteros = 0;
  startTimeEnteros = millis();
  juegoEnProgresoEnteros = true;

  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW);
  centerText("Numeros Enteros", 20);
  delay(2000);

  generarPreguntasEnteros();
  displayQuestionEnteros();
}

void loopEnteros() {
  if (juegoEnProgresoEnteros) {
    if (currentQuestionEnteros < totalQuestionsEnteros) {
      if (debounce(BUTTON_A)) {
        checkAnswerEnteros(questionsEnteros[currentQuestionEnteros].options[0]);
      } else if (debounce(BUTTON_B)) {
        checkAnswerEnteros(questionsEnteros[currentQuestionEnteros].options[1]);
      } else if (debounce(BUTTON_C)) {
        checkAnswerEnteros(questionsEnteros[currentQuestionEnteros].options[2]);
      } else if (debounce(BUTTON_D)) {
        checkAnswerEnteros(questionsEnteros[currentQuestionEnteros].options[3]);
      }
    } else {
      juegoEnProgresoEnteros = false;
      totalTimeEnteros = millis() - startTimeEnteros;
      displayFinalScoreEnteros();
    }
  } else {
    if (debounce(BUTTON_A)) {
      delay(500);
      playFunTone();
      reiniciarMenu();
    }
  }
}

void displayQuestionEnteros() {
  tft.fillScreen(ST77XX_BLACK);
 
  // Mostrar "Pregunta X de 10" - Texto blanco tamaño 2 (reducido de 3)
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(0, 10);
  tft.print("Pregunta ");
  tft.print(currentQuestionEnteros + 1);
  tft.print(" de ");
  tft.println(totalQuestionsEnteros);

  // Operación matemática - Texto amarillo tamaño 3 (reducido de 4)
  tft.setTextSize(3);
  tft.setTextColor(ST77XX_YELLOW);  // Cambiado de ROJO a AMARILLO
  tft.setCursor(0, 40);
  tft.print(questionsEnteros[currentQuestionEnteros].num1);
  tft.print(" ");
  tft.print(getOperationSymbol(questionsEnteros[currentQuestionEnteros].operation));
  tft.print(" ");
  tft.print(questionsEnteros[currentQuestionEnteros].num2);
  tft.print(" = ?");

  // Opciones de respuesta en dos columnas
  tft.setTextSize(3);  // Tamaño de fuente para opciones
  tft.setTextColor(ST77XX_CYAN);
 
  // Columna izquierda (A y B)
  tft.setCursor(0, 90);    // Opción A
  tft.print("A) ");
  tft.print(questionsEnteros[currentQuestionEnteros].options[0]);
 
  tft.setCursor(0, 125);   // Opción B
  tft.print("B) ");
  tft.print(questionsEnteros[currentQuestionEnteros].options[1]);

  // Columna derecha (C y D)
  tft.setCursor(160, 90);  // Opción C (alineada frente a A)
  tft.print("C) ");
  tft.print(questionsEnteros[currentQuestionEnteros].options[2]);
 
  tft.setCursor(160, 125); // Opción D (alineada frente a B)
  tft.print("D) ");
  tft.print(questionsEnteros[currentQuestionEnteros].options[3]);
}

char getOperationSymbol(int operation) {
  switch (operation) {
    case 0: return '+';
    case 1: return '-';
    case 2: return '*';
    case 3: return '/';
    default: return '?';
  }
}

void checkAnswerEnteros(int selectedAnswer) {
  if (selectedAnswer == questionsEnteros[currentQuestionEnteros].correctAnswer) {
    tone(BUZZER, 1000, 200);
    delay(200);
    tone(BUZZER, 1500, 200);
    delay(200);
    tone(BUZZER, 2000, 200);
    delay(200);
    scoreEnteros += 0.5;
    tft.fillScreen(ST77XX_BLACK);
    tft.setTextColor(ST77XX_GREEN);
    tft.setTextSize(3);
    centerText("CORRECTO", 20);
    delay(1000);
  } else {
    playWrongTone();
    tft.fillScreen(ST77XX_BLACK);
    tft.setTextColor(ST77XX_RED);
    tft.setTextSize(3);
    centerText("Incorrecto", 20);
    delay(1000);
  }

  currentQuestionEnteros++;
  if (currentQuestionEnteros >= totalQuestionsEnteros) {
    juegoEnProgresoEnteros = false;
    totalTimeEnteros = millis() - startTimeEnteros;
    displayFinalScoreEnteros();
  } else {
    displayQuestionEnteros();
  }
}

void displayFinalScoreEnteros() {
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW);
  tft.setCursor(0, 0);
  tft.println("Puntuacion final:");
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(0, 30);
  tft.print(scoreEnteros);
  tft.print(" de ");
  tft.print(totalQuestionsEnteros * 0.5);

  tft.setCursor(0, 55);
  tft.print("Tiempo: ");
  tft.print(totalTimeEnteros / 1000);
  tft.print(" segundos");

  tft.setTextColor(ST77XX_CYAN);
  tft.setCursor(0, 85);
  tft.println("Oprima boton A");
  tft.println("para nueva partida");
}

// ========== JUEGO DE NÚMEROS DECIMALES ==========
void generarPreguntaDecimales() {
  num1Decimales = random(1, 100) / 10.0;
  num2Decimales = random(1, 100) / 10.0;
  resultadoDecimales = num1Decimales + num2Decimales;

  respuestaCorrectaDecimales = random(0, 4);

  for (int i = 0; i < 4; i++) {
    if (i == respuestaCorrectaDecimales) {
      dtostrf(resultadoDecimales, 4, 2, opcionesDecimales[i]);
    } else {
      float incorrecta = resultadoDecimales + (random(-20, 20) / 10.0);
      dtostrf(incorrecta, 4, 2, opcionesDecimales[i]);
    }
  }
}

void iniciarJuegoDecimales() {
  puntuacionDecimales = 0;
  preguntaActualDecimales = 0;
  tiempoInicioDecimales = millis();
  midiendoTiempoDecimales = true;

  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW);
  centerText("Numeros Decimales", 20);
  delay(2000);

  generarPreguntaDecimales();
  mostrarPreguntaConOpcionesDecimales();
}

void loopDecimales() {
  if (preguntaActualDecimales < 10) {
    if (!midiendoTiempoDecimales) {
      tiempoInicioDecimales = millis();
      midiendoTiempoDecimales = true;
    }

    int respuestaElegida = -1;
    if (debounce(BUTTON_A)) respuestaElegida = 0;
    else if (debounce(BUTTON_B)) respuestaElegida = 1;
    else if (debounce(BUTTON_C)) respuestaElegida = 2;
    else if (debounce(BUTTON_D)) respuestaElegida = 3;

    if (respuestaElegida != -1) {
      if (respuestaElegida == respuestaCorrectaDecimales) {
        indicarResultadoDecimales(true);
        puntuacionDecimales += 0.5;
      } else {
        indicarResultadoDecimales(false);
      }

      preguntaActualDecimales++;
      if (preguntaActualDecimales < 10) {
        generarPreguntaDecimales();
        mostrarPreguntaConOpcionesDecimales();
      } else {
        tiempoFinalDecimales = millis();
        midiendoTiempoDecimales = false;
        mostrarPuntuacionYTiempoDecimales();
      }
    }
  } else {
    if (debounce(BUTTON_A)) {
      delay(500);
      playFunTone();
      reiniciarMenu();
    }
  }
}

void mostrarPreguntaConOpcionesDecimales() {
  tft.fillScreen(ST77XX_BLACK);
 
  // Mostrar "Pregunta X de 10" - Texto blanco tamaño 2
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(0, 10);
  tft.print("Pregunta ");
  tft.print(preguntaActualDecimales + 1);
  tft.print(" de 10");

  // Operación matemática - Texto amarillo tamaño 3
  tft.setTextSize(3);
  tft.setTextColor(ST77XX_YELLOW);
  tft.setCursor(0, 40);
  tft.print(num1Decimales, 1);
  tft.print(" + ");
  tft.print(num2Decimales, 1);
  tft.print(" = ?");

  // Opciones de respuesta en dos columnas
  tft.setTextSize(2);  // Tamaño de fuente para opciones
  tft.setTextColor(ST77XX_CYAN);
 
  // Columna izquierda (A y B)
  tft.setCursor(0, 90);    // Opción A
  tft.print("A. ");
  tft.print(opcionesDecimales[0]);
 
  tft.setCursor(0, 125);   // Opción B
  tft.print("B. ");
  tft.print(opcionesDecimales[1]);

  // Columna derecha (C y D)
  tft.setCursor(160, 90);  // Opción C (alineada frente a A)
  tft.print("C. ");
  tft.print(opcionesDecimales[2]);
 
  tft.setCursor(160, 125); // Opción D (alineada frente a B)
  tft.print("D. ");
  tft.print(opcionesDecimales[3]);
}

void indicarResultadoDecimales(bool esCorrecta) {
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(3);
  tft.setCursor(0, 20);
  if (esCorrecta) {
    tft.setTextColor(ST77XX_GREEN);
    centerText("CORRECTO", 20);
    tone(BUZZER, 1000, 200);
  } else {
    tft.setTextColor(ST77XX_RED);
    centerText("INCORRECTO", 20);
    tone(BUZZER, 500, 200);
  }
  delay(1000);
}

void mostrarPuntuacionYTiempoDecimales() {
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW);
  tft.setCursor(0, 0);
  tft.print("Puntuacion final: ");
  tft.setCursor(0, 25);
  tft.setTextSize(3);
  tft.setTextColor(ST77XX_CYAN);
  tft.print(puntuacionDecimales);
  tft.print("/5.0");

  unsigned long tiempoTotal = tiempoFinalDecimales - tiempoInicioDecimales;
  unsigned long segundos = tiempoTotal / 1000;
  unsigned long minutos = segundos / 60;
  segundos = segundos % 60;
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(0, 65);
  tft.print("Tiempo total: ");
  tft.print(minutos);
  tft.print(":");
  if (segundos < 10) {
    tft.print("0");
  }
  tft.print(segundos);

  tft.setTextColor(ST77XX_CYAN);
  tft.setCursor(0, 95);
  tft.println("Oprima el boton A");
  tft.println("para nueva ronda.");
}
// ========== QUIZ DE QUÍMICA ==========
void seleccionarPreguntasAleatorias(const char* preguntas[], int total_preguntas) {
  randomSeed(analogRead(12));
  for(int i = 0; i < 10; i++) {
    bool repetida;
    int nueva_pregunta;
    do {
      repetida = false;
      nueva_pregunta = random(total_preguntas);
      for(int j = 0; j < i; j++) {
        if(preguntas_seleccionadas[j] == nueva_pregunta) {
          repetida = true;
          break;
        }
      }
    } while(repetida);
    preguntas_seleccionadas[i] = nueva_pregunta;
  }
}

void iniciarQuizQuimica() {
  pregunta_actual = 0;
  puntuacion = 0;
 
  seleccionarPreguntasAleatorias(preguntas_quimica, 30);
 
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW);
  centerText("QUIZ DE QUIMICA", 10);
  centerText("10 PREGUNTAS", 30);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_CYAN);
  centerText("Presiona A", 60);
  esperarCualquierBoton();
}

void loopQuizQuimica() {
  static bool mostradoResultado = false; // Variable para controlar si ya se mostró el resultado
 
  if(pregunta_actual < 10) {
    int pregunta_idx = preguntas_seleccionadas[pregunta_actual];
   
    mostrarNumeroPregunta();
    delay(800);
   
    mostrarPreguntaCompleta(preguntas_quimica[pregunta_idx], respuestas_quimica, pregunta_idx);
   
    int respuesta = esperarRespuesta();
   
    if(respuesta == 3) return;
   
    verificarRespuesta(respuestas_correctas_quimica[pregunta_idx], respuesta);
    pregunta_actual++;
    mostradoResultado = false; // Resetear cuando comienza nueva pregunta
  }
  else if(!mostradoResultado) {
    mostrarResultadoFinal();
    mostradoResultado = true;
  }
  else {
    if (debounce(BUTTON_A) || debounce(BUTTON_B) || debounce(BUTTON_C) || debounce(BUTTON_D)) {
      reiniciarMenu();
    }
  }
}


// ========== QUIZ DE BIOLOGÍA ==========
void iniciarQuizBiologia() {
  pregunta_actual = 0;
  puntuacion = 0;
 
  seleccionarPreguntasAleatorias(preguntas_biologia, 30);
 
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW);
  centerText("QUIZ DE BIOLOGIA", 10);
  centerText("10 PREGUNTAS", 30);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_CYAN);
  centerText("Presiona A", 60);
  esperarCualquierBoton();
}

void loopQuizBiologia() {
  static bool mostradoResultado = false; // Variable para controlar si ya se mostró el resultado
 
  if(pregunta_actual < 10) {
    int pregunta_idx = preguntas_seleccionadas[pregunta_actual];
   
    mostrarNumeroPregunta();
    delay(800);
   
    mostrarPreguntaCompleta(preguntas_biologia[pregunta_idx], respuestas_biologia, pregunta_idx);
   
    int respuesta = esperarRespuesta();
   
    if(respuesta == 3) return;
   
    verificarRespuesta(respuestas_correctas_biologia[pregunta_idx], respuesta);
    pregunta_actual++;
    mostradoResultado = false; // Resetear cuando comienza nueva pregunta
  }
  else if(!mostradoResultado) {
    mostrarResultadoFinal();
    mostradoResultado = true;
  }
  else {
    if (debounce(BUTTON_A) || debounce(BUTTON_B) || debounce(BUTTON_C) || debounce(BUTTON_D)) {
      reiniciarMenu();
    }
  }
}


// ========== FUNCIONES AUXILIARES PARA QUIZZES ==========
void mostrarNumeroPregunta() {
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW);
  centerText("Pregunta", 15);
  char texto[20];
  sprintf(texto, "%d de 10", pregunta_actual + 1);
  tft.setTextColor(ST77XX_WHITE);
  centerText(texto, 35);
}

void mostrarPreguntaCompleta(const char* pregunta, const char* opciones[][3], int idx) {
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_RED);
  tft.setCursor(0, 0);
  tft.println(pregunta);
  tft.println("");
 
  tft.setTextColor(ST77XX_BLUE);
  for(int i = 0; i < 3; i++) {
    tft.println(opciones[idx][i]);
    tft.println("");
  }
}

int esperarRespuesta() {
  while(true) {
    if (debounce(BUTTON_A)) return 0;
    if (debounce(BUTTON_B)) return 1;
    if (debounce(BUTTON_C)) return 2;
    if (debounce(BUTTON_D)) return 3;
    delay(10);
  }
}

void verificarRespuesta(int respuesta_correcta, int respuesta) {
  bool correcta = (respuesta == respuesta_correcta);
 
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(correcta ? ST77XX_GREEN : ST77XX_RED);
  centerText(correcta ? "CORRECTO!" : "INCORRECTO", 20);
 
  if(correcta) {
    puntuacion++;
    tone(BUZZER, 1000, 200);
  } else {
    tone(BUZZER, 300, 500);
  }
 
  delay(1000);
}

void mostrarResultadoFinal() {
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW);
  centerText("RESULTADO FINAL", 10);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(0, 40);
  tft.print("Puntuacion: ");
  tft.setTextColor(ST77XX_YELLOW);
  tft.print(puntuacion);
  tft.setTextColor(ST77XX_WHITE);
  tft.print("/10");
  tft.setTextColor(ST77XX_CYAN);
  tft.setCursor(0, 80);
  centerText("Presione cualquier", 80);
  centerText("boton para continuar", 100);
}

void esperarCualquierBoton() {
  while(!(debounce(BUTTON_A) || debounce(BUTTON_B) || debounce(BUTTON_C) || debounce(BUTTON_D))) {
    delay(10);
  }
  delay(debounceDelay);
}

Comentarios

Entradas populares de este blog

CLASIFICADOR DE COLORES CON SENSOR TCS3472 (I2C) CON ARDUINO NANO

PROTOTIPO - CONTROL DE ACCESO CON RFID + TECLADO CON ALMACENAMIENTO EN MICRO SD

CONTROL DE ACCESO CON RFID YMODULO RTC DS3231