#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include <Adafruit_NeoPixel.h>
// --- CONFIGURAÇÕES DE HARDWARE ---
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define MAX_DEVICES 4
#define CS_PIN 10
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
#define PIN_NEO 9
#define NUM_PIXELS 24
Adafruit_NeoPixel ring = Adafruit_NeoPixel(NUM_PIXELS, PIN_NEO, NEO_GRB + NEO_KHZ800);
// --- PINOS ---
const int pinoBotao = 2;
const int pinoSensor = A0;
const int pinoBuzzer = 5;
// --- VARIÁVEIS DE CONTROLE ---
int valorSensor = 0;
bool testeIniciado = false;
unsigned long tempoSirene = 0;
int freqSirene = 440;
int direcaoSirene = 10;
bool sireneAtiva = false;
// --- MOTOR DE CORES PREMIUM (CHAMA ATENÇÃO) ---
uint32_t corLux(float fase, float brilhoLocal) {
// Cores luxuosas multiplicadas pela oscilação de brilho (0.5 a 1.0)
int r = ((sin(fase) + 1) * 100) * brilhoLocal;
int g = ((sin(fase + 2.0) + 1) * 80) * brilhoLocal;
int b = ((sin(fase + 4.0) + 1) * 120) * brilhoLocal;
return ring.Color(r, g, b);
}
void efeitoEsperaPremium() {
static float offset = 0;
static float oscilacaoBrilho = 0;
// Respiração Global: Oscila suavemente entre 50% e 100% de intensidade
float brilhoBase = 0.75 + (sin(oscilacaoBrilho) * 0.25);
for(int i=0; i<NUM_PIXELS; i++) {
float f = offset + (i * 0.3);
// Cintilação individual por pixel (efeito diamante lapidado)
float pulsoIndividual = 0.9 + (cos(offset * 2.0 + i) * 0.1);
float brilhoFinal = brilhoBase * pulsoIndividual;
// Limites de segurança para manter a sofisticação
if(brilhoFinal < 0.5) brilhoFinal = 0.5;
if(brilhoFinal > 1.0) brilhoFinal = 1.0;
ring.setPixelColor(i, corLux(f, brilhoFinal));
}
ring.show();
offset += 0.04;
oscilacaoBrilho += 0.03;
}
void feedbackBotao() {
// Bip tecnológico de dois tons
tone(pinoBuzzer, 1200, 80);
delay(80);
tone(pinoBuzzer, 1800, 100);
// Flash Branco de impacto (Acordando o sistema)
for(int j=0; j<3; j++) {
for(int i=0; i<NUM_PIXELS; i++) ring.setPixelColor(i, 255, 255, 255);
ring.show(); delay(30);
ring.clear(); ring.show(); delay(30);
}
}
void animaProgressoSopro(int progresso) {
int ledsAcesos = map(progresso, 0, 100, 0, NUM_PIXELS);
ring.clear();
for(int i=0; i<NUM_PIXELS; i++) {
if(i < ledsAcesos) {
ring.setPixelColor(i, 0, 255, 50); // Verde Esmeralda vibrante
} else {
// Ajuste de "Higiene Visual" para ser perceptível no Wokwi e elegante no Físico
ring.setPixelColor(i, 0, 60, 15); // Verde Floresta Profundo
}
}
ring.show();
}
void animaPoliciaPremium() {
static bool lado = false;
static unsigned long ultimoM = 0;
if(millis() - ultimoM > 50) {
ring.clear();
for(int i=0; i<NUM_PIXELS/2; i++) {
int p = lado ? i : i + (NUM_PIXELS/2);
ring.setPixelColor(p, lado ? 255 : 0, 0, lado ? 0 : 255);
}
ring.show();
lado = !lado;
ultimoM = millis();
}
}
void gerenciarSirene() {
if (!sireneAtiva) { noTone(pinoBuzzer); return; }
if (millis() - tempoSirene >= 8) {
tempoSirene = millis();
freqSirene += direcaoSirene;
if (freqSirene >= 1300 || freqSirene <= 400) direcaoSirene *= -1;
tone(pinoBuzzer, freqSirene);
}
}
// --- CONFIGURAÇÃO INICIAL ---
void setup() {
P.begin();
P.setIntensity(8); // Brilho da matriz
ring.begin();
ring.setBrightness(180); // Brilho do NeoPixel
ring.show();
pinMode(pinoBotao, INPUT_PULLUP);
pinMode(pinoBuzzer, OUTPUT);
P.displayText("BLITZ DA GRATIDAO: UM PIX, UM SOPRO!", PA_CENTER, 35, 2000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}
// --- CICLO PRINCIPAL ---
void loop() {
// MODO ESPERA (STANDBY)
if (!testeIniciado) {
efeitoEsperaPremium();
if (P.displayAnimate()) P.displayReset();
if (digitalRead(pinoBotao) == LOW) {
delay(50); // Debounce manual
feedbackBotao();
testeIniciado = true;
P.displayClear();
}
}
// MODO TESTE (ATIVADO)
else {
// 1. Fase de Sopro (5 segundos de interação)
unsigned long inicioSopro = millis();
while(millis() - inicioSopro < 5000) {
int prog = map(millis() - inicioSopro, 0, 5000, 0, 100);
P.print("SOPRE!");
animaProgressoSopro(prog);
delay(10);
}
// 2. Fase de Processamento (Animação de Radar)
P.displayClear();
for(int r=0; r<25; r++) {
ring.clear();
ring.setPixelColor(r % NUM_PIXELS, 255, 255, 255);
ring.setPixelColor((r+1) % NUM_PIXELS, 120, 120, 120);
ring.show();
P.print("...");
delay(60);
}
// 3. Leitura e Decisão
valorSensor = analogRead(pinoSensor);
const char* msg;
if (valorSensor <= 300) msg = "SOBRIO? PAGUE UM DRINK!";
else if (valorSensor <= 550) msg = "MELHORE O PIX! BEBEU BEM!";
else if (valorSensor <= 850) msg = "NIVEL CRITICO! PIX DOBROU!";
else msg = "GAME OVER! PIX OU GUINCHO?";
// 4. Resultado (Sirene + Luzes Policiais)
sireneAtiva = true;
unsigned long tempoRes = millis();
while (millis() - tempoRes < 4500) {
gerenciarSirene();
animaPoliciaPremium();
char strV[15]; sprintf(strV, "V: %d", valorSensor); P.print(strV);
}
// 5. Scroll da Mensagem Final
P.displayClear();
P.displayText(msg, PA_CENTER, 40, 400, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
int repeticoes = 0;
while (repeticoes < 2) {
gerenciarSirene();
animaPoliciaPremium();
if (P.displayAnimate()) { repeticoes++; P.displayReset(); }
}
// 6. Finalização e Retorno Suave (Fade-out)
sireneAtiva = false;
noTone(pinoBuzzer);
for(int b=180; b>=0; b-=10) {
ring.setBrightness(b); ring.show(); delay(20);
}
ring.clear(); ring.show();
ring.setBrightness(180); // Restaura brilho para o loop de espera
P.displayText("BLITZ DA GRATIDAO: UM PIX, UM SOPRO!", PA_CENTER, 35, 2000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
testeIniciado = false;
}
}