Обратный отсчет когда-нибудь остановится

Я сделал игру с 3 обратными отсчетами и 1 общим обратным отсчетом.

  • Когда мы начнем, глобальный, иди с 45:00 до 00:00.

  • 1 идет с 15:00 до 00:00, а если доходит до 00:00:

  • 2 начинаются и продолжаются с 15:00 до 00:00, а если доходят до 00:00:

  • Три стартуют и идут с 15:00 до 00:00

Если вы выиграли небольшую игру, вы можете остановить обратный отсчет и начать следующую. Если вы выиграете 3 игры, вы остановите обратный отсчет 3 и глобальный тоже.

Но иногда мой второй обратный отсчет останавливается сам, где-то в 13:58 или 10:54…

Возможно ли, что millis() вызывает эту ошибку? У вас есть идея улучшить мой код и исправить это?

Забавный факт: иногда обратный отсчет идет от 01 до 60 вместо 00 до 59……

Извините за мой английский, я не носитель языка.

//14-06-17 Индикатор коррекции
//10-06-17 Коррекция Жеу 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); //ТАЙМЕР ГЛОБАЛЬНЫЙ 45
TM1637 tm1637b(4, 5); // ТАЙМЕР 1 15
TM1637 tm1637c(6, 7); //ТАЙМЕР 2 15
TM1637 tm1637d(8, 9); //ТАЙМЕР 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" );


//ПРЕМЬЕР ЖЕУ
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; // объявление временных переменных
int second1=0, minute1=15, hour1=0; // объявляем временные переменные
int second2=0, minute2=15, hour2=0; // объявляем временные переменные
int second3=0, minute3=15, hour3=0; // объявляем временные переменные
long previousTime = 0;// хранит предыдущее время в мс
long previousTime1 = 0;// хранит предыдущее время в мс
long previousTime2 = 0;// хранит предыдущее время в мс
long previousTime3 = 0;// хранит предыдущее время в мс
long previousDelay = 0;// хранит предыдущее время в мс с момента активации зуммера
long previousDelay1 = 0;// хранит предыдущее время в мс с момента активации зуммера
long previousDelay2 = 0;// хранит предыдущее время в мс с момента активации зуммера
long previousDelay3 = 0;// хранит предыдущее время в мс с момента активации зуммера
long interval = 60000;//задержка 60 секунд
long interval1 = 60000;//задержка 60 секунд
long interval2 = 60000;//задержка 60 секунд
long interval3 = 60000;//задержка 60 секунд

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]; // Чтобы преобразовать int в char
char minute_str[4]; // Чтобы преобразовать int в char
char second_str[4]; // Чтобы преобразовать int в char
char timeInt_str[0];  // Чтобы преобразовать int в char

int accessgame = 3; //Активация светодиода за 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);


};




// запустить цикл (навсегда)
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);}


// ЖЕЛЕЗНАЯ ИНФОРМАЦИЯ 1 //
  if (reading == LOW) {  //ВЕРСИЯ 1 HIGH -> НИЗКИЙ
      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;
  }


// ЖЕЛЕЗНАЯ ИНФОРМАЦИЯ 1 //


// ЖЕЛЕЗНАЯ ВЕРСИЯ 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);
      }

// ЖЕЛЕЗНАЯ ВЕРСИЯ 2 //


// ЖЕЛЕЗНАЯ ВЕРСИЯ 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; // объявляем временные переменные
second1=0; minute1=15; hour1=0; // объявляем временные переменные
second2=0; minute2=15; hour2=0; // объявляем временные переменные
second3=0; minute3=15; hour3=0; // объявляем временные переменные
previousTime = 0;// хранит предыдущее время в мс
previousTime1 = 0;// хранит предыдущее время в мс
previousTime2 = 0;// хранит предыдущее время в мс
previousTime3 = 0;// хранит предыдущее время в мс
previousDelay = 0;// хранит предыдущее время в мс, так как зуммер был активен
previousDelay1 = 0;// хранит предыдущее время в мс, так как зуммер был активен
previousDelay2 = 0;// хранит предыдущее время в мс, так как зуммер был активен
previousDelay3 = 0;// хранит предыдущее время в мс, так как зуммер был активен
interval = 60000;//задержка 60 секунд
interval1 = 60000;//задержка 60 секунд
interval2 = 60000;//задержка 60 секунд
interval3 = 60000;//задержка 60 секунд
runOPEN = 0;
dismentel1 = 0; fail1 = 0;
dismentel2 = 0; fail2 = 0;
dismentel3 = 0; fail3 = 0;
dismentel4 = 0; fail4 = 0;
    displaya.print("-   ");
    displayd.print("-   ");


      }

}

// ЖЕЛЕЗНАЯ ВЕРСИЯ 3 //


// ПРОВЕРКА ГЛОБАЛЬНОГО ЦИФРОВОГО ПАРАМЕТРА //
  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";
  }
// ПРОВЕРКА ГЛОБАЛЬНОГО ЦИФРОВОГО ПАРАМЕТРА //




};


void beep (int speakerPin, float noteFrequency, long noteDuration)
{    
  int x;
  // Преобразование частоты в микросекунды
  float microsecondsPerWave = 1000000/noteFrequency;
  // Подсчитаем, сколько циклов HIGH/LOW приходится на миллисекунду
  float millisecondsPerCycle = 1000/(microsecondsPerWave * 2);
  // Умножить noteDuration * количество циклов в миллисекунду
  float loopTime = noteDuration * millisecondsPerCycle;
  // Воспроизведение ноты в течение рассчитанного цикла 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); //Б б
          delay(50);
          beep(speakerPin, note_C6,300); //С
          delay(50);
          beep(speakerPin, note_Ab5,300); //А б
          delay(50);
          beep(speakerPin, note_Ab4,300); //А б
          delay(50);
          beep(speakerPin, note_Eb5,500); //Е б
          delay(500);   
}

void squeak() {
// для (целое i=100; i < 5000; i=i*1.45) {
// звуковой сигнал (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; // устанавливаем локальную переменную для хранения последнего времени, когда мы уменьшаем значение на одну секунду
  static unsigned long currentMillis = 0; 
  // (статические переменные инициализируются один раз и сохраняют свои значения между вызовами функций)
  // уменьшаем до одной секунды каждые 1000 миллисекунд
  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();
      }    
  }
 // уменьшаем на одну минуту каждые 60 секунд
  if (minute > 0) { 
    if (second <= 0) { 
      minute--; 
      second = 60; // сбросить секунды до 60
    } 
  } 
  // уменьшаем на один час каждые 60 минут
  if (hour > 0) { 
    if (minute <= 0) { 
      hour--; 
      minute = 60; // сброс минут до 60
    }//закрыть, если
  }//закрыть, если

  //приведенный ниже код издает звуковой сигнал сирены раз в минуту.
  currentMillis = millis(); 
  if (currentMillis - previousTime > interval) 
  { 
    previousTime = currentMillis; 
    previousDelay = currentMillis; 
  } 

  if (currentMillis - previousDelay > 100) {//длительность щебета 100 мс
  } 
} // закрыть обратный отсчет();


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

 static unsigned long lastTick1 = 0; // устанавливаем локальную переменную для хранения последнего времени, когда мы уменьшаем значение на одну секунду
  static unsigned long currentMillis1 = 0; 
  // (статические переменные инициализируются один раз и сохраняют свои значения между вызовами функций)
  // уменьшаем до одной секунды каждые 1000 миллисекунд
  if (second1 > 0) { 
    if (millis() - lastTick1 >= 1000) { 
      lastTick1 = millis(); 
      second1--; 

    } 
  } 
 // уменьшаем на одну минуту каждые 60 секунд
  if (minute1 > 0) { 
    if (second1 <= 0) { 
      minute1--; 
      second1 = 60; // сбросить секунды до 60
    } 
  } 
  // уменьшаем на один час каждые 60 минут
  if (hour1 > 0) { 
    if (minute1 <= 0) { 
      hour1--; 
      minute1 = 60; // сброс минут до 60
    }//закрыть, если
  }//закрыть, если

  //приведенный ниже код издает звуковой сигнал сирены раз в минуту.
  currentMillis1 = millis(); 
  if (currentMillis1 - previousTime1 > interval1) 
  { 
    previousTime1 = currentMillis1; 
    previousDelay1 = currentMillis1; 
  } 

  if (currentMillis1 - previousDelay1 > 100) {//длительность щебета 100 мс
  } 





} //закрыть обратный отсчет1();


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

 static unsigned long lastTick2 = 0; // устанавливаем локальную переменную для хранения последнего времени, когда мы уменьшаем значение на одну секунду
  static unsigned long currentMillis2 = 0; 
  // (статические переменные инициализируются один раз и сохраняют свои значения между вызовами функций)
  // уменьшаем до одной секунды каждые 1000 миллисекунд
  if (second2 > 0) { 
    if (millis() - lastTick2 >= 1000) { 
      lastTick2 = millis(); 
      second2--; 
    } 
  } 
 // уменьшаем на одну минуту каждые 60 секунд
  if (minute2 > 0) { 
    if (second2 <= 0) { 
      minute2--; 
      second2 = 60; // сбросить секунды до 60
    } 
  } 
  // уменьшаем на один час каждые 60 минут
  if (hour2 > 0) { 
    if (minute2 <= 0) { 
      hour2--; 
      minute2 = 60; // сброс минут до 60
    }//закрыть, если
  }//закрыть, если

  //приведенный ниже код издает звуковой сигнал сирены раз в минуту.
  currentMillis2 = millis(); 
  if (currentMillis2 - previousTime2 > interval1) 
  { 
    previousTime2 = currentMillis2; 
    previousDelay2 = currentMillis2; 
  } 

  if (currentMillis2 - previousDelay2 > 100) {//длительность щебета 100 мс
  }





} //закрыть обратный отсчет2();



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

 static unsigned long lastTick3 = 0; // устанавливаем локальную переменную для хранения последнего времени, когда мы уменьшаем значение на одну секунду
  static unsigned long currentMillis3 = 0; 
  // (статические переменные инициализируются один раз и сохраняют свои значения между вызовами функций)
  // уменьшаем до одной секунды каждые 1000 миллисекунд
  if (second3 > 0) { 
    if (millis() - lastTick3 >= 1000) { 
      lastTick3 = millis(); 
      second3--; 


    } 
  } 
 // уменьшаем на одну минуту каждые 60 секунд
  if (minute3 > 0) { 
    if (second3 <= 0) { 
      minute3--; 
      second3 = 60; // сбросить секунды до 60
    } 
  } 
  // уменьшаем на один час каждые 60 минут
  if (hour3 > 0) { 
    if (minute3 <= 0) { 
      hour3--; 
      minute3 = 60; // сброс минут до 60
    }//закрыть, если
  }//закрыть, если

  //приведенный ниже код издает звуковой сигнал сирены раз в минуту.
  currentMillis3 = millis(); 
  if (currentMillis3 - previousTime3 > interval1) 
  { 
    previousTime3 = currentMillis3; 
    previousDelay3 = currentMillis3; 
  } 

  if (currentMillis3 - previousDelay3 > 100) {//100 мс продолжительность щебета
  } 
} //закрыть обратный отсчет2();

, 👍0


1 ответ


1

Этот код беспорядок. Я уверен, что вы можете реорганизовать многое из этого. Для начала вот класс C++, который можно использовать в качестве таймера обратного отсчета. Я просто на скорую руку это, но это должно работать. Однако он не обрабатывает случай, когда millis() переполняется (примерно через 50 дней).

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;

    // Конструктор
    Countdown(int hours, int minutes, int seconds) {
        duration = hours * ms_per_hour +
                   minutes * ms_per_minute  +
                   seconds * ms_per_second ;
        started = false;
        finished = false;
    }
    // Вызовите это, чтобы начать обратный отсчет
    void start() {
        started = true;
        finished = false;
        start_time = millis();
    }

    // Остановить таймер
    void stop() {
        finished = true;
    }

    // Таймер запущен?
    bool is_running() {
        return started == true && finished == false;
    }

    // Вызовите это, чтобы проверить, завершен ли обратный отсчет
    bool times_up() {
        if (finished) return true;
        else if (!started) return false;
        if (elapsed_time() >= duration) {
            finished = true;
            return true;
        }
        return false;
    }

    // Возвращает строковое представление того, сколько времени прошло
    const char * get_elapsed(const char *format="hh:mm:ss") {
        return ms_to_string(format, elapsed_time());
    }
    // Возвращает строковое представление того, сколько времени осталось
    const char * get_remaining(const char *format="hh:mm:ss") {
        return ms_to_string(format, duration - elapsed_time());
    }

    // Возвращает массив десятичных цифр для отображения прошедшего времени
    const char * get_digits_elapsed(const char *format="mmss") {
        return ms_to_digits(format, elapsed_time());
    }
    // Возвращает массив десятичных цифр для отображения оставшегося времени
    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;
    }
};

Чтобы использовать его, вы должны создать 3 объекта:

Countdown cd1(0, 45, 0);  // 45 минут
Countdown cd2(0, 15, 0);  // 15 минут
Countdown cd3(0, 15, 0);  // 15 минут

Затем запустите #1 в настройках:

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

И в цикле:

void loop() {
    if (cd3.times_up()) {
       // игра закончена
    }
    else if (cd2.times_up()) {
        // 2-й таймер истек, сделайте что-нибудь
        if (!cd3.is_running()) cd3.start();  // Старт №3
        // обновить дисплей
        tm1637d.display(c3.get_digits_remaining());
    }
    else if (cd1.times_up()) {
        // 1-й таймер истек, сделайте что-нибудь
        if (!cd2.is_running()) cd2.start();  // Старт #2
        // обновить дисплей
        tm1637c.display(c2.get_digits_remaining());
    }
    else {
        // обновить дисплей
        tm1637b.display(c1.get_digits_remaining());
    }
}
,

Вы написали: «_это не обрабатывает случай, когда millis() переполняется_». Ваш код прекрасно обрабатывает ролловер millis()., @Edgar Bonet

@EdgarBonet Ах, да. Ты прав. Спасибо что подметил это., @Johnny Mopp

Спасибо за ответ. Я понимаю ваш код, но я не знаю, как показать обратный отсчет на 4-значном дисплее с этим кодом., @Fluti

А также как остановить обратный отсчет с помощью if (reading == LOW), но я попробую. Благодарю вас !, @Fluti

@Fluti Хорошо, я добавил функцию для возврата времени в виде массива цифр. Также добавлена функция stop()., @Johnny Mopp