Topics

► Games

► Sound & Music

► Watches & Clocks

► GPS

► Power Supplies

► Computers

► Graphics

► Thermometers

► Wearables

► Test Equipment

► Tutorials

► Libraries

► PCB-Based Projects

By processor

AVR ATtiny

► ATtiny10

► ATtiny2313

► ATtiny84

► ATtiny841

► ATtiny85

► ATtiny861

► ATtiny88

AVR ATmega

► ATmega328

► ATmega1284

AVR 0-series and 1-series

► ATmega4809

► ATtiny1604

► ATtiny1614

► ATtiny3216

► ATtiny3227

► ATtiny402

► ATtiny404

► ATtiny414

► ATtiny814

AVR DA/DB-series

► AVR128DA28

► AVR128DA32

► AVR128DA48

► AVR128DB28

ARM

► ATSAMD21

► RP2040

► RA4M1

About me

  • About me
  • Twitter
  • Mastodon

Feeds

RSS feed

Making millis() tell the time

13th May 2016

The built-in Arduino function millis() returns the number of milliseconds since the Arduino was reset. Wouldn't it be more useful if it gave the actual time of day? For example, you've designed a digital clock program and uploaded it to your Arduino. Why can't it start at the correct time? After all, the computer you uploaded it from knows the time. Here's a solution.

The GCC compiler provides a macro __TIME__ which gives the time that the source file was compiled, in the format "HH:MM:SS". We can substitute that into a string T with:

char T[] = __TIME__;

Now we can access the characters of the hours, minutes, and seconds as T[0], T[1], T[3], T[4], T[6], and T[7]. The millis() function uses an internal unsigned long variable timer0_millis to store the time. The following routine SetMillis() updates it to the time of day; in other words, the number of milliseconds since midnight:

void SetMillis () {
  char T[] = __TIME__;
  uint8_t oldSREG = SREG;
  cli();
  unsigned long temp = (unsigned long)((T[0]*10+T[1]-528)*60+T[3]*10+T[4]-528)*60
    +T[6]*10+T[7]-528+Adjust;
  timer0_millis = (unsigned long)temp*1000;
  SREG = oldSREG;
}

The routine turns off interrupts before changing timer0_millis to avoid it being changed by the Timer/Counter0 interrupt, and restores them to their previous state using oldSREG. The three occurrences of 528 in the equation convert between the ASCII digits and numbers.

I've included an Adjust constant to allow you to compensate for the delay between compiling the source file and resetting the Arduino. On my system it is about 8 seconds:

const int Adjust = 8;

Finally, here's a simple demonstration of a clock using the time-of-day version of millis():

void setup() {
  SetMillis();
  // initialize serial communication
  Serial.begin(9600);
}

// Add a leading zero when necessary
void Print2 (int n) {
  if (n<10) Serial.print('0');
  Serial.print(n);
}

void loop() {
  // Demonstrate clock
  unsigned long Now = millis()/1000;
  int Seconds = Now%60;
  int Minutes = (Now/60)%60;
  int Hours = (Now/3600)%24;
  Print2(Hours); Serial.print(':'); 
  Print2(Minutes); Serial.print(':'); 
  Print2(Seconds); Serial.println();
  delay(1000);
}

This would be a good addition to my clock projects, such as ATtiny85 Analogue Clock, and Dot Matrix Clock.

Note that the time won't be correct if you simply reset the Arduino, as SetMillis() only transfers the correct time when you compile and upload.

Here's the whole program: Set Millis Program.

Addendum

14th May 2016: Corrected an error that was causing overflow with some times.


blog comments powered by Disqus