Resetting Millis() to zero, reset clock

redtails:
Whenever you want to reset millis():

timer0_millis = 0;

I just want to also point out, in case you ignore my earlier advice, that this is flawed because you haven't "protected" that line from interrupts.

Scroll down to "critical sections". If you must do the above, you have to turn interrupts off:

noInterrupts ()
timer0_millis = 0;
interrupts ();

Without doing that, at the moment you attempt to set the timer to zero, an interrupt may occur which means it is possibly only half set to zero.

AWOL:
Apologies - maybe it was taught as "modulo arithmetic" in English-speaking institutions in the 1970s.
That's my recollection anyway.

Yes you are right, that is what I was taught and what I did teach.

AWOL:
Apologies - maybe it was taught as "modulo arithmetic" in English-speaking institutions in the 1970s.
That's my recollection anyway.

None needed. Probably just another of those differences between English and Usanian :wink:

Thanks Nick!

Karma++

AWOL:
Thanks Nick!

Karma++

Same for me :slight_smile:
Jantje

Aw sweet, it all makes sense now. I executed the following code

unsigned long var;
unsigned long varminus;

void setup(void) 
{
  // initialize serial:
  Serial.begin(9600);
  var = 100;
  varminus = var-200;
  
  Serial.println("The answer to 100 minus 200 on unsigned-long vars is: ");
  Serial.println(varminus);
}

void loop(void) 
{ 
}

And the output is:

The answer to 100 minus 200 on unsigned-long vars is: 
4294967196

The odometer analogy is the perfect one for a commoner to get around obscure binary/hex-thinking.

In any case, many thanks for forum members that stuck around, I've edited OP to accommodate future reference

While a staunch defender of the "pure" English tongue, on cogitation, I do believe the term "modular arithmetic" is in fact quite correct and indeed, more accurately descriptive than "modulo arithmetic".

The modulus is a parameter of a numeric value determined by a specific operation and the act of performing that operation is described as "modulo" or a modulo function. So the arithmetic of performing further operations - notably comparisons - on such results, the moduli as a plural, can appropriately be described as "modular" arithmetic where "modular" derives from the "moduli" in the same way we refer to "alveolar" where the (lung) alveoli are concerned, and a few other Latin terms - though I cannot quote the original Latin rules behind that (I never formally studied Latin).

There could be claimed to be the occasional word ending in o - such as "medico" whose apparent derived adjective ("medical") ends in "al" but I suspect this is spurious and results from the colloquial addition of "o" to a word (such as "nymph") whose derived adjective did end in "al".

My point - for what it is worth :sleeping: - is that "modulo" is the name of a process the result of which is the modulus and the arithmetic relating to these is concerned more with what you can do with the moduli, than how you obtain them.

redtails:
In any case, many thanks for forum members that stuck around, I've edited OP to accommodate future reference

Thank you.

Take a look at Gamon's explanation of the millis() issue, and what is often done wrong that causes errors.

It's "Gammon" (with two "m"s.).

Gamon was a guy in Orgrimmar in World of Warcraft, who was constantly being killed.

Hi,
I have solved this problem in this way:

unsigned long check_millis = 0;
long interval = 1000;
unsigned long previousMillis = 0;

void setup(){
}
void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis >= check_millis) check_millis = currentMillis;
  else {
    check_millis = 0;
    previousMillis = 0;
  }

  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;   
    //do somethings
  }
}

The solution that I used to avoid the rollover issue was to make my own replacement for millis() that returns a "unsigned long long int". It calls millis() to get the current time and remembers the last value that it got from millis(), so if the current time is less than the previous time, it adds 0x0100000000UL to the previous time and then subtracts the current time (this getting the elapsed time) and then adds the elapsed time to the "unsigned long long int" variable that it uses to keep track of the current time. There is still a possibility of a rollover, but it is pushed so far into the future that I don't consider it an issue. The rollover would occur around the 584.9 million year mark. The only issue with this method is that if successive calls to this routine do not happen at least once every millis() rollover interval, it could miss a rollover and the time it keeps track of might not be accurate. Since I use this routine for scheduling automatic actions in a state machine instead of using delay() calls, the routine is called every time the loop() routine is called. As such, I don't foresee there will be a case where the code is not called for a long enough period of time that the millis() rollover would be an issue.

NavyVet1959:
The solution that I used to avoid the rollover issue was to make my own replacement for millis() that returns a "unsigned long long int".

That sounds awfully complicated when the rollover problem can be avoided simply by using subtraction as in

if (millis() - previousTime >= desiredInterval) {

...R

NavyVet1959:
The solution that I used to avoid the rollover issue was to make my own replacement for millis() that returns a "unsigned long long int".

A truly terrible idea for the AVR processor.

Robin2:
That sounds awfully complicated when the rollover problem can be avoided simply by using subtraction as in

if (millis() - previousTime >= desiredInterval) {

...R

I suspect that it's more complicated describing it than actually doing it. :slight_smile:

My system consists of basically a scheduler and events that can occur. There might be an event for turning on a particular pin in the future and another one for turning it off at some point later. The event handler might generate future events in the handling of the current event that it is processing. It is possible that it might be necessary to schedule an event that will happen after multiple millis() rollovers. Each of the events in the event queue have a 64-bit time value associated with them to tell the event handler when it needs to process that event.

Something along the line of this:

typedef unsigned long long int TIME64;

TIME64  GetTime( void )
{
    static TIME64  curTime64 = 0;
    TIME64         curMillis;
    static TIME64  prevMillis = 0;

    curMillis = millis();
    if (curMillis < prevMillis) {
        /* curMillis < prevMillis, adjusting... */
        curTime64 += 0x0100000000UL + curMillis - prevMillis;
    } else {
        curTime64 += curMillis - prevMillis;
    }
    prevMillis = curMillis;

    return (curTime64);
}

Manipulating 64-bit integers is inefficient with these processors, but for what I use it for, it's fast enough. The events that are being scheduled are a few seconds apart at the closest and often hours or days apart -- some might even be months apart. Having the capability to schedule events that are across multiple millis() rollover intervals was more important to me than a few extra processor cycles being spend on 64-bit arithmetic. I will admit though that having a 584.9M year rollover is a bit more than I needed. :slight_smile:

Just did a quick timing test, printing out the elapsed time between successive calls to GetTime() in my code and it works out to be 4ms, with the occasional 5ms time. Trying the same thing with a loop of calling millis() and displaying the elapsed time using "unsigned long int" values results in mostly 3ms with the occasional 4ms value. It's possible that the Serial.println() call is taking up a good portion of that.

So, I decided to try another test without the console output... I used a loop of 10,000 where millis() was called, stored as the current time, calculate the elapsed time, and then store the current time as the previous time. This results in 15ms for the 10,000 iterations of the loop.

Changing this to use the 64-bit unsigned integer in my GetTime() routine results in 187ms for the 10,000 iterations of the loop. So, it most definitely takes a lot longer to do 64-bit math. For some systems, this might be an unacceptable time penalty. For other systems though, this might be perfectly acceptable.

        typedef unsigned long long int  TIME64;

        TIME64         ct;                          /* current time */
        TIME64         pt = 0;                      /* previous time */
        TIME64         et;                          /* elapsed time */
        unsigned long  startTime;
        unsigned long  endTime;
        int            i;

        Serial.println("Testing millis() loop...");
        startTime = millis();
        for (i = 0; i < 10000; i++) {
            ct = GetTime();
            et = ct - pt;
            // Serial.println(et);
            pt = ct;
        }
        endTime = millis();
        Serial.print("startTime=");
        Serial.print(startTime);
        Serial.print(", endTime=");
        Serial.print(endTime);
        Serial.print(", elapsedTime=");
        Serial.println(endTime-startTime);

NavyVet1959:
The event handler might generate future events in the handling of the current event that it is processing. It is possible that it might be necessary to schedule an event that will happen after multiple millis() rollovers.

That sounds like a role for a Real Time Clock

Alternatively why not use millis() to update a 1-second counter in an unsigned-long variable. That should see you out.

...R

NavyVet1959:
...fast enough...

"Fast enough" is secondary to the failure to produce a viable image because of the oh-so-fun inability to find a spill register.

I haven't had the pleasure to have that error yet, but I've done a lot of 'C' programming in non-memory-protected architectures over the years and I've had my share of fun with strange errors that were hard to trace down. Ones where you put a printf statement in the code to see the value of some variable at some point and when you add the code, the program either starts working correctly or the error gets moved elsewhere. Of course, you know you are overwriting your code somewhere, but trying to find it can be a pain. :slight_smile:

Is there something about the "long long" data types that might cause this sort of error to occur more often?

NavyVet1959:
Is there something about the "long long" data types that might cause this sort of error to occur more often?

Datatype size, the amount of data that can be stored in processor registers, and a likely compiler limitation that prevents a stack frame from being created when too much data is being squeezed into too small of a register space.