Day timer using an Arduino

Good day,

I made a day timer as part of a bigger project but I feel that there are ways it can be done better. You basically set the number of days the timer runs and that's it.

Hoping I can do the code with may be less lines or less redundancies, not sure but I feel there's a better way of doing it. I understand I can also use an RTC for time tracking but I can assure that the code wont run anything beyond a week.

Could anyone point out some flaws or ways to improve my code?

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#define dayCount 10;

LiquidCrystal_I2C lcd(0x27, 20, 4);

#include "Countimer.h"

Countimer tUp;
Countimer tDown;
Countimer tNone;

int y = 0;
int loopCount = 0;
int prevCount = 0;

void setup(){
  // Setup LCD with backlight and initialize
  Serial.begin(9600);
  lcd.backlight();
  lcd.init();
  lcd.begin(20, 4);
  daysLeft();
  loopCount = dayCount;
  loopCount > 9 ? y=4 : y=5;
  lcd.setCursor(y, 2);
  lcd.print(String(loopCount) + ":00:00:00");
  loopCount--;
  delay(1000);
  lcd.clear();
  tDown.setCounter(23, 59, 59, tDown.COUNT_DOWN, tDownComplete);
  tDown.setInterval(print_time2, 1000);
}
 
void loop(){
  do{
    tDown.run();
    tDown.start();
  }while (loopCount>=0);
  tDown.stop();
  tDown.restart();
  loopCount = dayCount;
  tDown.start();
}

void tDownComplete()
{
  prevCount = loopCount;
  daysLeft();
  loopCount > 9 ? y=4:y=5;
  lcd.setCursor(y, 2);
  lcd.print(String(loopCount) + ":");
  lcd.print("00:00:00");
  loopCount--;
}
void print_time2()
{
  if ((loopCount == 99 && prevCount == 100) || (loopCount == 9 && prevCount == 10)){
    lcd.clear();
    prevCount = loopCount;
  }
  daysLeft();
  loopCount > 9 ? y=4:y=5;
  lcd.setCursor(y, 2);
  lcd.print(loopCount);
  lcd.print(':');
	lcd.print(tDown.getCurrentTime());
}
void daysLeft(){
  lcd.setCursor(0,0);
  lcd.print("Days left");
}
loopCount > 9 ? y=4:y=5;

The above seems awkward. :thinking:

y = loopCount > 9 ? 4 : 5;

Both works. Is that proper coding syntax?

  • Yes both work.
  • It was just a comment.
  • I like the x = something style.

Thank you, this is something I have to consider.

If you find anything else that's a bit off/awkward. Let me know.

It's a good way for me to improve :grinning:

  • I have not used the Countimer library.

  • Whenever I need a similar process, excluding using an RTC, I just use a TIMER based on millis() or micros().

  • Am not a fan of using do() or while() as they can result in code blocking.
    They are fine for very fast/short timing situations.

What's the benefit of using millis() and micros() over say a timer library?

How would you go about doing a do while instead?

Suggest this as a starting point sketch:

//
//  https://forum.arduino.cc/t/day-timer-using-an-arduino/1238757/1
//
//
//  Version    YY/MM/DD    Comments
//  =======    ========    ====================================================================
//  1.00       24/03/22    Running code
//
//
//

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);


//********************************************^************************************************

#define EXPIRED                         true
#define stillTIMING                     false

#define ENABLED                         true
#define DISABLED                        false

#define LEDon                           HIGH    //+5V---[220R]---[LED]---PIN
#define LEDoff                          LOW

#define OPENED                          HIGH    //+5V---[Internal 50k]---PIN---[switch]---GND
#define CLOSED                          LOW

#define PRESSED                         HIGH    //+5V---[Internal 50k]---PIN---[switch]---GND
#define notPRESSED                      LOW

const byte heartbeatLED               = 13;

//                                                  1
//elements                                01234567890
char _time[]                          = {"00:00:00:00"};

const int days                        = 10;
const int hours                       = 0;
const int minutes                     = 0;
const int seconds                     = 0;

int  currentDays                      = days;
int  currentHours                     = hours;
int  currentMinutes                   = minutes;
int  currentSeconds                   = seconds;

//timing stuff
unsigned long heartbeatTime;
unsigned long heartbeatInterval       = 250ul;

unsigned long clockTime;
unsigned long clockInterval           = 1000ul;


//                                       s e t u p ( )
//********************************************^************************************************
void setup()
{
  Serial.begin(9600);

  pinMode(heartbeatLED, OUTPUT);

  lcd.backlight();
  lcd.init();
  lcd.begin(20, 4);

  lcd.clear();
  lcd.setCursor(4, 2);

  //update the clock
  convertClockTime();
  
  lcd.print(_time);

  delay(1000);

} //END of   setup()


//                                        l o o p ( )
//********************************************^************************************************
void loop()
{
  //****************************************                      heartbeat   T I M E R
  //is it time to toggle the heartbeatLED ?
  if (millis() - heartbeatTime >= heartbeatInterval)
  {
    //restart this TIMER
    heartbeatTime = millis();

    //toggle the heartbeatLED
    digitalWrite(heartbeatLED, digitalRead(heartbeatLED) == HIGH ? LOW : HIGH);
  }

  //****************************************                      counter   T I M E R
  //has a second expired ?
  if (millis() - clockTime >= clockInterval)
  {
    //restart this TIMER
    clockTime = clockTime + clockInterval;

    //update the clock
    currentSeconds--;

    //update the time in our clock
    if (currentSeconds < 0)
    {
      currentSeconds = 59;
      currentMinutes--;

      if (currentMinutes < 0)
      {
        currentMinutes = 59;
        currentHours--;

        if (currentHours < 0)
        {
          currentHours = 23;
          currentDays--;

          if (currentDays < 0)
          {
            //reset the count down clock
            currentDays = days;
          }
        }
      }
    }

    updateDisplay();
  }

  //****************************************
  // Other non blocking code goes here
  //****************************************

} //END of   loop()


//                               u p d a t e D i s p l a y ( )
//********************************************^************************************************
void updateDisplay()
{
  convertClockTime();

  lcd.setCursor(4, 2);

  //                   1111111111
  //         01234567890123456789
  //             10:00:00:00
  //             09:23:59:59
  lcd.print(_time);

} //END of   updateDisplay()


//                           c o n v e r t C l o c k T i m e ( )
//********************************************^************************************************
void convertClockTime()
{
  //convert the count down clock to a character string
  _time[0] = '0' + currentDays / 10;
  _time[1] = '0' + currentDays - (currentDays / 10) * 10;
  _time[2] = ':';
  _time[3] = '0' + currentHours / 10;
  _time[4] = '0' + currentHours - (currentHours / 10) * 10;
  _time[5] = ':';
  _time[6] = '0' + currentMinutes / 10;
  _time[7] = '0' + currentMinutes - (currentMinutes / 10) * 10;
  _time[8] = ':';
  _time[9]  = '0' + currentSeconds / 10;
  _time[10] = '0' + currentSeconds - (currentSeconds / 10) * 10;

} //END of convertClockTime()

Edit

  • Libraries tend to hide things from the user.
    Libraries are useful in complex code situations.
    Simple things like TIMERs, can be handled easily without a Library.
1 Like

Woah, this looks pretty clean for a code that doesn't use a timer-focused library.

  • As it stands, this sketch only displays a count down, however, it is easily added to make something happen when the countdown reaches zero . . .
    Further more, this shows non blocking code behaviour which we should always strive for.

Hi @roamingal ,

here is a sketch that also performs a day countdown based on the millis() function

  • therefore precision limited to the internal Arduino millis() function and
  • it will start from the beginning in case of a restart (which could be handled in case a RTC would be used)
/*

  Forum: https://forum.arduino.cc/t/day-timer-using-an-arduino/1238757
  Wokwi: https://wokwi.com/projects/393146816707048449

*/

const unsigned long secsPerMinute  = 60;
const unsigned long secsPerHour    = 60 * secsPerMinute;
const unsigned long maxSecsPerDay  = 24 * secsPerHour;
unsigned long leftSecsPerDay;
unsigned long lastUpdate = 0;

int daysSpent = 0;
boolean countDownActive;

// Set the days to count here:
const int maxDaysSpent = 5;


void setup() {
  Serial.begin(115200);
  countDownActive = true;

  // Set a test date and time here the sketch
  // shall start with
  // E.g.: 5 days expired, at 23:59:55 hours:minutes:seconds
  setTestDateTime(5, 23, 59, 55);

}

void loop() {
  countDown();
}

void countDown() {
  if (!countDownActive) {
    return;
  }
  if (millis() - lastUpdate >= 1000) {
    lastUpdate = millis();
    leftSecsPerDay--;
    if (leftSecsPerDay == 0) {
      daysSpent++;
      leftSecsPerDay = maxSecsPerDay;
    }
    if (daysSpent > maxDaysSpent) {
        countDownActive = false;
     }
    if (countDownActive) {
      printCountDown();
    } else {
      Serial.println("Countdown stopped");
    }
  }
}

void printCountDown() {
  int days = maxDaysSpent - daysSpent;
  uint16_t hours = leftSecsPerDay / secsPerHour;
  uint16_t minutes = (leftSecsPerDay - hours * secsPerHour) / secsPerMinute;
  uint16_t seconds = leftSecsPerDay - hours * secsPerHour - minutes * secsPerMinute;
  char buffer[40];
  snprintf(buffer, sizeof(buffer), " %2d - %02d:%02d:%02d", days, hours, minutes, seconds);
  Serial.println(buffer);
}

void setTestDateTime(int daysDone, uint16_t hourAtDayDone, uint16_t minutesAtDayDone,
                     uint16_t secondsAtDayDone)
{
  daysSpent = daysDone;
  leftSecsPerDay = maxSecsPerDay - hourAtDayDone * secsPerHour - minutesAtDayDone * secsPerMinute - secondsAtDayDone;
}

You can check it out on Wokwi: https://wokwi.com/projects/393146816707048449

I have implemented a simple function that sets certain variables which is only required for testing purposes:

  // Set a test date and time here the sketch
  // shall start with
  // E.g.: 5 days expired, at 23:59:55 hours:minutes:seconds
  setTestDateTime(5, 23, 59, 55);

Based on the naming of the variables and functions, it should (I hope) be more or less “self-explanatory” (I hope :wink: ).

Good luck!
ec2021

A version for LCD on Wokwi https://wokwi.com/projects/393161788734307329 :

Sketch for LCD
/*

  Forum: https://forum.arduino.cc/t/day-timer-using-an-arduino/1238757
  Wokwi: https://wokwi.com/projects/393161788734307329

*/

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);

const unsigned long secsPerMinute  = 60;
const unsigned long secsPerHour    = 60 * secsPerMinute;
const unsigned long maxSecsPerDay  = 24 * secsPerHour;
unsigned long leftSecsPerDay;
unsigned long lastUpdate = 0;

int daysSpent = 0;
boolean countDownActive;

// Set the days to count here:
const int maxDaysSpent = 5;


void setup() {
  Serial.begin(115200);
  countDownActive = true;
  lcd.backlight();
  lcd.init();
  lcd.begin(20, 4);
  // Set a test date and time here the sketch
  // shall start with
  // E.g.: 5 days expired, at 23:59:55 hours:minutes:seconds
  setTestDateTime(5, 23, 59, 55);

}

void loop() {
  countDown();
}

void countDown() {
  if (!countDownActive) {
    return;
  }
  if (millis() - lastUpdate >= 1000) {
    lastUpdate = millis();
    leftSecsPerDay--;
    if (leftSecsPerDay == 0) {
      daysSpent++;
      leftSecsPerDay = maxSecsPerDay;
    }
    if (daysSpent > maxDaysSpent) {
      countDownActive = false;
    }
    if (countDownActive) {
      printCountDown();
    } else {
      printLCD("  0 - 00:00:00", 4, 2);
      //      Serial.println("Countdown stopped");
    }
  }
}

void printCountDown() {
  int days = maxDaysSpent - daysSpent;
  uint16_t hours = leftSecsPerDay / secsPerHour;
  uint16_t minutes = (leftSecsPerDay - hours * secsPerHour) / secsPerMinute;
  uint16_t seconds = leftSecsPerDay - hours * secsPerHour - minutes * secsPerMinute;
  char buffer[40];
  snprintf(buffer, sizeof(buffer), " %2d - %02d:%02d:%02d", days, hours, minutes, seconds);
  //  Serial.println(buffer);
  printLCD(buffer, 4, 2);
}

void printLCD(char msg[40], int col, int row) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Days left");
  lcd.setCursor(col, row);
  lcd.print(msg);
}


void setTestDateTime(int daysDone, uint16_t hourAtDayDone, uint16_t minutesAtDayDone,
                     uint16_t secondsAtDayDone)
{
  daysSpent = daysDone;
  leftSecsPerDay = maxSecsPerDay - hourAtDayDone * secsPerHour - minutesAtDayDone * secsPerMinute - secondsAtDayDone;
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.