#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int inverm = 12;
const int inverd = 14;
const int inazul = 27;
const int inamar = 26;
const int outverm = 2;
const int outverd = 4;
const int outazul = 16;
const int outamar = 17;
const int buzzer = 19;
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASS = "";
const char* MQTT_BROKER = "broker.hivemq.com";
const int MQTT_PORT = 1883;
const char* MQTT_TOPIC = "disruptive/genius/rank";
WiFiClient espClient;
PubSubClient mqttClient(espClient);
typedef enum ESTADOS {
CHOOSE_TAG = 0,
STARTUP,
GAME,
GAMEOVER
} estados;
estados estadoAtual = CHOOSE_TAG;
char tag[4] = "AAA";
int tagIndex = 0;
bool tagMudou = true;
int ultimoEstadoVerm = 0;
int ultimoEstadoVerd = 0;
int ultimoEstadoAzul = 0;
int ultimoEstadoAmar = 0;
int GameState = 0;
int cores[100];
int nivel = 0;
int atual = 0;
int keyDown = 0;
int tempoLed = 500;
int tempoIntervalo = 250;
// 5s fixos por jogada — reinicia a cada acerto
#define TIMEOUT_POR_JOGADA 5000
unsigned long inicioEspera = 0;
bool esperandoInput = false;
void conectarWiFi() {
Serial.print("Conectando ao WiFi");
WiFi.begin(WIFI_SSID, WIFI_PASS);
lcd.clear();
lcd.setCursor(0, 0); lcd.print("Conectando WiFi");
int tentativas = 0;
while (WiFi.status() != WL_CONNECTED && tentativas < 20) {
delay(500); Serial.print(".");
lcd.setCursor(tentativas % 16, 1); lcd.print(".");
tentativas++;
}
lcd.clear();
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi conectado!");
lcd.setCursor(0, 0); lcd.print("WiFi OK!");
} else {
Serial.println("\nSem WiFi - modo offline");
lcd.setCursor(0, 0); lcd.print("Sem WiFi :(");
lcd.setCursor(0, 1); lcd.print("Modo offline");
}
delay(1500); lcd.clear();
}
void reconectarMQTT() {
String id = "GeniusESP32-" + String(random(0xffff), HEX);
mqttClient.connect(id.c_str());
}
void publicarResultado() {
if (!mqttClient.connected()) reconectarMQTT();
StaticJsonDocument<64> doc;
doc["tag"] = tag;
doc["score"] = nivel;
char payload[64];
serializeJson(doc, payload);
bool ok = mqttClient.publish(MQTT_TOPIC, payload);
Serial.print("MQTT -> "); Serial.print(payload);
Serial.println(ok ? " [OK]" : " [FALHOU]");
}
void mostraCor(int cor) {
if(cor == 1) { digitalWrite(outverm, HIGH); tone(buzzer, 440); }
else if(cor == 2) { digitalWrite(outverd, HIGH); tone(buzzer, 420); }
else if(cor == 3) { digitalWrite(outazul, HIGH); tone(buzzer, 400); }
else if(cor == 4) { digitalWrite(outamar, HIGH); tone(buzzer, 460); }
delay(tempoLed);
digitalWrite(outverm, LOW); digitalWrite(outverd, LOW);
digitalWrite(outazul, LOW); digitalWrite(outamar, LOW);
noTone(buzzer);
delay(tempoIntervalo);
}
int leInput() {
if(digitalRead(inverm)) return 1;
if(digitalRead(inverd)) return 2;
if(digitalRead(inazul)) return 3;
if(digitalRead(inamar)) return 4;
return 0;
}
void runChooseTag() {
if (tagMudou) {
Serial.print("TAG: [ "); Serial.print(tag);
Serial.print(" ] - Letra "); Serial.println(tagIndex + 1);
lcd.setCursor(0, 0); lcd.print("Bem-vindo! ");
lcd.setCursor(0, 1); lcd.print("TAG: "); lcd.print(tag);
lcd.setCursor(5 + tagIndex, 1); lcd.blink();
tagMudou = false;
}
int estadoVerm = digitalRead(inverm);
int estadoVerd = digitalRead(inverd);
int estadoAzul = digitalRead(inazul);
int estadoAmar = digitalRead(inamar);
if(estadoVerm == HIGH && ultimoEstadoVerm == LOW) {
tag[tagIndex] = (tag[tagIndex] < 'Z') ? tag[tagIndex]+1 : 'A';
tagMudou = true; delay(50);
}
if(estadoVerd == HIGH && ultimoEstadoVerd == LOW) {
tag[tagIndex] = (tag[tagIndex] > 'A') ? tag[tagIndex]-1 : 'Z';
tagMudou = true; delay(50);
}
if(estadoAzul == HIGH && ultimoEstadoAzul == LOW) {
tagIndex++; if(tagIndex > 2) tagIndex = 0;
tagMudou = true; delay(50);
}
if(estadoAmar == HIGH && ultimoEstadoAmar == LOW) {
Serial.print("TAG Confirmada: "); Serial.println(tag);
lcd.noBlink(); lcd.clear(); delay(1000);
estadoAtual = STARTUP;
}
ultimoEstadoVerm = estadoVerm; ultimoEstadoVerd = estadoVerd;
ultimoEstadoAzul = estadoAzul; ultimoEstadoAmar = estadoAmar;
}
void runStartup() {
lcd.setCursor(0,0); lcd.print("Iniciando...");
tone(buzzer, 440, 200); delay(200); digitalWrite(outverm, HIGH);
tone(buzzer, 455, 200); delay(200); digitalWrite(outverd, HIGH);
tone(buzzer, 470, 200); delay(200); digitalWrite(outazul, HIGH);
tone(buzzer, 485, 200); delay(200); digitalWrite(outamar, HIGH);
tone(buzzer, 500, 200); delay(200);
digitalWrite(outverm, LOW); digitalWrite(outverd, LOW);
digitalWrite(outazul, LOW); digitalWrite(outamar, LOW);
noTone(buzzer); lcd.clear();
estadoAtual = GAME;
}
void runGame() {
if(GameState == 0) {
lcd.setCursor(0, 0); lcd.print("Jogador: "); lcd.print(tag);
lcd.setCursor(0, 1); lcd.print("Nivel: "); lcd.print(nivel); lcd.print(" ");
Serial.print("--- Nivel: "); Serial.println(nivel);
cores[nivel] = random(1, 5);
for(int i = 0; i <= nivel; i++) {
Serial.print(cores[i]); Serial.print(" ");
mostraCor(cores[i]);
}
Serial.println("\n[ Aguardando jogada... timeout: 5s ]");
inicioEspera = millis();
esperandoInput = true;
GameState = 1;
} else {
unsigned long tempoPassado = millis() - inicioEspera;
// Contagem regressiva no LCD
int segundos = (TIMEOUT_POR_JOGADA - tempoPassado) / 1000 + 1;
lcd.setCursor(0, 1);
lcd.print("Nivel:"); lcd.print(nivel);
lcd.print(" T:"); lcd.print(segundos); lcd.print("s ");
// Verifica timeout
if (tempoPassado >= TIMEOUT_POR_JOGADA) {
Serial.println("-> TIMEOUT! Game Over.");
esperandoInput = false;
estadoAtual = GAMEOVER;
return;
}
// Lê botão
int cor = leInput();
if(cor != 0 && keyDown == 0) {
Serial.print("Clique: "); Serial.print(cor);
Serial.print(" | Esperado: "); Serial.println(cores[atual]);
keyDown = 1;
if(cor == cores[atual]) {
Serial.println("-> ACERTOU! Timer reiniciado.");
atual++;
inicioEspera = millis(); // reinicia 5s para a próxima jogada
mostraCor(cor);
} else {
Serial.println("-> ERROU! Game Over.");
esperandoInput = false;
estadoAtual = GAMEOVER;
}
} else if(cor == 0) {
keyDown = 0;
}
if(atual > nivel) {
Serial.println("-> NIVEL CONCLUIDO!");
esperandoInput = false;
GameState = 0; nivel++; atual = 0;
tempoLed *= 0.9;
tempoIntervalo *= 0.9;
if(tempoLed < 100) tempoLed = 100;
if(tempoIntervalo < 50) tempoIntervalo = 50;
delay(500);
}
}
}
void runGameOver() {
Serial.print("GAME OVER! Score: "); Serial.println(nivel);
lcd.clear();
lcd.setCursor(0, 0); lcd.print("GAME OVER!");
lcd.setCursor(0, 1); lcd.print("Score: "); lcd.print(nivel);
tone(buzzer, 220);
for(int i = 0; i < 4; i++) {
digitalWrite(outverm, HIGH); digitalWrite(outverd, HIGH);
digitalWrite(outazul, HIGH); digitalWrite(outamar, HIGH);
delay(200);
digitalWrite(outverm, LOW); digitalWrite(outverd, LOW);
digitalWrite(outazul, LOW); digitalWrite(outamar, LOW);
delay(200);
}
noTone(buzzer);
publicarResultado();
nivel = 0; atual = 0; GameState = 0;
tempoLed = 500; tempoIntervalo = 250;
tagMudou = true;
delay(3000);
lcd.clear();
estadoAtual = CHOOSE_TAG;
}
void setup() {
Serial.begin(115200);
lcd.init(); lcd.backlight();
pinMode(inverm, INPUT_PULLDOWN); pinMode(inverd, INPUT_PULLDOWN);
pinMode(inazul, INPUT_PULLDOWN); pinMode(inamar, INPUT_PULLDOWN);
pinMode(outverm, OUTPUT); pinMode(outverd, OUTPUT);
pinMode(outazul, OUTPUT); pinMode(outamar, OUTPUT);
pinMode(buzzer, OUTPUT);
randomSeed(analogRead(0));
conectarWiFi();
mqttClient.setServer(MQTT_BROKER, MQTT_PORT);
Serial.println("Genius IoT pronto!");
}
void loop() {
static unsigned long ultimoCheckMQTT = 0;
if (millis() - ultimoCheckMQTT > 5000) {
ultimoCheckMQTT = millis();
if (!mqttClient.connected()) reconectarMQTT();
mqttClient.loop();
}
switch(estadoAtual) {
case CHOOSE_TAG: runChooseTag(); break;
case STARTUP: runStartup(); break;
case GAME: runGame(); break;
case GAMEOVER: runGameOver(); break;
}
}