Timer function using millis(); not working

I am making a timer function using millis(). I have to make a stopwatch and current time function too and all three have to be able to run simultaneously.

Code:

#include <Adafruit_GFX.h>    // Core graphics library
#include <splash.h>
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <SPI.h>

#include "bitmaps.h"

#define TFT_CS  10
#define TFT_RST 9
#define TFT_DC 8


const uint16_t BLACK = 0x0000;
const uint16_t WHITE = 0xffff;
const uint16_t BLUE = 0x001f;
const uint16_t RED = 0xf800;
const uint16_t YELLOW = 0xffe0;
const uint16_t GREEN = 0x07e0;


Adafruit_ST7735 display = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

bool timerActive = false;
int timerHour = 59;
int timerMinute = 59;
int timerSecond = 59;

int timerCurrentMillis = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  display.initR(INITR_MINI160x80);
  display.setRotation(3);
  display.invertDisplay(true);

  display.setCursor(0, 0);
  display.setTextSize(3);
  display.setTextColor(WHITE, BLACK);
  display.fillScreen(BLACK);
  
  display.drawBitmap(0, 0, boot_logo, 160, 80, BLUE);
  delay(2000);
}

void loop() {
  // put your main code here, to run repeatedly:
  fnTimer();
  
}

void uiHome() {
  
}

void fnTimer() {
  if(millis() - timerCurrentMillis == 1000) {
    if((timerHour == 0) & (timerMinute == 0) & (timerSecond == 0)){
      
    }
    else if(timerSecond > 0) {
      timerSecond --;
      
    } else {
      timerSecond = 59;
      if(timerMinute > 0) {
        timerMinute --;
        
      } else {
        timerMinute = 59;
        if(timerHour > 0) {
          timerHour --;
        }
      }
    }
    Serial.println(String(timerHour) + ":" + String(timerMinute) + ":" + String(timerSecond));
    timerCurrentMillis = millis();
  }
}

But nothing is being printed in serial. An example code or a correction would be helpful.

Here are a few things you need to look into:

  • millis returns type unsigned long, all variables for calculation should be of that type

  • comparing timers should not be done with equal but less then or more then, otherwise you may miss the event

  • your timerCurrentMillis is not the current value, it is the previous value, you may want to change the name

  • time math should be done with epoch/UNIX time, convert to hours, minutes and seconds for display and storing in files e.g., csv

Have a look at the following example

File -> Examples -> 02.Digital -> BlinkWithoutDelay

https://en.wikipedia.org/wiki/Unix_time

1 Like

The Serial.println() can print integers, you don't have to convert them to String().

To use 'AND' in a condition of a if-statement, you have to use '&&' or 'and'.

if( today == sunday && month == december)
  ...

if( today == sunday and month == december)
  ...

See also here: https://www.arduino.cc/reference/en/, the section "Boolean Operators".

This is doing nothing, you may remove it:

if((timerHour == 0) & (timerMinute == 0) & (timerSecond == 0)){
}

Do you want a stopwatch (counting up) or a count-down timer ?

There are two ways for a count-down timer:

  • Use millis() itself. The advantage is that it has millisecond precision and nothing can be missed. The disadvantage is that millis() could run past the zero point, and you need some extra code to deal with that.
  • Use a millis-timer and solve everything in that millis-timer.

Try again with all our tips and show us the new sketch.
Is it for school ?

#include <Adafruit_GFX.h>    // Core graphics library
#include <splash.h>
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <SPI.h>

#include "bitmaps.h"

#define TFT_CS  10
#define TFT_RST 9
#define TFT_DC 8


const uint16_t BLACK = 0x0000;
const uint16_t WHITE = 0xffff;
const uint16_t BLUE = 0x001f;
const uint16_t RED = 0xf800;
const uint16_t YELLOW = 0xffe0;
const uint16_t GREEN = 0x07e0;


Adafruit_ST7735 display = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

bool timerActive = false;
int timerHour = 59;
int timerMinute = 59;
int timerSecond = 59;

unsigned long timerCurrentMillis = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  display.initR(INITR_MINI160x80);
  display.setRotation(3);
  display.invertDisplay(true);

  display.setCursor(0, 0);
  display.setTextSize(3);
  display.setTextColor(WHITE, BLACK);
  display.fillScreen(BLACK);
  
  display.drawBitmap(0, 0, boot_logo, 160, 80, BLUE);
  delay(2000);
}

void loop() {
  // put your main code here, to run repeatedly:
  fnTimer();
  
}

void uiHome() {
  
}

void fnTimer() {
  if(millis() - timerCurrentMillis >= 1000) {
    if((timerHour == 0) && (timerMinute == 0) && (timerSecond == 0)){
      
    }
    else if(timerSecond > 0) {
      timerSecond --;
      
    } else {
      timerSecond = 59;
      if(timerMinute > 0) {
        timerMinute --;
        
      } else {
        timerMinute = 59;
        if(timerHour > 0) {
          timerHour --;
        }
      }
    }
    Serial.println(String(timerHour) + ":" + String(timerMinute) + ":" + String(timerSecond));
    timerCurrentMillis = millis();
  }
}

I changed some things in this code and now it works. This is a part of a project so I will add things to this later:

if((timerHour == 0) && (timerMinute == 0) && (timerSecond == 0)){
      
    }

And also I wonder if I make a stopwatch function and run it at the same time with the timer function will it work?

It is a good start :smiley:

In your Arduino loop(), you have a millis timer with:

if(millis() - timerCurrentMillis >= 1000) {

It only becomes active once per second. That means the Arduino is doing nothing most of the time. You can add many more millis-timers.
With a little tweaking I could run 400 millis-timers on a Arduino Uno here.

The variable name 'timerCurrentMillis' is confusing.
Could you follow the Blink Without Delay example ? https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay.
It is the value of millis() that is stored to be able to compare it. You could call it 'previousMillis' or something with the term "timestamp" or so.

This can be more precise:

if(millis() - timerCurrentMillis >= 1000) {

  // lots of code

  timerCurrentMillis = millis();
}

The millis() could meanwhile have changed. That can be fixed with by using a variable instead of the millis().

// global variables
unsigned long previousMillis;
const unsigned long interval = 1000;
...

unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= interval) {
  previousMillis = currentMillis;    // normal millis-timer, is safe and works always

  // lots of code

}

It can be even more improved for a clock or countdown timer.
If there is other code in the Arduino loop(), then millis() could be passed the interval and everything shifts forward in time. That can be prevented by staying in sync with the time.

// global variables
unsigned long previousMillis;
const unsigned long interval = 1000;
...

unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= interval) {
  previousMillis += interval;   // special millis-timer, stay in sync with time

  // lots of code

}