#include <Adafruit_NeoPixel.h>
#include <HardwareSerial.h>
#include "BluetoothSerial.h" // Biblioteca para Bluetooth en ESP32
#define PIN_NEO_PIXEL 12
#define NUM_PIXELS 64
#define BUZZER_PIN 13
Adafruit_NeoPixel NeoPixel(NUM_PIXELS, PIN_NEO_PIXEL, NEO_GRB + NEO_KHZ800);
BluetoothSerial SerialBT; // Objeto para la comunicación Bluetooth
const uint8_t levels[9][8] = {
{0x00, 0x44, 0x4C, 0x44, 0x44, 0x6E, 0x00, 0x00}, // Nivel 1
{0x00, 0x4E, 0x42, 0x4E, 0x48, 0x6E, 0x00, 0x00}, // Nivel 2
{0x00, 0x4E, 0x42, 0x4E, 0x42, 0x6E, 0x00, 0x00}, // Nivel 3
{0x00, 0x4A, 0x4A, 0x4E, 0x42, 0x62, 0x00, 0x00}, // Nivel 4
{0x00, 0x4E, 0x48, 0x4E, 0x42, 0x6E, 0x00, 0x00}, // Nivel 5
{0x00, 0x4E, 0x48, 0x4E, 0x4A, 0x6E, 0x00, 0x00}, // Nivel 6
{0x00, 0x4E, 0x42, 0x44, 0x48, 0x68, 0x00, 0x00}, // Nivel 7
{0x00, 0x4E, 0x4A, 0x4E, 0x4A, 0x6E, 0x00, 0x00}, // Nivel 8
{0x00, 0x4E, 0x4A, 0x4E, 0x42, 0x6E, 0x00, 0x00} // Nivel 9
};
const uint8_t face[8] =
{0x00, 0xA5, 0x42, 0xA5, 0x00, 0x7E, 0x81, 0x00}; // Lose
// Variables del juego
int posPaddleX1 = 3; //Posición de la paleta
int posPaddleY1 = 7;
int posPaddleX2 = 3;
int posPaddleY2 = 0;
int posBallX = 5; //Posición de la bola
int posBallY = 5;
int dirBallX = 1; //Dirección de la bola
int dirBallY = -1;
char buffer[100];
char buffer2[100];
char bufferb[100];
int len;
int len2;
int lenb;
int val;
int val2;
int level;
int numPlayers = 1;
int playerLost;
int Button;
int state = 0;
bool soundPlayed = false;
bool gameOverSoundPlayed = false;
hw_timer_t * timer = NULL;
volatile boolean updateBallFlag = false; // Bandera para actualizar la posición de la pelota
void IRAM_ATTR onTimer() {
updateBallFlag = true; // Establece la bandera para actualizar la pelota
}
void setup() {
NeoPixel.begin();
NeoPixel.setBrightness(50); // Ajusta el brillo (valor entre 0 y 255)
Serial.begin(9600);
SerialBT.begin("ESP32_GameConsole", true); // Nombre del dispositivo Bluetooth
if(SerialBT.connect("HC-JUANPA")) {
Serial.println("Connected Successfully!");
}
timer = timerBegin(0, 80, true); // Divisor de reloj a 80 para un timer de 1MHz (80 MHz / 80)
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 1000000, true); // 1 segundo
timerAlarmEnable(timer);
Serial.println("Setup complete");
}
void loop() {
switch (state) {
case 0: // Estado de configuración inicial
Read_Button();
Read_Control1();
level = (val * 9) / 256;
level += 1;
displayLevelSelection(level);
if (Button == 1) {
numPlayers = NumPlayers();
state = 1; // Cambiar al estado de juego
delay(1000);
}
break;
case 1: // Estado de juego activo
if (updateBallFlag) {
updateBallFlag = false; // Restablece la bandera
playerLost = updateBall();
if (playerLost != 0) {
Serial.println("Perdió Jugador " + String(playerLost));
displayFace();
playGameOverSound();
state = 2; // Cambio al estado de juego terminado
delay(500);
return; // Salir inmediatamente del loop() para evitar mostrar el juego
}
}
displayGame(); // Solo se llama si el estado sigue siendo 1
Read_Control1();
if (numPlayers == 2) {
Read_Control2_BT(); // Cambia la lectura del mando 2 a Bluetooth
}
break;
case 2: // Estado de juego terminado
displayFace();
Read_Button();
if (Button == 0) {
state = 0;
Serial.println("RST");
NeoPixel.clear();
NeoPixel.show();
resetGameSettings();
}
break;
}
}
int updateBall() {
// Actualizar posición de la bola antes de revisar colisiones
posBallX += dirBallX;
posBallY += dirBallY;
// Revisar colisiones con los bordes del área de juego
checkCollisions();
// Condiciones específicas basadas en el número de jugadores
if (numPlayers == 1) {
// Colisión con el paddle del jugador 1
if (posBallY == 7 && posBallX >= posPaddleX1 && posBallX <= posPaddleX1 + 1) {
dirBallY *= -1; // Invierte la dirección vertical
playSound(1000, 50); // Reproduce un sonido agudo
// Invertir dirección horizontal aleatoriamente para variar la trayectoria
}if (posBallY == 0 && posBallX >= posPaddleX2 && posBallX <= posPaddleX2 + 1) {
dirBallY *= -1; // Invierte la dirección vertical
} else if (posBallY > 7) { // Si la pelota pasa el borde inferior
return 1; // Jugador 1 pierde
}
} else if (numPlayers == 2) {
// Colisión con el paddle del jugador 2
if (posBallY == 0 && posBallX >= posPaddleX2 && posBallX <= posPaddleX2 + 1) {
dirBallY *= -1; // Invierte la dirección vertical
playSound(1000, 50); // Reproduce un sonido agudo
} else if (posBallY == 7 && posBallX >= posPaddleX1 && posBallX <= posPaddleX1 + 1) {
dirBallY *= -1; // Invierte la dirección vertical
playSound(1000, 50); // Reproduce un sonido agudo
} else if (posBallY < 0 || posBallY > 7) {
if (posBallY < 0) return 2; // Jugador 2 pierde
if (posBallY > 7) return 1; // Jugador 1 pierde
}
}
// No hay pérdidas, devolver 0
return 0;
}
void checkCollisions() {
// Colisión con los lados del área de juego
if (posBallX < 0 || posBallX > 7) {
dirBallX *= -1; // Invertir dirección horizontal
posBallX += dirBallX; // Ajustar posición para evitar que la pelota "salte"
playSound(150, 30);
}
if (posBallY < 0 || posBallY > 7) {
dirBallY *= -1; // Invertir dirección Y en colisiones superior/inferior
playSound(150, 30);
}
}
void displayGame() {
NeoPixel.clear();
// Display paddle Player 1
NeoPixel.setPixelColor(posPaddleY1 * 8 + posPaddleX1, NeoPixel.Color(0, 255, 255));
NeoPixel.setPixelColor(posPaddleY1 * 8 + posPaddleX1 + 1, NeoPixel.Color(0, 255, 255));
// Display paddle Player 2
if (numPlayers == 2) {
NeoPixel.setPixelColor(posPaddleY2 * 8 + posPaddleX2, NeoPixel.Color(255, 255, 0));
NeoPixel.setPixelColor(posPaddleY2 * 8 + posPaddleX2 + 1, NeoPixel.Color(255, 255, 0));
}
// Display ball
NeoPixel.setPixelColor(posBallY * 8 + posBallX, NeoPixel.Color(0, 255, 0));
NeoPixel.show();
}
void playSound(int toneVal, int duration) {
noTone(BUZZER_PIN); // Si el buzzer no está ya sonando
tone(BUZZER_PIN, toneVal, duration);
}
void playGameOverSound() {
if (!gameOverSoundPlayed) {
tone(BUZZER_PIN, 200, 500); // Frecuencia baja, duración más larga
delay(500);
noTone(BUZZER_PIN); // Detiene el sonido
gameOverSoundPlayed = true; // Marca el sonido como reproducido
}
}
void resetGameSettings() {
posBallX = 5;
posBallY = 5;
dirBallX = 1;
dirBallY = -1;
Button = 0;
gameOverSoundPlayed = false; // Resetear la variable para permitir que el sonido se reproduzca de nuevo
}
void displayLevelSelection(int level) {
NeoPixel.clear();
for (int r = 0; r < 8; r++) {
for (int c = 0; c < 8; c++) {
if (bitRead(levels[level - 1][r], 7 - c)) {
NeoPixel.setPixelColor(r * 8 + c, NeoPixel.Color(191, 255, 0));
}
}
}
NeoPixel.show();
timerAlarmWrite(timer, 1000000 / level, true); // Ajusta el timer para el nivel
}
void displayFace() {
NeoPixel.clear();
for (int r = 0; r < 8; r++) {
for (int c = 0; c < 8; c++) {
if (bitRead(face[r], 7 - c)) {
NeoPixel.setPixelColor(r * 8 + c, NeoPixel.Color(255, 0, 0));
}
}
}
NeoPixel.show();
}
int NumPlayers() {
int contador = 0;
Serial.print("SENSE:PADDLE?\r");
len = Serial.readBytesUntil('\r', buffer, sizeof(buffer));
if (len > 0) {
contador++;
}
SerialBT.print("SENSE:PADDLE?\r");
len2 = SerialBT.readBytesUntil('\r', buffer2, sizeof(buffer2));
if (len2 > 0) {
contador++;
}
return contador;
}
void Read_Button() {
Serial.print("SENSE:START?\r");
lenb = Serial.readBytesUntil('\r', bufferb, sizeof(bufferb));
if (lenb > 0) {
bufferb[lenb] = 0;
Button = atoi(bufferb);
}
}
void Read_Control1() {
Serial.print("SENSE:PADDLE?\r");
len = Serial.readBytesUntil('\r', buffer, sizeof(buffer));
if (len > 0) {
buffer[len] = 0;
val = atoi(buffer);
posPaddleX1 = min(6, max(0, (val / 31))); // Ajusta para asegurarse de que el paddle no se salga de la pantalla
}
}
void Read_Control2_BT() {
SerialBT.print("SENSE:PADDLE?\r");
len2 = SerialBT.readBytesUntil('\r', buffer2, sizeof(buffer2));
if (len2 > 0) {
buffer2[len2] = 0;
val2 = atoi(buffer2);
posPaddleX2 = min(6, max(0, (val2 / 31))); // Ajusta para asegurarse de que el paddle no se salga de la pantalla
}
}