#include #include #include #include // Definição da Pinagem const int ledStart1 = 2; const int ledStart2 = 3; const int ledStart3 = 4; const int ledSlower = 5; const int ledFaster = 6; const int ledRecord = 7; const int butCntrl = 8; const int swtSensor = 9; const int pinBuzzer = 10; const int ledBotao = 13; // Globais gerais int flagInicializa = 1; int flagAcao = 0; int delayBlink250 = 250; int swtStatus = 0; int flagShowRaceTime = 0; String decAnterior = ""; unsigned long timeNow = 0; // Globais Buzzer unsigned long delayBuzzer = 150; unsigned int freqBuzzerRed = 500; unsigned int freqBuzzerGreen = 700; // Globais do botão unsigned long butOn = 0; unsigned long butOff = 0; int butStateNow = 0; int butStateBef = 0; unsigned long butTimeDif = 0; // Globais da Corrida int flagExibeTela = 1; int delayLight = 1000; int numLap = 0; unsigned long timeBestLapDriver = 0; unsigned long timeLapStart = 0; unsigned long timeLapFinish = 0; unsigned long timeLastLap = 0; unsigned long timeRaceStart = 0; unsigned long timeRaceFinish = 0; unsigned long timeOfRace = 0; char symDiff = '+'; unsigned long timeRaceDiff = 0; unsigned long timeRaceLap[21]; // Globais Reset unsigned long resetOn = 0; unsigned long resetOff = 0; unsigned long resetTimeDif = 0; int delayToReset = 3000; int delayCancelReset = 5000; int delayEfetivaReset = 100; // Globais dos Recordes int flagNewRecord = 0; unsigned int minLapRec = 0; unsigned int segLapRec = 0; unsigned int decLapRec = 0; unsigned int ctsLapRec = 0; unsigned int mlsLapRec = 0; unsigned int minRaceRec = 0; unsigned int segRaceRec = 0; unsigned int decRaceRec = 0; unsigned int ctsRaceRec = 0; unsigned int mlsRaceRec = 0; unsigned long timeLapRec = 0; unsigned long timeRaceRec = 0; unsigned long lapRaceRec[21]; LiquidCrystal_I2C lcdRace(0x3B,2,1,0,4,5,6,7,3, POSITIVE); LiquidCrystal_I2C lcdRecord(0x3E,2,1,0,4,5,6,7,3, POSITIVE); void setup() { Serial.begin(9600); lcdRace.begin (16,2); lcdRecord.begin (16,2); pinMode(ledStart1,OUTPUT); pinMode(ledStart2,OUTPUT); pinMode(ledStart3,OUTPUT); pinMode(ledSlower,OUTPUT); pinMode(ledFaster,OUTPUT); pinMode(ledRecord,OUTPUT); pinMode(ledBotao,OUTPUT); pinMode(pinBuzzer,OUTPUT); pinMode(butCntrl,INPUT); pinMode(swtSensor, INPUT); } void loop() { if (flagInicializa == 1) inicializa(); timeNow = millis(); butStateNow = digitalRead(butCntrl); if (butStateNow == HIGH) { digitalWrite(ledBotao,HIGH); if (butStateBef == LOW) // Armazena-se o momento de aperto do botão. butOn = millis(); } else { digitalWrite(ledBotao,LOW); if (butStateBef == HIGH) // Se o botão acaba de ser solto, trata o tempo desde o aperto do botão e toma-se a decisão: Corrida ou Reseta Recorde { butOff = millis(); butTimeDif = butOff - butOn; switch (flagAcao) // Acao 0 - Sistema acabou de ligar, corrida rolando ou fim de corrida. Aguardando Start de Corrida ou Solicitacao de Reset { case 0: case 2: case 3: if (flagAcao == 2 || flagAcao == 3) // Ação=2 -> Corrida em andamento; Ação=3 -> Corrida finalizada. Desliga os LED's de corrida, devido a solicitação via botão { digitalWrite(ledSlower,LOW); digitalWrite(ledFaster,LOW); digitalWrite(ledRecord,LOW); } if(butTimeDif > 20 && butTimeDif < delayToReset) // Solicitação do início da corrida startRace(); if(butTimeDif >= delayToReset) // Solicitação de resetar recordes solicitaReset(); break; case 1: // Acao = 1, significa solicitação de Reset de Recorde em andamento. if( butTimeDif > 20) // Confirmação do reset de recordes. Usa-se butTimeDif > 20 para ignorar o deboucing efetivaReset(); break; } } } if(flagAcao == 1) // Se for solicitado o Reset dos Recordes e o botão não for pressionado novamente em 5s após a solicitação, a mesma é cancelada. { resetOff = millis(); resetTimeDif = resetOff - resetOn; if( (resetTimeDif % 500) == 0) // Pisca Leds de recorde para chamar atenção ao solicitar o reset digitalWrite(ledRecord,HIGH); if( (resetTimeDif % 1000) == 0) digitalWrite(ledRecord,LOW); if (resetTimeDif >= delayCancelReset) { alteraLCDRecord( 2 , 0 , "RECORD RESET"); alteraLCDRecord( 4 , 1 , "CANCELED"); flagAcao = 0; digitalWrite(ledRecord,HIGH); delay(3000); digitalWrite(ledRecord,LOW); leRecord(); exibeLapTimeRec(); exibeRaceTimeRec(20); } } if(flagAcao == 2) // Corrida em andamento, tratamento volta a volta... { swtStatus = digitalRead(swtSensor); if (swtStatus == HIGH) { numLap = numLap + 1; if ( numLap == 0) flagExibeTela = 1; if ( numLap > 0) { tone(pinBuzzer , 900 , 150 ); trataVolta(); validaLapRecorde(); calculaDifRace(); flagExibeTela = 1; } if ( numLap == 20) { validaRaceRecorde(); flagAcao = 3; int i; for( i == 1; i <= 5 ; i++) { delay(500); tone(pinBuzzer , 3000 , 150 ); } } if(numLap < 20) exibeRaceTimeRec(numLap+1); else exibeRaceTimeRec(20); delay(500); } } if ( (flagAcao == 2 || flagAcao == 3) && (numLap >= 0)) // Tratamento do Display de Corrida intercalando Tela 1 (B Lap + R Time) e Tela 2 (Lap + Diff) { unsigned long timeCurrentDiff; timeCurrentDiff = (timeNow - timeRaceStart) / 1000; // Diferença entre o momento de largada e o agora, divide por 1000 para eliminar os milesimos if( (timeCurrentDiff % 7) == 0) // alterna a tela 1 e 2 a cada 7s flagExibeTela = 1; if( (timeCurrentDiff % 14) == 0) flagExibeTela = 2; switch(flagExibeTela) { case 1: exibeNumLap(); exibeLapTime(); flagExibeTela = 0; flagShowRaceTime = 1; if ( flagAcao == 3) exibeRaceTime(); break; case 2: exibeBestLapDriver(); exibeRaceDiff(); flagExibeTela = 0; flagShowRaceTime = 0; } // if(flagAcao == 2 && flagShowRaceTime == 1 ) // exibeCurrentTime(); if ( flagNewRecord == 1) // Piscar Led de recorde caso a volta rápida seja quebrada { timeCurrentDiff = (timeNow - timeRaceStart) / 1000; // Diferença entre o momento de largada e o agora, divide por 1000 para eliminar os milesimos if( (timeCurrentDiff % 1) == 0) digitalWrite(ledRecord,HIGH); if( (timeCurrentDiff % 2) == 0) digitalWrite(ledRecord,LOW); } } if (butStateNow != butStateBef) // Troca de estado do botão caso o estado atual seja diferente do estado anterior. butStateBef = butStateNow; } void startRace() { startLight(); timeRaceStart = millis(); timeLapStart = timeRaceStart; flagAcao = 2; numLap = -1; timeLastLap = 0; timeBestLapDriver = 0; timeRaceDiff = 0; flagNewRecord = 0; } void startLight() { alteraLCDRace(3,0,"LET'S RACE!"); alteraLCDRace(0,1," "); tone(pinBuzzer,freqBuzzerRed,delayBuzzer); digitalWrite(ledStart1,HIGH); delay(delayLight); tone(pinBuzzer,freqBuzzerRed,delayBuzzer); digitalWrite(ledStart2,HIGH); delay(delayLight); tone(pinBuzzer,freqBuzzerRed,delayBuzzer); digitalWrite(ledStart3,HIGH); delay(delayLight); tone(pinBuzzer,freqBuzzerGreen,delayBuzzer*4); digitalWrite(ledStart1,LOW); digitalWrite(ledStart2,LOW); digitalWrite(ledStart3,LOW); alteraLCDRace(3,1,"GO! GO! GO!"); } void trataVolta() // Calcula o tempo em milisegundos da ultima volta, tempo de corrida e re-marca o inicio da nova volta { timeLapFinish = millis(); timeLastLap = timeLapFinish - timeLapStart; timeOfRace = timeLapFinish - timeRaceStart; timeRaceLap[numLap] = timeOfRace; timeLapStart = timeLapFinish; if ( numLap == 1) timeBestLapDriver = timeLastLap; else if ( timeLastLap < timeBestLapDriver ) timeBestLapDriver = timeLastLap; } void validaLapRecorde() { unsigned long auxResto; if (timeLastLap < timeLapRec) { minLapRec = timeLastLap / 60000; // Converte o timeLastLap de milisegundos para MM:SS.dcm auxResto = timeLastLap % 60000; segLapRec = auxResto / 1000; auxResto = auxResto % 1000; decLapRec = auxResto / 100; auxResto = auxResto % 100; ctsLapRec = auxResto / 10; mlsLapRec = auxResto % 10; EEPROM.write( 0 , minLapRec ); // Grava no EEPROM o novo recorde da volta mais rápida EEPROM.write( 1 , segLapRec ); EEPROM.write( 2 , decLapRec ); EEPROM.write( 3 , ctsLapRec ); EEPROM.write( 4 , mlsLapRec ); timeLapRec = timeLastLap; flagNewRecord = 1; exibeLapTimeRec(); } } void calculaDifRace() { if ( timeOfRace == lapRaceRec[numLap]) { timeRaceDiff = 0; symDiff = '+'; digitalWrite(ledSlower,LOW); digitalWrite(ledFaster,LOW); } if ( timeOfRace < lapRaceRec[numLap]) { timeRaceDiff = lapRaceRec[numLap] - timeOfRace; symDiff = '-'; digitalWrite(ledSlower,LOW); digitalWrite(ledFaster,HIGH); } if ( timeOfRace > lapRaceRec[numLap]) { timeRaceDiff = timeOfRace - lapRaceRec[numLap]; symDiff = '+'; digitalWrite(ledSlower,HIGH); digitalWrite(ledFaster,LOW); } } void validaRaceRecorde() { unsigned long timeAux, auxMin, auxSeg, auxDec, auxCnt, auxMls, auxResto; int i; auxMin = timeOfRace / 60000; // Converte o timeLastLap de milisegundos para MM:SS.dcm auxResto = timeOfRace % 60000; auxSeg = auxResto / 1000; auxResto = auxResto % 1000; auxDec = auxResto / 100; auxResto = auxResto % 100; auxCnt = auxResto / 10; auxMls = auxResto % 10; if (timeOfRace < timeRaceRec) { for ( i = 1; i <= 20 ; i++ ) { timeAux = timeRaceLap[i]; // Converte de milisegundos para MM:SS.dcm cada uma das voltas realizadas. auxMin = timeAux / 60000; auxResto = timeAux % 60000; auxSeg = auxResto / 1000; auxResto = auxResto % 1000; auxDec = auxResto / 100; auxResto = auxResto % 100; auxCnt = auxResto / 10; auxMls = auxResto % 10; EEPROM.write( 5 * i + 0 , auxMin ); // Grava no EEPROM a volta convertida EEPROM.write( 5 * i + 1 , auxSeg ); EEPROM.write( 5 * i + 2 , auxDec ); EEPROM.write( 5 * i + 3 , auxCnt ); EEPROM.write( 5 * i + 4 , auxMls ); lapRaceRec[i] = timeRaceLap[i]; } timeRaceRec = timeOfRace; flagNewRecord = 1; exibeRaceTimeRec(20); } } void solicitaReset() { resetOn = millis(); flagAcao = 1; numLap = 0; alteraLCDRecord( 1 , 0 , "RESET RECORDS?"); alteraLCDRecord( 1 , 1 , "CONFIRM OR WAIT"); } void efetivaReset() { int i; EEPROM.write( 0 , 59 ); // Grava no EEPROM os valores maximo de melhor volta EEPROM.write( 1 , 59 ); EEPROM.write( 2 , 9 ); EEPROM.write( 3 , 9 ); EEPROM.write( 4 , 9 ); for( i=1 ; i <= 20 ; i++) // Reseta volta a volta o recorde de corrida { EEPROM.write( 5 * i + 0 , 59 ); EEPROM.write( 5 * i + 1 , 59 ); EEPROM.write( 5 * i + 2 , 9 ); EEPROM.write( 5 * i + 3 , 9 ); EEPROM.write( 5 * i + 4 , 9 ); } flagAcao = 0; digitalWrite(ledRecord,LOW); alteraLCDRecord( 5 , 0 , "RECORD"); alteraLCDRecord( 5 , 1 , "RESET!"); for(i = 1 ; i <= 10 ; i++) { digitalWrite(ledRecord,HIGH); // Pisca Leds para alerta de efetivação do reset. delay(delayEfetivaReset); digitalWrite(ledRecord,LOW); delay(delayEfetivaReset); } leRecord(); exibeLapTimeRec(); exibeRaceTimeRec(20); } void inicializa() { delay(2000); welcome(); leRecord(); exibeLapTimeRec(); exibeRaceTimeRec(20); testaLed(); flagInicializa = 0; } void leRecord() { int i = 0; minLapRec = leEEPROM( 0 , 'm' ); segLapRec = leEEPROM( 1 , 'm' ); decLapRec = leEEPROM( 2 , 's' ); ctsLapRec = leEEPROM( 3 , 's' ); mlsLapRec = leEEPROM( 4 , 's' ); timeLapRec = minLapRec * 60000 + segLapRec * 1000 + decLapRec * 100 + ctsLapRec * 10 + mlsLapRec; for( i=1 ; i <= 20 ; i++) // Le o tempo de cada volta que pertence ao recorde de corrida e armazena em um array { minRaceRec = leEEPROM( 5 * i + 0 , 'm' ); segRaceRec = leEEPROM( 5 * i + 1 , 'm' ); decRaceRec = leEEPROM( 5 * i + 2 , 's' ); ctsRaceRec = leEEPROM( 5 * i + 3 , 's' ); mlsRaceRec = leEEPROM( 5 * i + 4 , 's' ); lapRaceRec[i] = minRaceRec * 60000 + segRaceRec * 1000 + decRaceRec * 100 + ctsRaceRec * 10 + mlsRaceRec; } timeRaceRec = lapRaceRec[20]; } int leEEPROM(int endereco, char tipo) { int conteudo; conteudo = EEPROM.read(endereco); if(conteudo == 255) // Verificação se há algo gravado na memoria { if (tipo == 'm') // Verifica se o endereco de memoria foi utilizado para mm ou ss conteudo = 59; else conteudo = 9; } return conteudo; } void welcome() { alteraLCDRace( 3 , 0 , "WELCOME TO" ); alteraLCDRace( 0 , 1 , "BLACK ROCK RALLY" ); } void testaLed() { int i = 1; while(i <= 5) { digitalWrite(ledStart1,HIGH); digitalWrite(ledStart2,HIGH); digitalWrite(ledStart3,HIGH); digitalWrite(ledSlower,HIGH); digitalWrite(ledFaster,HIGH); digitalWrite(ledRecord,HIGH); digitalWrite(ledBotao,HIGH); delay(delayBlink250); digitalWrite(ledStart1,LOW); digitalWrite(ledStart2,LOW); digitalWrite(ledStart3,LOW); digitalWrite(ledSlower,LOW); digitalWrite(ledFaster,LOW); digitalWrite(ledRecord,LOW); digitalWrite(ledBotao,LOW); delay(delayBlink250); i = i + 1; } } void exibeNumLap() { String auxMsgLCD, auxNumLap; int showLap; if (numLap < 20) showLap = numLap + 1; else showLap = numLap; if ( showLap < 10) auxNumLap = "0" + String(showLap); else auxNumLap = showLap; auxMsgLCD = "LAP " + auxNumLap; alteraLCDRace( 5 , 0 , auxMsgLCD ); } void exibeLapTime() { unsigned long auxMin, auxSeg, auxDCMls, auxResto; String txtMin, txtSeg, txtDCMls, auxMsgLCD, auxNumLap; auxMin = timeLastLap / 60000; // Converte o timeLastLap de milisegundos para MM:SS.mmm e Exibe no LCD auxResto = timeLastLap % 60000; auxSeg = auxResto / 1000; auxDCMls = auxResto % 1000; if( auxMin < 10) txtMin = String("0") + auxMin ; else txtMin = auxMin; if( auxSeg < 10) txtSeg = String("0") + auxSeg ; else txtSeg = auxSeg; if( auxDCMls < 100) txtDCMls = String("0") + auxDCMls ; else txtDCMls = auxDCMls; if( auxDCMls < 10) txtDCMls = String("00") + auxDCMls ; auxMsgLCD = "L.LAP: " + txtMin + ":" + txtSeg + "." + txtDCMls; alteraLCDRace( 0 , 1 , auxMsgLCD ); } void exibeBestLapDriver() { unsigned long auxMin, auxSeg, auxDCMls, auxResto; String txtMin, txtSeg, txtDCMls, auxMsgLCD, auxNumLap; auxMin = timeBestLapDriver / 60000; // Converte o timeBestLapDriver de milisegundos para MM:SS.mmm e Exibe no LCD auxResto = timeBestLapDriver % 60000; auxSeg = auxResto / 1000; auxDCMls = auxResto % 1000; if( auxMin < 10) txtMin = String("0") + auxMin ; else txtMin = auxMin; if( auxSeg < 10) txtSeg = String("0") + auxSeg ; else txtSeg = auxSeg; if( auxDCMls < 100) txtDCMls = String("0") + auxDCMls ; else txtDCMls = auxDCMls; if( auxDCMls < 10) txtDCMls = String("00") + auxDCMls ; auxMsgLCD = "B.LAP: " + txtMin + ":" + txtSeg + "." + txtDCMls; alteraLCDRace( 0 , 0 , auxMsgLCD ); } void exibeCurrentTime() { unsigned long timeCurrentDiff, auxMin, auxSeg, auxDCMls, auxResto; String txtMin, txtSeg, txtDCMls, auxMsgLCD, auxNumLap, decAtual; int showLap; if (numLap < 0) showLap = 0; else showLap = numLap + 1; timeCurrentDiff = timeNow - timeRaceStart; auxMin = timeCurrentDiff / 60000; // Converte o timeCurrentDiff de milisegundos para MM:SS.mmm e Exibe no LCD auxResto = timeCurrentDiff % 60000; auxSeg = auxResto / 1000; auxDCMls = auxResto % 1000; if( auxMin < 10) txtMin = String("0") + auxMin ; else txtMin = auxMin; if( auxSeg < 10) txtSeg = String("0") + auxSeg ; else txtSeg = auxSeg; if( auxDCMls < 100) txtDCMls = String("0") + auxDCMls ; else txtDCMls = auxDCMls; if( auxDCMls < 10) txtDCMls = String("00") + auxDCMls ; if ( showLap < 10) auxNumLap = "0" + String(showLap); else auxNumLap = showLap; decAtual = txtDCMls.charAt(0); if (decAtual != decAnterior) { auxMsgLCD = "LAP" + auxNumLap + ": " + txtMin + ":" + txtSeg + "." + txtDCMls; alteraLCDRace( 0 , 1 , auxMsgLCD ); decAnterior = decAtual; } } void exibeRaceTime() { unsigned long auxMin, auxSeg, auxDCMls, auxResto; String txtMin, txtSeg, txtDCMls, auxMsgLCD, auxNumLap; int showLap; if (numLap < 0) showLap = 0; else showLap = numLap; auxMin = timeOfRace / 60000; // Converte o timeOfRace de milisegundos para MM:SS.mmm e Exibe no LCD auxResto = timeOfRace % 60000; auxSeg = auxResto / 1000; auxDCMls = auxResto % 1000; if( auxMin < 10) txtMin = String("0") + auxMin ; else txtMin = auxMin; if( auxSeg < 10) txtSeg = String("0") + auxSeg ; else txtSeg = auxSeg; if( auxDCMls < 100) txtDCMls = String("0") + auxDCMls ; else txtDCMls = auxDCMls; if( auxDCMls < 10) txtDCMls = String("00") + auxDCMls ; if ( showLap < 10) auxNumLap = "0" + String(showLap); else auxNumLap = showLap; auxMsgLCD = "LAP" + auxNumLap + ": " + txtMin + ":" + txtSeg + "." + txtDCMls; alteraLCDRace( 0 , 1 , auxMsgLCD ); } void exibeRaceDiff() { unsigned long auxMin, auxSeg, auxDCMls, auxResto; String txtMin, txtSeg, txtDCMls, auxMsgLCD, auxNumLap; auxMin = timeRaceDiff / 60000; // Converte o timeRaceDiff de milisegundos para MM:SS.mmm e Exibe no LCD auxResto = timeRaceDiff % 60000; auxSeg = auxResto / 1000; auxDCMls = auxResto % 1000; if( auxMin < 10) txtMin = String("0") + auxMin ; else txtMin = auxMin; if( auxSeg < 10) txtSeg = String("0") + auxSeg ; else txtSeg = auxSeg; if( auxDCMls < 100) txtDCMls = String("0") + auxDCMls ; else txtDCMls = auxDCMls; if( auxDCMls < 10) txtDCMls = String("00") + auxDCMls ; auxMsgLCD = "R.DIF:" + String(symDiff) + txtMin + ":" + txtSeg + "." + txtDCMls; alteraLCDRace( 0 , 1 , auxMsgLCD); } void exibeLapTimeRec() { String txtMin, txtSeg, txtDCMls, auxMsgLCD; if( minLapRec < 10) txtMin = String("0") + minLapRec ; else txtMin = minLapRec; if( segLapRec < 10) txtSeg = String("0") + segLapRec ; else txtSeg = segLapRec; txtDCMls = String(decLapRec) + String(ctsLapRec) + String(mlsLapRec); auxMsgLCD = "LAP: " + txtMin + ":" + txtSeg + ":" + txtDCMls; alteraLCDRecord( 1 , 0 , auxMsgLCD ); } void exibeRaceTimeRec(int entLap) { unsigned long auxMin, auxSeg, auxDCMls, auxResto; String txtMin, txtSeg, txtDCMls, auxMsgLCD, auxNumLap; int showLap; if (entLap == 0) showLap = 20; else showLap = entLap; auxMin = lapRaceRec[showLap] / 60000; // Converte o lapRaceRec[showLap] de milisegundos para MM:SS.mmm e Exibe no LCD auxResto = lapRaceRec[showLap] % 60000; auxSeg = auxResto / 1000; auxDCMls = auxResto % 1000; if( auxMin < 10) txtMin = String("0") + auxMin ; else txtMin = auxMin; if( auxSeg < 10) txtSeg = String("0") + auxSeg ; else txtSeg = auxSeg; if( auxDCMls < 100) txtDCMls = String("0") + auxDCMls ; else txtDCMls = auxDCMls; if( auxDCMls < 10) txtDCMls = String("00") + auxDCMls ; if ( showLap < 10) auxNumLap = "0" + String(showLap); else auxNumLap = showLap; auxMsgLCD = "L" + auxNumLap + ": " + txtMin + ":" + txtSeg + "." + txtDCMls; alteraLCDRecord( 1 , 1 , auxMsgLCD ); } void alteraLCDRace(int col, int lin, String texto) { lcdRace.setBacklight(HIGH); lcdRace.setCursor(0,lin); lcdRace.print(" "); lcdRace.setCursor(col,lin); lcdRace.print(texto); } void alteraLCDRecord(int col, int lin, String texto) { lcdRecord.setBacklight(HIGH); lcdRecord.setCursor(0,lin); lcdRecord.print(" "); lcdRecord.setCursor(col,lin); lcdRecord.print(texto); }