0

I've made a game with 3 countdowns and 1 global countdown.

  • When we start, the global one, go from 45:00 to 00:00.

  • The 1 go from 15:00 to 00:00 and if it reach 00:00:

  • The 2 start and go from 15:00 to 00:00 and it if reach 00:00:

  • The 3 start and go from 15:00 to 00:00

If you win little game, you can stop a countdown and start the next. If you win the 3 games, you stop the 3 countdown and the global too.

But sometime, my 2nd countdown stop itself, sometime at 13:58 or 10:54…

Is it possible that the millis() cause that bug ? Do you have an idea to improve my code and fix that ?

Fun fact : Sometime countdowns go from 01 to 60 instead of 00 to 59……

Sorry for my english, i'm not native speaker.

//14-06-17 Correctifs LED
//10-06-17 Correctif Jeu 1

#include "musical_notes.h"
#include "TM1637.h"                                    
#include "SevenSegmentTM1637.h"
#include "OnewireKeypad.h"
#include "Password.h"

SevenSegmentTM1637    displaya(4, 5);
SevenSegmentTM1637    displayb(6, 7);
SevenSegmentTM1637    displayc(8, 9);
SevenSegmentTM1637    displayd(50, 51);

TM1637 tm1637a(50, 51); //TIMER GLOBAL 45
TM1637 tm1637b(4, 5); //TIMER 1 15
TM1637 tm1637c(6, 7); //TIMER 2 15
TM1637 tm1637d(8, 9); //TIMER 3 15



char KEYS[] = {
  '1', '4', '7', '*',
  '2', '5', '8', '0',
  '3', '6', '9', '#',
  'A', 'B', 'C', 'D'
};
OnewireKeypad <Print, 16 > KP(Serial, KEYS, 4, 4, A0, 4700, 1000, ExtremePrec );
Password password = Password( "8751" );


//PREMIER JEU
int checkbranche = 23;
int checkbrancheb = 22;
int checkbranchec = 24;
int state = HIGH;     
int reading;   
int readingb;
int readingc;           
int previous = LOW;
int previousb = LOW;

int readingstart;
int checkstart = A1;

int speakerPin = 52;
int runOPEN = 0;
int runJEU1 = 0;
int runJEU2 = 0;
int runJEU3 = 0;
int runFAILGLOBAL = 0;

int winJEU1 = 0;
int winJEU2 = 0;
int winJEU3 = 0;
int winGLOBAL = 0;
int looseGLOBAL = 0;

int LED_jeu1_V = 30;
int LED_jeu1_R = 31;
int LED_jeu2_V = 33;
int LED_jeu2_R = 32;
int LED_jeu3_V = 35;
int LED_jeu3_R = 34;

int LED_jglobal1 = 36;
int LED_jglobal2 = 37;
int LED_jglobal3 = 38;
int LED_jglobal4 = 39;

int runcode = 0;

int second=0, minute=45, hour=0; // déclarer des variables de temps 
int second1=0, minute1=15, hour1=0; // déclarer des variables de temps 
int second2=0, minute2=15, hour2=0; // déclarer des variables de temps 
int second3=0, minute3=15, hour3=0; // déclarer des variables de temps 
long previousTime = 0;//détient temps précédent en ms 
long previousTime1 = 0;//détient temps précédent en ms 
long previousTime2 = 0;//détient temps précédent en ms 
long previousTime3 = 0;//détient temps précédent en ms 
long previousDelay = 0;//détient temps précédent en ms depuis buzzer était actif 
long previousDelay1 = 0;//détient temps précédent en ms depuis buzzer était actif 
long previousDelay2 = 0;//détient temps précédent en ms depuis buzzer était actif 
long previousDelay3 = 0;//détient temps précédent en ms depuis buzzer était actif 
long interval = 60000;//60 secondes de retard 
long interval1 = 60000;//60 secondes de retard 
long interval2 = 60000;//60 secondes de retard 
long interval3 = 60000;//60 secondes de retard 

int dismentel1 = 0; int fail1 = 0;
int dismentel2 = 0; int fail2 = 0;
int dismentel3 = 0; int fail3 = 0;
int dismentel4 = 0; int fail4 = 0;

char hour_str[4]; // Pour Convertir int en char 
char minute_str[4]; // Pour Convertir int en char 
char second_str[4]; // Pour Convertir int en char 
char timeInt_str[0];  // Pour Convertir int en char 

int accessgame = 3; //Activation de led quand les minutes arrivent à 3

unsigned char halfsecond=0;

void setup() {
  Serial.begin(19200);

  KP.SetHoldTime(3000);
  KP.SetKeypadVoltage(5.0);
  KP.ShowRange();

   displaya.begin();
   displayb.begin();  
   displayc.begin();
   displayd.begin();

   tm1637a.set(4); 
   tm1637a.init();
   tm1637a.point(POINT_ON); 

   tm1637b.set(4); 
   tm1637b.init();
   tm1637b.point(POINT_ON); 

   tm1637c.set(4); 
   tm1637c.init();
   tm1637c.point(POINT_ON); 

   tm1637d.set(4); 
   tm1637d.init();
   tm1637d.point(POINT_ON); 

   pinMode(checkbranche, INPUT);
   pinMode(checkbrancheb, INPUT);
   pinMode(checkbranchec, INPUT);
   pinMode(checkstart, INPUT);
   pinMode(speakerPin, OUTPUT);

   pinMode(LED_jeu1_V, OUTPUT);
   pinMode(LED_jeu1_R, OUTPUT);
   pinMode(LED_jeu2_V, OUTPUT);
   pinMode(LED_jeu2_R, OUTPUT);
   pinMode(LED_jeu3_V, OUTPUT);
   pinMode(LED_jeu3_R, OUTPUT);

   pinMode(LED_jglobal1, OUTPUT);
   pinMode(LED_jglobal2, OUTPUT);
   pinMode(LED_jglobal3, OUTPUT);
   pinMode(LED_jglobal4, OUTPUT);


   delay(1000);


};




// run loop (forever)
void loop() {

readingstart = digitalRead(checkstart);
reading = digitalRead(checkbranche);
readingb = digitalRead(checkbrancheb);
readingc = digitalRead(checkbranchec); 


if (runcode < 1) {
Serial.println("WHAT");


      if (readingstart == HIGH) {

        for (; runOPEN < 1; runOPEN++) {
          uhoh();
         } 


    countdown();


Serial.print(halfsecond);
Serial.println(" ");


  if (dismentel1 == 1) {digitalWrite(LED_jglobal1, HIGH);} else {digitalWrite(LED_jglobal1, LOW);}
  if (dismentel2 == 1) {digitalWrite(LED_jglobal2, HIGH);} else {digitalWrite(LED_jglobal2, LOW);}
  if (dismentel3 == 1) {digitalWrite(LED_jglobal3, HIGH);} else {digitalWrite(LED_jglobal3, LOW);}
  if (dismentel4 == 1) {digitalWrite(LED_jglobal4, HIGH);} else {digitalWrite(LED_jglobal4, LOW);}


// GESTION DU JEU 1 //
  if (reading == LOW) {  //REVISION 1 HIGH -> LOW
      countdown1();

      if (second1 > 0 && minute1 >= 0) { 
          int8_t timeDisp1[4];          
          timeDisp1[0] = minute1 / 10;   
          timeDisp1[1] = minute1 % 10;   
          timeDisp1[2] = second1 / 10;   
          timeDisp1[3] = second1 % 10;   
          tm1637b.display(timeDisp1);

      if (minute1 < accessgame) {
        digitalWrite(LED_jeu1_V, LOW);
        digitalWrite(LED_jeu1_R, HIGH);        
      } else {
        digitalWrite(LED_jeu1_V, HIGH);
        digitalWrite(LED_jeu1_R, LOW);
      }

          dismentel1 = 0;

      } else {
          displaya.print("FAIL");
            digitalWrite(LED_jeu1_V, HIGH);
            digitalWrite(LED_jeu1_R, LOW);
            for (; runJEU1 < 1; runJEU1++) {
              squeak();
            } 
           fail1 = 1;
      }

  } else {
      displaya.print("--5-");
      digitalWrite(LED_jeu1_V, LOW);
      digitalWrite(LED_jeu1_R, HIGH);
      for (; winJEU1 < 1; winJEU1++) {
        wingame();
       }        
       dismentel1 = 1;
  }


// GESTION DU JEU 1 //


// GESTION DU JEU 2 // 
  if (readingb == HIGH) {
    dismentel2 = 1;
  }

  if ((dismentel1 == 1 or fail1 == 1) && dismentel2 == 0) {  
    countdown2();

    if (second2 > 0 && minute2 >= 0) { 
      int8_t timeDisp2[4];          
      timeDisp2[0] = minute2 / 10;   
      timeDisp2[1] = minute2 % 10;   
      timeDisp2[2] = second2 / 10;   
      timeDisp2[3] = second2 % 10;   
      tm1637c.display(timeDisp2);   
      if (minute2 < accessgame) {
        digitalWrite(LED_jeu2_V, LOW);
        digitalWrite(LED_jeu2_R, HIGH);        
      } else {
        digitalWrite(LED_jeu2_V, HIGH);
        digitalWrite(LED_jeu2_R, LOW);
      }
      dismentel2 = 0;  
    } else {
        displayb.print("FAIL");
          digitalWrite(LED_jeu2_V, HIGH);
          digitalWrite(LED_jeu2_R, LOW);
          for (; runJEU2 < 1; runJEU2++) {
            squeak();
          } 
         fail2 = 1;
    }

  } else if ((dismentel1 == 1 or fail1 == 1)  && dismentel2 == 1){
      displayb.print("-7--");
      digitalWrite(LED_jeu2_V, LOW);
      digitalWrite(LED_jeu2_R, HIGH);  
      for (; winJEU2 < 1; winJEU2++) {
        wingame();
       }        
       dismentel2 = 1;
  } else { 
    displayb.print("----");
      digitalWrite(LED_jeu2_V, HIGH);
      digitalWrite(LED_jeu2_R, HIGH);
      }

// GESTION DU JEU 2 // 


// GESTION DU JEU 3 // 


  if (readingc == HIGH) {
    dismentel3 = 1;
  }

  if ((dismentel2 == 1 or fail2 == 1) && dismentel3 == 0) {  
    countdown3();

    if (second3 > 0 && minute3 >= 0) { 
      int8_t timeDisp3[4];          
      timeDisp3[0] = minute3 / 10;   
      timeDisp3[1] = minute3 % 10;   
      timeDisp3[2] = second3 / 10;   
      timeDisp3[3] = second3 % 10;   
      tm1637d.display(timeDisp3);   
      if (minute3 < accessgame) {
        digitalWrite(LED_jeu3_V, LOW);
        digitalWrite(LED_jeu3_R, HIGH);        
      } else {
        digitalWrite(LED_jeu3_V, HIGH);
        digitalWrite(LED_jeu3_R, LOW);
      }
      dismentel3 = 0;  
    } else {
        displayc.print("FAIL");
          digitalWrite(LED_jeu3_V, HIGH);
          digitalWrite(LED_jeu3_R, LOW);
          for (; runJEU3 < 1; runJEU3++) {
            squeak();
          } 
         fail3 = 1;
    }

  } else if ((dismentel2 == 1 or fail2 == 1)  && dismentel3 == 1){
      displayc.print("---1");
      digitalWrite(LED_jeu3_V, LOW);
      digitalWrite(LED_jeu3_R, HIGH);  
      for (; winJEU3 < 1; winJEU3++) {
        wingame();
       }        
       dismentel3 = 1;
  } else { 
    displayc.print("----");
      digitalWrite(LED_jeu3_V, HIGH);
      digitalWrite(LED_jeu3_R, HIGH);
      }

      } else {

second=0; minute=45; hour=0; // déclarer des variables de temps 
second1=0; minute1=15; hour1=0; // déclarer des variables de temps 
second2=0; minute2=15; hour2=0; // déclarer des variables de temps 
second3=0; minute3=15; hour3=0; // déclarer des variables de temps 
previousTime = 0;//détient temps précédent en ms 
previousTime1 = 0;//détient temps précédent en ms 
previousTime2 = 0;//détient temps précédent en ms 
previousTime3 = 0;//détient temps précédent en ms 
previousDelay = 0;//détient temps précédent en ms depuis buzzer était actif 
previousDelay1 = 0;//détient temps précédent en ms depuis buzzer était actif 
previousDelay2 = 0;//détient temps précédent en ms depuis buzzer était actif 
previousDelay3 = 0;//détient temps précédent en ms depuis buzzer était actif 
interval = 60000;//60 secondes de retard 
interval1 = 60000;//60 secondes de retard 
interval2 = 60000;//60 secondes de retard 
interval3 = 60000;//60 secondes de retard 
runOPEN = 0;
dismentel1 = 0; fail1 = 0;
dismentel2 = 0; fail2 = 0;
dismentel3 = 0; fail3 = 0;
dismentel4 = 0; fail4 = 0;
    displaya.print("-   ");
    displayd.print("-   ");


      }

}

// GESTION DU JEU 3 // 


// VALIDATION GLOBALE PAR DIGICODE //
  char Key;
  byte KState = KP.Key_State();

  if (KState == PRESSED) {
    if ( Key = KP.Getkey() ) {
      Serial << "Pressed: " << Key << "\n";
      switch (Key) {
        case '*': checkPassword(); break;
        case '#': password.reset(); break;
        default: password.append(Key);
      }
    }
  }
  else if (KState == HELD) {
    Serial << "Key:" << KP.Getkey() << " being held\n";
  }
// VALIDATION GLOBALE PAR DIGICODE //




};


void beep (int speakerPin, float noteFrequency, long noteDuration)
{    
  int x;
  // Convert the frequency to microseconds
  float microsecondsPerWave = 1000000/noteFrequency;
  // Calculate how many HIGH/LOW cycles there are per millisecond
  float millisecondsPerCycle = 1000/(microsecondsPerWave * 2);
  // Multiply noteDuration * number or cycles per millisecond
  float loopTime = noteDuration * millisecondsPerCycle;
  // Play the note for the calculated loopTime.
  for (x=0;x<loopTime;x++)   
          {   
              digitalWrite(speakerPin,HIGH); 
              delayMicroseconds(microsecondsPerWave); 
              digitalWrite(speakerPin,LOW); 
              delayMicroseconds(microsecondsPerWave); 
          } 
} 

void waka() {
  for (int i=1000; i<3000; i=i*1.05) {
    beep(speakerPin,i,10);
  }
  delay(100);
  for (int i=2000; i>1000; i=i*.95) {
    beep(speakerPin,i,10);
  }
    for (int i=1000; i<3000; i=i*1.05) {
    beep(speakerPin,i,10);
  }
  delay(100);
  for (int i=2000; i>1000; i=i*.95) {
    beep(speakerPin,i,10);
  }
    for (int i=1000; i<3000; i=i*1.05) {
    beep(speakerPin,i,10);
  }
  delay(100);
  for (int i=2000; i>1000; i=i*.95) {
    beep(speakerPin,i,10);
  }
    for (int i=1000; i<3000; i=i*1.05) {
    beep(speakerPin,i,10);
  }
  delay(100);

            beep(speakerPin, note_Bb5,300); //B b
          delay(50);
          beep(speakerPin, note_C6,300); //C
          delay(50);
          beep(speakerPin, note_Ab5,300); //A b
          delay(50);
          beep(speakerPin, note_Ab4,300); //A b
          delay(50);
          beep(speakerPin, note_Eb5,500); //E b   
          delay(500);   
}

void squeak() {
//  for (int i=100; i<5000; i=i*1.45) {
//    beep(speakerPin,i,60);
//  }
//  delay(10);
  for (int i=3500; i>10; i=i/1.15) {
    beep(speakerPin,i,60);
  }
}
void wingame() {
  for (int i=100; i<5000; i=i*1.45) {
    beep(speakerPin,i,60);
  }

}

void uhoh() {
  for (int i=1000; i<2000; i=i*1.10) {
    beep(speakerPin,i,10);
  }
  delay(50);
  for (int i=1000; i>500; i=i*.90) {
    beep(speakerPin,i,10);
  }
  delay(50);
  for (int i=1000; i<2000; i=i*1.10) {
    beep(speakerPin,i,10);
  }
  delay(50);
  for (int i=1000; i>500; i=i*.90) {
    beep(speakerPin,i,10);
  }
  delay(50);
    for (int i=1000; i<2000; i=i*1.10) {
    beep(speakerPin,i,10);
  }
  delay(50);
  for (int i=1000; i>500; i=i*.90) {
    beep(speakerPin,i,10);
  }
  delay(50);
    for (int i=1000; i<2000; i=i*1.10) {
    beep(speakerPin,i,10);
  }
  delay(50);
  for (int i=1000; i>500; i=i*.90) {
    beep(speakerPin,i,10);
  }
  delay(50);
    for (int i=1000; i<5000; i=i*1.05) {
    beep(speakerPin,i,10);
  }
 delay(300);

  for (int i=1000; i<3000; i=i*1.03) {
    beep(speakerPin,i,10);
  }
  for (int i=3000; i>1000; i=i*.97) {
    beep(speakerPin,i,10);
  }
}

void checkPassword()
{
  if (password.evaluate()) {
    Serial.println("Success");
    runcode = 1;
    displayd.print(" YOU");
    displayd.print(" YOU");
    displaya.print(" ARE");
    displaya.print(" ARE");
    displayb.print(" THE");
    displayb.print(" THE");
    displayc.print("BEST");
    displayc.print("BEST");
    digitalWrite(LED_jglobal4, HIGH);

    for (; winGLOBAL < 1; winGLOBAL++) {
      waka();
     }     
    password.reset();
  } else {
    Serial.println("Wrong");
    password.reset();


    for (; looseGLOBAL < 1; looseGLOBAL++) {
    squeak();
    looseGLOBAL = 0;
    } 


  }
}
/********************************************************************** 
 *                        GLOBAL                                      * 
 **********************************************************************/ 
void countdown(){ 

 static unsigned long lastTick = 0; // mettre en place une variable locale pour maintenir la dernière fois que nous décrémentés une seconde 
  static unsigned long currentMillis = 0; 
  // (variables statiques sont initialisées une fois et conservent leurs valeurs entre les appels de fonction) 
  // diminuer à une seconde tous 1000 millisecondes 
  if (second > 0) { 
    if (millis() - lastTick >= 1000) { 
      lastTick = millis(); 
      second--; 

      int8_t timeDisp[4];          
      timeDisp[0] = minute / 10;   
      timeDisp[1] = minute % 10;   
      timeDisp[2] = second / 10;   
      timeDisp[3] = second % 10;   
      tm1637a.display(timeDisp);   
      tm1637a.point(POINT_ON);

    } 
  } else {
       displayd.print("FAIL");
      for (; runFAILGLOBAL < 1; runFAILGLOBAL++) {
          squeak();
      }    
  }
 // décrémenter une minute toutes les 60 secondes 
  if (minute > 0) { 
    if (second <= 0) { 
      minute--; 
      second = 60; // remettre les secondes à 60 
    } 
  } 
  // décrémenter une heure tous les 60 minutes 
  if (hour > 0) { 
    if (minute <= 0) { 
      hour--; 
      minute = 60; // réinitialiser minutes à 60 
    }//closes if 
  }//closes if 

  //le code ci-dessous bips de la sirène une fois par minute. 
  currentMillis = millis(); 
  if (currentMillis - previousTime > interval) 
  { 
    previousTime = currentMillis; 
    previousDelay = currentMillis; 
  } 

  if (currentMillis - previousDelay > 100) {//100ms durée de chirp 
  } 
} //close countdown(); 


/********************************************************************** 
 *                        JEU1                                        * 
 **********************************************************************/ 
void countdown1(){ 

 static unsigned long lastTick1 = 0; // mettre en place une variable locale pour maintenir la dernière fois que nous décrémentés une seconde 
  static unsigned long currentMillis1 = 0; 
  // (variables statiques sont initialisées une fois et conservent leurs valeurs entre les appels de fonction) 
  // diminuer à une seconde tous 1000 millisecondes 
  if (second1 > 0) { 
    if (millis() - lastTick1 >= 1000) { 
      lastTick1 = millis(); 
      second1--; 

    } 
  } 
 // décrémenter une minute toutes les 60 secondes 
  if (minute1 > 0) { 
    if (second1 <= 0) { 
      minute1--; 
      second1 = 60; // remettre les secondes à 60 
    } 
  } 
  // décrémenter une heure tous les 60 minutes 
  if (hour1 > 0) { 
    if (minute1 <= 0) { 
      hour1--; 
      minute1 = 60; // réinitialiser minutes à 60 
    }//closes if 
  }//closes if 

  //le code ci-dessous bips de la sirène une fois par minute. 
  currentMillis1 = millis(); 
  if (currentMillis1 - previousTime1 > interval1) 
  { 
    previousTime1 = currentMillis1; 
    previousDelay1 = currentMillis1; 
  } 

  if (currentMillis1 - previousDelay1 > 100) {//100ms durée de chirp 
  } 





} //close countdown1();


/********************************************************************** 
 *                        JEU2                                        * 
 **********************************************************************/ 
void countdown2(){ 

 static unsigned long lastTick2 = 0; // mettre en place une variable locale pour maintenir la dernière fois que nous décrémentés une seconde 
  static unsigned long currentMillis2 = 0; 
  // (variables statiques sont initialisées une fois et conservent leurs valeurs entre les appels de fonction) 
  // diminuer à une seconde tous 1000 millisecondes 
  if (second2 > 0) { 
    if (millis() - lastTick2 >= 1000) { 
      lastTick2 = millis(); 
      second2--; 
    } 
  } 
 // décrémenter une minute toutes les 60 secondes 
  if (minute2 > 0) { 
    if (second2 <= 0) { 
      minute2--; 
      second2 = 60; // remettre les secondes à 60 
    } 
  } 
  // décrémenter une heure tous les 60 minutes 
  if (hour2 > 0) { 
    if (minute2 <= 0) { 
      hour2--; 
      minute2 = 60; // réinitialiser minutes à 60 
    }//closes if 
  }//closes if 

  //le code ci-dessous bips de la sirène une fois par minute. 
  currentMillis2 = millis(); 
  if (currentMillis2 - previousTime2 > interval1) 
  { 
    previousTime2 = currentMillis2; 
    previousDelay2 = currentMillis2; 
  } 

  if (currentMillis2 - previousDelay2 > 100) {//100ms durée de chirp 
  }





} //close countdown2();



/********************************************************************** 
 *                        JEU3                                        * 
 **********************************************************************/ 
void countdown3(){ 

 static unsigned long lastTick3 = 0; // mettre en place une variable locale pour maintenir la dernière fois que nous décrémentés une seconde 
  static unsigned long currentMillis3 = 0; 
  // (variables statiques sont initialisées une fois et conservent leurs valeurs entre les appels de fonction) 
  // diminuer à une seconde tous 1000 millisecondes 
  if (second3 > 0) { 
    if (millis() - lastTick3 >= 1000) { 
      lastTick3 = millis(); 
      second3--; 


    } 
  } 
 // décrémenter une minute toutes les 60 secondes 
  if (minute3 > 0) { 
    if (second3 <= 0) { 
      minute3--; 
      second3 = 60; // remettre les secondes à 60 
    } 
  } 
  // décrémenter une heure tous les 60 minutes 
  if (hour3 > 0) { 
    if (minute3 <= 0) { 
      hour3--; 
      minute3 = 60; // réinitialiser minutes à 60 
    }//closes if 
  }//closes if 

  //le code ci-dessous bips de la sirène une fois par minute. 
  currentMillis3 = millis(); 
  if (currentMillis3 - previousTime3 > interval1) 
  { 
    previousTime3 = currentMillis3; 
    previousDelay3 = currentMillis3; 
  } 

  if (currentMillis3 - previousDelay3 > 100) {//100ms durée de chirp 
  } 
} //close countdown2(); 
1

That code is a mess. I'm sure you can refactor a lot of it. To get you started here's a C++ class you can use as a countdown timer. I just whipped this up, but it should work. However, it does not handle the case where millis() overflows (after about 50 days).

class Countdown {
public:
    const uint32_t ms_per_hour = 60l * 60l * 1000l;
    const uint32_t ms_per_minute = 60l * 1000l;
    const uint32_t ms_per_second = 1000l;

    // Constructor
    Countdown(int hours, int minutes, int seconds) {
        duration = hours * ms_per_hour +
                   minutes * ms_per_minute  +
                   seconds * ms_per_second ;
        started = false;
        finished = false;
    }
    // Call this to start the countdown
    void start() {
        started = true;
        finished = false;
        start_time = millis();
    }

    // Stop the timer
    void stop() {
        finished = true;
    }

    // Is timer running?
    bool is_running() {
        return started == true && finished == false;
    }

    // Call this to check if the countdown is finished
    bool times_up() {
        if (finished) return true;
        else if (!started) return false;
        if (elapsed_time() >= duration) {
            finished = true;
            return true;
        }
        return false;
    }

    // Returns a string representation of how much time has passed
    const char * get_elapsed(const char *format="hh:mm:ss") {
        return ms_to_string(format, elapsed_time());
    }
    // Returns a string representation of how much time is left
    const char * get_remaining(const char *format="hh:mm:ss") {
        return ms_to_string(format, duration - elapsed_time());
    }

    // Returns an array of decimal digits for display of time passed
    const char * get_digits_elapsed(const char *format="mmss") {
        return ms_to_digits(format, elapsed_time());
    }
    // Returns an array of decimal digits for display of time remaining
    const char * get_digits_remaining(const char *format="mmss") {
        return ms_to_digits(format, duration - elapsed_time());
    }

protected:
    unsigned long elapsed_time() {
        return millis() - start_time;
    }

    bool started, finished;
    uint32_t duration, start_time;
    char buffer[32];

    char * ms_to_digits(const char *format, uint32_t ms) {
        uint16_t hours = ms / ms_per_hour;
        ms %= ms_per_hour;
        uint16_t minutes = ms / ms_per_minute;
        ms %= ms_per_minute;
        uint16_t seconds= ms / ms_per_second;

        int index = 0;
        for (int i = 0; 0 != format[i]; i++) {
            if ('h' == format[i] || 'm' == format[i] || 's' == format[i]) {
                uint16_t val = 'h' == format[i] ? hours : ('m' == format[i] ? minutes : seconds);
                if (format[i] == format[i+1]) {
                    buffer[index++] = (char)(val / 10);
                    i += 1;
                }
                buffer[index++] = char(val % 10);
            }
        }
        return buffer;
    }

    char * ms_to_string(const char *format, uint32_t ms) {
        uint16_t hours = ms / ms_per_hour;
        ms %= ms_per_hour;
        uint16_t minutes = ms / ms_per_minute;
        ms %= ms_per_minute;
        uint16_t seconds= ms / ms_per_second;

        buffer[0] = 0;
        int index = 0;
        for (int i = 0; 0 != format[i]; i++) {
            if ('h' == format[i] || 'm' == format[i] || 's' == format[i]) {
                uint16_t val = 'h' == format[i] ? hours : ('m' == format[i] ? minutes : seconds);
                if (format[i] == format[i+1]) {
                    index += sprintf(&buffer[index], "%02d", val);
                    i += 1;
                }
                else {
                  index += sprintf(&buffer[index], "%d", val);
                }
            }
            else {
                buffer[index++] = format[i];
            }
        }
        buffer[index] = '\0';
        return buffer;
    }
};

To use it, you would create 3 objects:

Countdown cd1(0, 45, 0);  // 45 minutes
Countdown cd2(0, 15, 0);  // 15 minutes
Countdown cd3(0, 15, 0);  // 15 minutes

Then, start #1 in setup:

void setup() {
    cd1.start();
}

And in loop:

void loop() {
    if (cd3.times_up()) {
       // game over
    }
    else if (cd2.times_up()) {
        // 2nd timer expired, do something
        if (!cd3.is_running()) cd3.start();  // Start #3
        // update display
        tm1637d.display(c3.get_digits_remaining());
    }
    else if (cd1.times_up()) {
        // 1st timer expired, do something
        if (!cd2.is_running()) cd2.start();  // Start #2
        // update display
        tm1637c.display(c2.get_digits_remaining());
    }
    else {
        // update display
        tm1637b.display(c1.get_digits_remaining());
    }
}
5
  • 1
    You wrote: “it does not handle the case where millis() overflows”. Your code handles the millis() rollover perfectly fine. – Edgar Bonet Jun 14 '17 at 21:42
  • @EdgarBonet Ah, yes. You are right. Thanks for pointing that out. – Johnny Mopp Jun 15 '17 at 11:30
  • Thank you for you answer. I understand your code, but i don't know how to show countdown on the 4 digit display with that code. – Fluti Jun 15 '17 at 15:28
  • And also how to stop a countdown with a if (reading == LOW) but i'm going to try. Thank you ! – Fluti Jun 15 '17 at 15:28
  • @Fluti Ok, I added a function to return the time as an array of digits. Also, added a stop() function. – Johnny Mopp Jun 15 '17 at 18:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.