#define entrada(x) pinMode(x, INPUT_PULLUP) #define saida(x) pinMode(x, OUTPUT) #define aguarde(x) delay(x) #define DESLIGA 0 // Usado para controlar LEDs #define NADA_ESCOLHIDO 0 // Usado para verificar botões #define VERMELHO (1 << 0) #define VERDE (1 << 1) #define AZUL (1 << 2) #define AMARELO (1 << 3) #define LED_VERMELHO 10 #define LED_VERDE 3 #define LED_AZUL 13 #define LED_AMARELO 5 // Definições dos pinos de botão #define BOTAO_VERMELHO 9 #define BOTAO_VERDE 2 #define BOTAO_AZUL 12 #define BOTAO_AMARELO 6 // Definições dos pinos do Buzzer #define PIEZO1 4 #define PIEZO2 7 // Parâmetros do jogo #define RODADAS_ATE_SER_CAMPEAO 10 // Número de rodadas para vencer o Jogo. #define TEMPO_DE_RESPOSTA_JOGADOR 3000 // Tempo para pressionar um botão antes que o jogo acabe. 3 segundos // Variáveis de estado do jogo byte tabuleiro[32]; //Salva a combinação de botões à medida que avançamos byte rodadas = 0; //Conta o número de rodadas de sucesso que o jogador fez void setup() { // Configuração dos pinos de entradas e saídas entrada(BOTAO_VERMELHO); entrada(BOTAO_VERDE); entrada(BOTAO_AZUL); entrada(BOTAO_AMARELO); saida(LED_VERMELHO); saida(LED_VERDE); saida(LED_AZUL); saida(LED_AMARELO); saida(PIEZO1); saida(PIEZO2); toqueInicial(); // Ao ligar toque a música vencedor if (checaBotao() == AMARELO) tocarMusica(); } void loop() { modoAtracao(); // Pisca luzes enquanto aguarda o usuário apertar um botão // Indique o início do jogo controlaLEDs(VERMELHO | VERDE | AZUL | AMARELO); // Ativar todos os LEDs aguarde(1000); controlaLEDs(DESLIGA); // Desligue os LEDs aguarde(250); // Roda o jogo de memória e recebe resultado if (jogoDaMemoria() == true) toqueVencedor(); // Ganhou, toca som de vitória else toquePerdedor(); // Perdeu, toca som de derrota } //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //As seguintes funções estão relacionadas apenas ao jogo // Jogue o jogo regular da memória // Retorna false se o jogador perde ou true se o jogador ganhar boolean jogoDaMemoria(void) { randomSeed(millis()); // Semeia o gerador aleatório rodadas = 0; // Redefinir o jogo para o começo while (rodadas < RODADAS_ATE_SER_CAMPEAO) { adicionaJogada(); // Adicione um botão aos movimentos atuais e reproduza-os apresentaJogadas(); // Jogue de volta o tabuleiro do jogo atual // Em seguida, solicite ao jogador que repita a sequência. for (byte atual = 0 ; atual < rodadas ; atual++) { byte opcao = aguardarBotao(); // Veja o botão que o usuário pressiona if (opcao == 0) return false; // Se a espera expirar, o jogador perde if (opcao != tabuleiro[atual]) return false; // Se a escolha estiver incorreta, o jogador perde } aguarde(1000); // O jogador estava correto, espera antes de jogar } return true; // O jogador cumpriu todas as rodadas para ganhar! } // Reproduz o conteúdo atual dos movimentos do jogo void apresentaJogadas(void) { for (byte atual = 0 ; atual < rodadas ; atual++) { emitirTom(tabuleiro[atual], 150); // Aguarde algum tempo entre a reprodução do botão // Encurtar isso para tornar o jogo mais difícil aguarde(150); // 150 funciona bem. 75 fica rápido. } } // Adiciona um novo botão aleatório à sequência do jogo void adicionaJogada(void) { byte novoBotao = random(0, 4); //min (incluido), max (excluido) // Temos que converter esse número, 0 até 3, para cor if(novoBotao == 0) novoBotao = VERMELHO; else if(novoBotao == 1) novoBotao = VERDE; else if(novoBotao == 2) novoBotao = AZUL; else if(novoBotao == 3) novoBotao = AMARELO; tabuleiro[rodadas++] = novoBotao; // Adicionar este novo botão ao array do jogo } //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // As seguintes funções controlam o hardware // ilumina um determinado LEDs // Passar em um byte composto de VERMELHO, AMARELO, etc void controlaLEDs(byte leds) { if ((leds & VERMELHO) != 0) digitalWrite(LED_VERMELHO, HIGH); else digitalWrite(LED_VERMELHO, LOW); if ((leds & VERDE) != 0) digitalWrite(LED_VERDE, HIGH); else digitalWrite(LED_VERDE, LOW); if ((leds & AZUL) != 0) digitalWrite(LED_AZUL, HIGH); else digitalWrite(LED_AZUL, LOW); if ((leds & AMARELO) != 0) digitalWrite(LED_AMARELO, HIGH); else digitalWrite(LED_AMARELO, LOW); } // Aguarde até que um botão seja pressionado. // Retorna uma das cores do LED (LED_VERMELHO, etc.) se for bem-sucedida, 0 se expirar byte aguardarBotao(void) { long inicio = millis(); // Lembre-se da hora em que começamos o loop // Enquanto tempo decorrido desde a definição da variável início for menor que limite do jogo: while ( (millis() - inicio) < TEMPO_DE_RESPOSTA_JOGADOR) // Faz um loop até que passe o tempo limite { byte botao = checaBotao(); if (botao != NADA_ESCOLHIDO) { emitirTom(botao, 150); // Reproduzir o botão que o usuário acabou de pressionar while(checaBotao() != NADA_ESCOLHIDO) ; // Agora vamos esperar que o usuário libere o botão aguarde(10); // Isso ajuda com toques duplos acidentais return botao; } } return NADA_ESCOLHIDO; // Se chegarmos aqui, expiramos! } // Retorna um bit '1' na posição correspondente a VERMELHO, VERDE, etc. byte checaBotao(void) { if (digitalRead(BOTAO_VERMELHO) == 0) return(VERMELHO); else if (digitalRead(BOTAO_VERDE) == 0) return(VERDE); else if (digitalRead(BOTAO_AZUL) == 0) return(AZUL); else if (digitalRead(BOTAO_AMARELO) == 0) return(AMARELO); return(NADA_ESCOLHIDO); // Se nenhum botão for pressionado, não retorne nenhum } // Acenda um LED e toque o tom // Vermelho, superior esquerdo: 440Hz - 2.272ms - 1.136ms de pulso // Verde, superior direito: 880Hz - 1.136ms - pulso de 0.568ms // Azul, inferior esquerdo: pulso de 587.33Hz - 1.702ms - 0.851ms // Amarelo, inferior direito: 784Hz - 1,276ms - pulso de 0,638ms void emitirTom(byte qual, int duracao_milisegundos) { controlaLEDs(qual); // Ligue um dado LED // Reproduz o som associado ao LED fornecido switch(qual) { case VERMELHO: emitirSom(duracao_milisegundos, 1136); break; case VERDE: emitirSom(duracao_milisegundos, 568); break; case AZUL: emitirSom(duracao_milisegundos, 851); break; case AMARELO: emitirSom(duracao_milisegundos, 638); break; } controlaLEDs(DESLIGA); // Desligue todos os LEDs } // Alterna o buzzer a cada pausa_microsegundos, por uma duração de duracao_milisegundos. void emitirSom(int duracao_milisegundos, int pausa_microsegundos) { // Converter tempo total de reprodução de milissegundos para microssegundos long comprimento_microsegundos = duracao_milisegundos * (long)1000; // Faz um loop até que o tempo restante de reprodução seja menor que uma única pausa_microsegundos while (comprimento_microsegundos > (pausa_microsegundos * 2)) { comprimento_microsegundos -= pausa_microsegundos * 2; // Diminui o tempo de jogo restante // Alterna a campainha em várias velocidades digitalWrite(PIEZO1, LOW); digitalWrite(PIEZO2, HIGH); delayMicroseconds(pausa_microsegundos); digitalWrite(PIEZO1, HIGH); digitalWrite(PIEZO2, LOW); delayMicroseconds(pausa_microsegundos); } } // Reproduzir o som e as luzes do vencedor void toqueInicial(void) { controlaLEDs(VERDE | AZUL); somVencedor(); controlaLEDs(VERMELHO | AMARELO); somVencedor(); controlaLEDs(VERDE | AZUL); somVencedor(); controlaLEDs(VERMELHO | AMARELO); somVencedor(); } void toqueVencedor(void) { toqueInicial(); tocarMusica(); } // Som vencedor // Este é apenas um som (chato) único que criamos, não há mágica nisso void somVencedor(void) { // Alterna a campainha em várias velocidades for (byte x = 250 ; x > 70 ; x--) { for (byte y = 0 ; y < 3 ; y++) { digitalWrite(PIEZO2, HIGH); digitalWrite(PIEZO1, LOW); delayMicroseconds(x); digitalWrite(PIEZO2, LOW); digitalWrite(PIEZO1, HIGH); delayMicroseconds(x); } } } // Toque o som perdedor / luzes void toquePerdedor(void) { controlaLEDs(VERMELHO | VERDE); emitirSom(255, 1500); controlaLEDs(AZUL | AMARELO); emitirSom(255, 1500); controlaLEDs(VERMELHO | VERDE); emitirSom(255, 1500); controlaLEDs(AZUL | AMARELO); emitirSom(255, 1500); } // Mostra uma tela de "modo de atração" enquanto aguarda o usuário pressionar o botão. void modoAtracao(void) { while(true) { controlaLEDs(VERMELHO); aguarde(100); if (checaBotao() != NADA_ESCOLHIDO) return; controlaLEDs(AZUL); aguarde(100); if (checaBotao() != NADA_ESCOLHIDO) return; controlaLEDs(VERDE); aguarde(100); if (checaBotao() != NADA_ESCOLHIDO) return; controlaLEDs(AMARELO); aguarde(100); if (checaBotao() != NADA_ESCOLHIDO) return; } } //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // As seguintes funções estão relacionadas apenas ao Easter Egg Musical int numeroDoLed = 0; // Mantém o controle de qual LED estamos ligando durante o ciclo musical // Não faça nada além de tocar a música // Esta função é ativada quando o usuário segura o botão inferior direito (amarelo) void tocarMusica(void) { // Notas (frequências) da música. int notas[] = { 2637, 2637, 0, 2637, 0, 2093, 2637, 0, 3136, 0, 0, 0, 1568, 0, 0, 0, 2093, 0, 0, 1568, 0, 0, 1319, 0, 0, 1760, 0, 1976, 0, 1865, 1760, 0, 1568, 2637, 3136, 3520, 0, 2794, 3136, 0, 2637, 0, 2093, 2349, 1976, 0, 0, 2093, 0, 0, 1568, 0, 0, 1319, 0, 0, 1760, 0, 1976, 0, 1865, 1760, 0, 1568, 2637, 3136, 3520, 0, 2794, 3136, 0, 2637, 0, 2093, 2349, 1976, 0, 0, 262, 523, 220, 440, 233, 466, 0, 0, 262, 523, 220, 440, 233, 466, 0, 0, 175, 349, 147, 294, 156, 311, 0, 0, 175, 349, 147, 294, 156, 311, 0, 0, 311, 277, 294, 277, 311, 311, 208, 196, 277, 262, 370, 349, 165, 466, 440, 415, 311, 247, 233, 220, 208, 0, 0, 0}; // Duração das notas da música int tempos[] = { 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 9, 9, 9, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 9, 9, 9, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 6, 3, 12, 12, 12, 12, 12, 12, 6, 3, 12, 12, 12, 12, 12, 12, 6, 3, 12, 12, 12, 12, 12, 12, 6, 6, 18, 18, 18, 6, 6, 6, 6, 6, 6, 18, 18, 18, 18, 18, 18, 10, 10, 10, 10, 10, 10, 3, 3, 3}; // Ligue o LED inferior direito (amarelo) controlaLEDs(AMARELO); emitirTom(AMARELO, 150); controlaLEDs(VERMELHO | VERDE | AZUL); // Ligue os outros LEDs até você soltar o botão while(checaBotao() != NADA_ESCOLHIDO) ; // Aguarde até que o usuário pare de pressionar o botão controlaLEDs(NADA_ESCOLHIDO); // Desligue os LEDs aguarde(1000); // Espere um segundo antes de tocar música digitalWrite(PIEZO2, LOW); // configure o lado "PIEZO1" da campainha para ficar baixo, enquanto tocamos o tom no outro pino. while(checaBotao() == NADA_ESCOLHIDO) // Reproduzir música até você pressionar um botão { int limite = sizeof notas / sizeof *notas; // Descobre o tamanho do array // iterar sobre as notas da melodia: for (int i = 0; i < limite; i++) { mudaLED(); int duracao = 1000 / tempos[i]; // Velocidade da música tone(PIEZO1, notas[i], duracao); aguarde(duracao * 1.3); // Pausa entre notas noTone(PIEZO1); if(checaBotao() != NADA_ESCOLHIDO) break; } } } // Cada vez que esta função é chamada, a placa se move para o próximo LED void mudaLED(void) { controlaLEDs(1 << numeroDoLed); // Mude o LED numeroDoLed++; // Ir para o próximo LED if(numeroDoLed > 3) numeroDoLed = 0; // Enrole o balcão, se necessário }