Countdown timer problem

Hi,

I know this topic has been covered a few times but I still cant get this to work.
I’m trying to make a countdown timer with an arduino uno by using millis that only dispays the seconds on a 4 bit 7 seconds. I’m trying to make it beep when it gets to the last five seconds to let people know the time is about to expire. I’m using a potmeter to set the amount of seconds.

I’ve gotten this far but I cant get it to work when I get to the last five seconds.

Please note that I’m still a beginner so there might be some useless stuff in there.

Could comeone please explain what I’m doing wrong?

countdowntimer.ino (2.27 KB)

#include "TM74HC595Display.h"

int SCLK = 7;
int RCLK = 6;
int DIO = 5;
int BUTTON = 2;
int ledPin = 3;
int ledState = LOW;
unsigned long starttime;
unsigned long countstart;
unsigned long value;
int  ledvalue;
unsigned long ledtimer;
bool pressed = false;

TM74HC595Display disp(SCLK, RCLK, DIO);
unsigned char LED_0F[29];


void setup() {
  // put your setup code here, to run once:
 Serial.begin(9600);
  LED_0F[0] = 0xC0; //0
  LED_0F[1] = 0xF9; //1
  LED_0F[2] = 0xA4; //2
  LED_0F[3] = 0xB0; //3
  LED_0F[4] = 0x99; //4
  LED_0F[5] = 0x92; //5
  LED_0F[6] = 0x82; //6
  LED_0F[7] = 0xF8; //7
  LED_0F[8] = 0x80; //8
  LED_0F[9] = 0x90; //9
  LED_0F[10] = 0x88; //A
  LED_0F[11] = 0x83; //b
  LED_0F[12] = 0xC6; //C
  LED_0F[13] = 0xA1; //d
  LED_0F[14] = 0x86; //E
  LED_0F[15] = 0x8E; //F
  LED_0F[16] = 0xC2; //G
  LED_0F[17] = 0x89; //H
  LED_0F[18] = 0xF9; //I
  LED_0F[19] = 0xF1; //J
  LED_0F[20] = 0xC3; //L
  LED_0F[21] = 0xA9; //n
  LED_0F[22] = 0xC0; //O
  LED_0F[23] = 0x8C; //P
  LED_0F[24] = 0x98; //q
  LED_0F[25] = 0x92; //S
  LED_0F[26] = 0xC1; //U
  LED_0F[27] = 0x91; //Y
  LED_0F[28] = 0xFE; //hight -

pinMode(ledPin, OUTPUT);
pinMode(BUTTON, INPUT);
starttime = 0;
ledvalue = 5;
}

void loop() {
if (digitalRead(BUTTON) == HIGH){
  pressed = true;
  Serial.println("pressed");
  countstart = millis();
  //starttime = millis();
}

int sensorValue = analogRead(A0); 
value = map(sensorValue,0,1023,0,180);
Serial.println(value);
disp.digit4(value,100);

while (pressed == true){

while ((value - (millis() - countstart)/1000) >= 5){
    Serial.println(value - (millis() - countstart)/1000);
    disp.digit4((value - (millis() - countstart)/1000),100);
    starttime = millis();
}

      if (millis() - starttime == 1000){
        ledvalue = ledvalue - 1;
        starttime = millis();
        Serial.println("beep");
        Serial.println(ledvalue);
        disp.digit4(ledvalue,100);
        digitalWrite(ledPin, HIGH);
        ledtimer = millis();
        
        if (ledvalue < 0);
          Serial.println("Timer has ended");
          while(1)
          {
            
          }

      }          
      if (millis() - ledtimer == 500){
            digitalWrite(ledPin, LOW);
      }
}
}

forgot to post the code

The first I would like to do is to declare Button pin as INPUT_PULLUP unless You have an external resistor connected. Maybe You have a ull down resistor.

In loop() I find "while( pressed)" . There the execution will be hanging because there is no pressed = false down on the code. You also have an anchor, while(1), that will hang up the sketch.

The while(1) is in the “timer ended” part so that may very well be what OP intends to do. Pressing the reset button will restart the timer.

The other while statements are probably best replaced by if statements.

if (millis() - starttime == 1000){

This is dangerous: what if you miss that particular millisecond? It’s not likely, but nonetheless not safe. Do this instead:

if (millis() - starttime >= 1000) {
  starttime += 1000;

Now if you happen to miss that ms, it doesn’t matter much. Increase starttime by 1000 every time to make sure the next is at the correct time, regardless whether you’re 1, 20 or even 100 ms late.

In loop() I find "while( pressed)" . There the execution will be hanging because there is no pressed = false down on the code. You also have an anchor, while(1), that will hang up the sketch.

thanks for the reply. The code is not finished yet, for now I'm just focussing on the countdown part. Later I'll add an action that takes it out of the ancor and sets the pressed to false.

Now if you happen to miss that ms, it doesn't matter much. Increase starttime by 1000 every time to make sure the next is at the correct time, regardless whether you're 1, 20 or even 100 ms late.

Good suggestion. I added it to the code but now when it gets to the part were the timing is lower than 5 the serial prints the ledvalue of 4 and just says it ends.

Looking at the code I would expect it to count down from 4 to 0.

if (millis () - ledtimer == 500)…
<=?

The only value you need to remember is the millisecond that the countdown started. From that you can calculate how many milliseconds have elapsed since the timer started. You calculate the countdown start value from the pot input. You can get the time to display (time remaining) by subtracting the elapsed milliseconds from the countdown start value. When the remaining time is <=5000 (5 seconds) do the beeping. When time remaining is 0, stop the timer.

// #include "TM74HC595Display.h"

int SCLK = 7;
int RCLK = 6;
int DIO = 5;
int BUTTON = 2;
int ledPin = 3;
int ledState = LOW;
unsigned long starttime;
unsigned long countstart;
unsigned long value;
int  ledvalue;
unsigned long ledtimer;
bool pressed = false;

// TM74HC595Display disp(SCLK, RCLK, DIO);
unsigned char LED_0F[29];


void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  LED_0F[0] = 0xC0; //0
  LED_0F[1] = 0xF9; //1
  LED_0F[2] = 0xA4; //2
  LED_0F[3] = 0xB0; //3
  LED_0F[4] = 0x99; //4
  LED_0F[5] = 0x92; //5
  LED_0F[6] = 0x82; //6
  LED_0F[7] = 0xF8; //7
  LED_0F[8] = 0x80; //8
  LED_0F[9] = 0x90; //9
  LED_0F[10] = 0x88; //A
  LED_0F[11] = 0x83; //b
  LED_0F[12] = 0xC6; //C
  LED_0F[13] = 0xA1; //d
  LED_0F[14] = 0x86; //E
  LED_0F[15] = 0x8E; //F
  LED_0F[16] = 0xC2; //G
  LED_0F[17] = 0x89; //H
  LED_0F[18] = 0xF9; //I
  LED_0F[19] = 0xF1; //J
  LED_0F[20] = 0xC3; //L
  LED_0F[21] = 0xA9; //n
  LED_0F[22] = 0xC0; //O
  LED_0F[23] = 0x8C; //P
  LED_0F[24] = 0x98; //q
  LED_0F[25] = 0x92; //S
  LED_0F[26] = 0xC1; //U
  LED_0F[27] = 0x91; //Y
  LED_0F[28] = 0xFE; //hight -

  pinMode(ledPin, OUTPUT);
  pinMode(BUTTON, INPUT);
  starttime = 0;
  ledvalue = 5;
  countstart = millis();
}

void loop()
{
  unsigned long currentMillis = millis();

  int sensorValue = analogRead(A0);
  unsigned long countdownStartValue = map(sensorValue, 0, 1023, 0, 180000);  // 0 to 180 seconds

  unsigned long elapsedMillis = currentMillis - countstart;
  unsigned long remainingMillis;
  if (elapsedMillis > countdownStartValue)
    remainingMillis = 0;
  else
    remainingMillis = countdownStartValue - elapsedMillis;

  //disp.digit4(remainingMillis / 1000 , 100);

  if (remainingMillis == 0)
  {
    Serial.println("Timer has ended");
    while (1)
    {
    }
  }

  if (remainingMillis < 5000)
  {
    Serial.println("beep");
    digitalWrite(ledPin, (remainingMillis/1000) & 1); // Light on odd numbered seconds
  }
}

Here is a sketch I have written, which you may wish to look at for ideas.
I warn you, though: parts of this sketch are untested code and might not work as expected!

// count_up_or_down_timer.ino
// by odometer  2018-12-26

// This sketch acts as a simple count-up or countdown timer.
// The time (elapsed or remaining) is shown on a 16x2 LCD character display.

#include <LiquidCrystal.h>

// declare and initialize variables
int countDirection = 1; // direction of count: 1 for up, -1 for down, 0 for stopped
int nowDays    = 0; // "days" part of time
int nowHours   = 0; // "hours" part of time
int nowMinutes = 0; // "minutes" part of time
int nowSeconds = 0; // "seconds" part of time
int nowTenths  = 0; // "tenths of seconds" part of time
unsigned long microsAtLastTenth = 0UL; // value of micros() at most recent 1/10 second

// a pretty important constant
const unsigned long MICROS_PER_TENTH = 100000UL; // number of microseconds per 1/10 second

// here we specify what pins our LCD is on
//                RS  EN  D4  D5  D6  D7
LiquidCrystal lcd(12, 11,  5,  4,  3,  2);

void setup() {
 
  // start the LCD going
  lcd.begin(16, 2);
 
  // display the time
  updateTimeDisplay();
 
}

void loop() {
 
  // check if it is time for the clock to advance
  if ((micros() - microsAtLastTenth) >= MICROS_PER_TENTH) {
   
    // make the clock advance (or retreat) 1/10 of a second
    nowTenths += countDirection;
   
    // make sure that the next advance happens exactly when it is due
    // (they should happen exactly at intervals of 1/10 of a second)
    microsAtLastTenth += MICROS_PER_TENTH;
   
    // our clock needs to do more than count tenths of seconds
    // it also needs to count whole seconds, and minutes, and hours, and days
    // so let's go ahead and do that
    
    // too many tenths?
    if (nowTenths >= 10) {
      // trade 10 tenths for 1 second
      nowTenths -= 10;
      nowSeconds++;
    }
   
    // too many seconds?
    if (nowSeconds >= 60) {
      // trade 60 seconds for 1 minute
      nowSeconds -= 60;
      nowMinutes++;
    }
   
    // too many minutes?
    if (nowMinutes >= 60) {
      // trade 60 minutes for 1 hour
      nowMinutes -= 60;
      nowHours++;
    }
   
    // too many hours?
    if (nowHours >= 24) {
      // trade 24 hours for 1 day
      nowHours -= 24;
      nowDays++;
    }
    
    // too few tenths?
    if (nowTenths < 0) {
      // trade 1 second for 10 tenths
      nowSeconds--;
      nowTenths += 10;
    }
    
    // too few seconds?
    if (nowSeconds < 0) {
      // trade 1 minute for 60 seconds
      nowMinutes--;
      nowSeconds += 60;
    }
    
    // too few minutes?
    if (nowMinutes < 0) {
      // trade 1 hour for 60 minutes
      nowHours--;
      nowMinutes += 60;
    }
    
    // too few hours?
    if (nowHours < 0) {
      // trade 1 day for 24 hours
      nowDays--;
      nowHours += 24;
    }
    
    // too few days?
    if (nowDays < 0) {
      // The time has gone below zero!! (this should never happen)
      // Deal with it by resetting the time to all zeros
      nowDays    = 0;
      nowHours   = 0;
      nowMinutes = 0;
      nowSeconds = 0;
      nowTenths  = 0;            
    }
    
    // are we counting down?
    if (countDirection < 0) {
      // if so, have we reached zero?
      if ((nowDays==0)&&(nowHours==0)&&(nowMinutes==0)&&(nowSeconds==0)&&(nowTenths==0)) {
        // reverse the direction of counting (so as to be counting up instead of down)
        countDirection = -countDirection;
      }
    }
   
    // update the display so we can see the new time
    updateTimeDisplay();
   
  }
 
}


void updateTimeDisplay () {
  // function to update the display to show the current elapsed time
  //
  // we want the display to look like this
  //   Position: 01234567890123456
  //     Line 0:  days  h  m  s 
  //     Line 1:     0 00:00:00.0

  // declare a buffer for storing a string (we'll need it later)
  // we expect to need only 16 characters, but to be safe, let's make room for 20
  // so we allow 20 characters worth of room, plus 1 extra for the null terminator
  // (Always allow 1 character extra worth of room for the null terminator!)
  char buf[21];

  // move to the top line of the display
  lcd.setCursor(0,0);
 
  // show the units
  lcd.print(" days  h  m  s  ");
 
  // convert the elapsed time to a string
  // for days, allow 5 digits worth of room
  // for hours, minutes, and seconds, allow 2 digits for each, and use leading zeros
  // for tenths, allow 1 digit worth of room
  sprintf(buf, "%5d %02d:%02d:%02d.%1d", nowDays, nowHours, nowMinutes, nowSeconds, nowTenths);

  // move to the bottom line of the display 
  lcd.setCursor(0,1);
 
  // show the string for the elapsed time
  lcd.print(buf);
}