I am currently on a project requiring the arduino to be able to keep the time (hours and minutes) for about a year. As millis still stop working after about 55 days i was wondering if there was a way how to manually reset the millis, for example every 24 hours? (my programm will do the same thing every day and can just reset in the evening)
If thats not possible, is it possible to restart the arduino with a command? (loosing a couple of seconds per day wouldnt be a big problem)
below is an example sketch that prints the minute and hour. The date rolls over in 2038 but the time will remain correct for as long as the system is powered
#include <DateTime.h>
void setup(){
Serial.begin(19200);
DateTime.sync(0); // sets the time to midnight Jan 1 1970
// you can set a real time by using a Unix type time value
}
void loop(){
unsigned long prevtime;
if(DateTime.available()) { // update clock if time has been synced
prevtime = DateTime.now();
while( prevtime == DateTime.now() ) // wait for the second to rollover
;
DateTime.available(); //refresh the Date and time properties
digitalClockDisplay( ); // update digital clock
}
delay(100);
}
void digitalClockDisplay(){
// digital clock display of current date and time
Serial.print(DateTime.Hour,DEC);
Serial.print(":");
Serial.println(DateTime.Minute,DEC);
}
The playground has some info on how to sync the time to an external source if you need to do that.
Note that the (new) rollover after 55 days is MUCH cleaner than the old 9-hour rollover. In particular, a blink-like program that checks millis() against previous_millis + interval will work fine with the new code, even after more than 55 days, because both calculations wrap at the same time. With the old code, millis() would wrap long before millis()+interval, which was what caused sketches to stop working (mostly.)
Which would return false for the remainder of the operation of the program.
Generally speaking, just extending the rollover period does not change the way in which you deal with rollover, just how often you do... =)
Here's a quick and dirty way:
// rollover! our cunt of running ms is less than
// our previous count!
if( millis() < last_event )
last_event = 0;
// now you can do your check
if( millis() - last_event >= 40 ) {
last_event = millis();
// etc...
}
This would cause one period that may be shorter than 40ms every 55 days, if you want to make sure this one is correct, subtract the last_event value from the absolute maximum (before rollover) value and add that to the amount of time passed.
This works because of the beauty of unsigned arithmetic. Try this little sketch:
void setup()
{
unsigned long max_ul = 0xFFFFFFFFul; // The largest possible 32-bit unsigned long
Serial.begin(9600);
Serial.println(20 - max_ul);
}
void loop(){}
This prints 21. The expression "if (millis() - last_event >= 40)" returns true whenever 40ms have elapsed... forever, whether a rollover occurs or not.
Maybe I'm not understanding something, but Mikal's rollover sketch doesn't work if max_ul is less than the maximum value. How would I rollover my unsigned long counters when millis() rollsover?
void setup()
{
unsigned long max_ul = 0xFFFFFFFFul; // The largest possible 32-bit unsigned long
Serial.begin(9600);
Serial.println(20 - max_ul);
}
void loop(){}
Currently I'm using something like the following to detect when it's time to run my update code
void setup()
{
unsigned long updatecounter = millis();
unsigned long updateperiod = 86400000; //about one day
}
void loop()
{
if (millis() - updatecounter > updateperiod){
update();
updatecounter = millis();
}
//do other stuff here
}
void update()
{
//updating stuff here
}
Is it possible to manually reset the millis() counter?
Matt, my code in #7 was designed merely to demonstrate "rollover subtraction" of unsigned values. It does work for any value of max_ul (greater than 20).
Your code should (almost) work fine for doing something once a day, although you have a scoping problem with your update* variables. Try this:
unsigned long updatecounter = 0;
unsigned long updateperiod = 86400000; //about one day
void setup()
{
}
void loop()
{
if (millis() - updatecounter > updateperiod){
update();
updatecounter = millis();
}
//do other stuff here
}
void update()
{
//updating stuff here
}
My bad, your code is working properly. If I lower max_ul by 1 (0xFFFFFFFEul) the printed value goes up by 1 and so on. So the only limitation then is if my update period is greater than 50 days it will never trigger?
Can you please explain or direct me to an explanation of the "scoping problem" you mentioned?
Quite right. If your period was greater than 50 days, you couldn't directly use this technique because you can't fit that number of milliseconds into a 32-bit unsigned long. But you could work around this difficulty by, say, triggering once a day, and incrementing a "day counter" each time. Then you could perform the update only when the counter is equal to 60 days or 1000 days or whatever.
By "scoping problem" I mean that your declarations of updatecounter and updateperiod were inside setup() ("inside the scope" of the function). That means they were not visible from (were "outside the scope of") loop(). The compiler would have given you an "undefined identifier" error, but this is easily fixed by simply making them global.