How to implement millisecond, elapsed time counter that resets?

This does not work; I will study the reference given below. Thank you.

/*******************************************
*
*     Interrupt millis() 
*     for Run_time monitoring
*     Pin D6 connected to output of Opto-isolator.
*
*********************************************/



#define furnace D6

float start, finished, elapsed;

void runtime()
{
   

   if (digitalRead (furnace) == HIGH)  
   {
      delay(1000);
      start = millis();
   }    
   else
   {
      
      elapsed = start - finished;
   
   }  
}  
// end of runtime

void setup ()
{
   Serial.begin(115200);
   pinMode (furnace, INPUT_PULLUP);
   attachInterrupt (digitalPinToInterrupt (furnace), runtime, FALLING);  
  
}  // end of setup

void loop ()
{
  
   yield();
  
   Serial.println(elapsed/60000);
  
}

William

Does not seem to be a way to reset the millis(); I believe this only occurs on board reset or reboot of sketch.

You are correct but there is no need.

Save the value of millis() when the timing period starts and determine that the timing period has elapsed by subtracting the start value from the current value returned by millis() and compare the result to the required period in milliseconds.

When that occurs take the required action(s) and save the value millis() again as the start of the next period.

See Using millis() for timing. A beginners guide, Several things at the same time and look at the BlinkWithoutDelay example in the IDE.

@William,

Depending on the granularity needed, I would suggest doing what the log monitors do. They increment a faster counter, then at a point (before the int/long rolls over in C/C++), it increments a slower counter. So again, depending on your requirements, you could count off seconds until you incremented a minute counter. After 60 of those, the hour counter, and so on.

Also, most here (including me) will try to convince you to not use delays like this. The way you can deal with millis() rolling over and giving a negative time is to use a second variable that occasionally holds millis' value. In pseudo-code:

long timer, secs, mins, hours, days, ...;
setup() {
timer = millis();
}
loop() {
logTime(timer);
<Something that is checking, running or looping>
trigger = true; # condition triggered, like stopping or starting
if (trigger) {
<do something with a state variable, etc.
}
}

void logTime(long t) {
if (millis() - t) >= 1000) {
secs += 1;
secs = 0;
timer = millis();
}
if (secs >= 60) {
mins += 1;
secs = 0;
}
if (mins>= 60) {
hours += 1;
mins = 0;
}
if (hours >= 24) {
days += 1;
hours = 0;
}
if ...
}

And of course, a good RTC and disk storage would be invaluable. RTCs have all the time functions you need, and with a battery can stay pretty accurate even through loss of power. Again, a lot depends on your full list of requirements.

The way you can deal with millis() rolling over and giving a negative time is to use a second variable that occasionally holds millis' value.

Why complicate things ? You can use a simple

if (millis() - startTime >= period)
{
  //code here to be executed at the end of the period
}

For any period up to 49 and a bit days as long as unsigned long variables are used even if millis() rolls over to zero during the period

@William
Congratulations on editing your original post and making a nonsense of the thread

Data logging portion of sketch is already done and uses NTP time syncing. Only issue remaining is the elapsed time counter for runtime. I used a simulation of the runtime to develop the rest of the sketch; which uses functions from my "NTP_Time-synced_Web_Interface.ino" sketch.

Not having a good day; apologies for my poor timing editing. I was not aware a reply had been posted.

William

Techno500:
Data logging portion of sketch is already done and uses NTP time syncing. Only issue remaining is the elapsed time counter for runtime.

If you have access to a real-time clock, then why even bother with millis() ?
Presumably, you know the starting time: you've been logging it, right? And we know that you know what time it is now. So all you need to do to find the elapsed time is...
At this point, this isn't even an Arduino question. It's a common sense question. Let's say you're at a restaurant. You order your food at 11:40, and it doesn't arrive until 12:30. In your review of the restaurant, you complain that you had to wait for... how many minutes?
If you know how to do that calculation, well then, just have the Arduino do the same calculation!

Where I need help is in getting time it took to get my meal on repeat visits to the restaurant.

William

Techno500:
Where I need help is in getting time it took to get my meal on repeat visits to the restaurant.

William

So, is it subtraction you have a problem with? (to get the waiting time for one visit)

Or addition? (to add up the total waiting time for multiple visits)

Or both?

Or are you trying to get an average, or what?

Must be God's will. I was defending Bob's complication comment, but TPTB decided against it. My reply got whacked on "Post", and this is what remained:


Why complicate things ? You can use a simple

if (millis() - startTime >= period)
{
  //code here to be executed at the end of the period
}

P.S. As usual, knowing the full use case & requirements saves everyone trying their best to help a lot of time, and in the process you get an answer you can use faster. Without this, well, this thread happens...

odometer:
At this point, this isn't even an Arduino question. It's a common sense question. Let's say you're at a restaurant. You order your food at 11:40, and it doesn't arrive until 12:30. In your review of the restaurant, you complain that you had to wait for... how many minutes?

Bad example because you likely would complain you waited 2hours and the food was cold and your server was rude and babies were crying the whole time..

Solution; working code verified using circuit of "4N25 OptoIsolator Schematic.jpg" for testing.

/*******************************************

      RunTime.ino
      Will be used for Furnace runtime monitoring
      Pin D6 will connect to output of Opto-isolator.
      
      September 6, 2018   William 

*********************************************/



#define furnace D6

volatile int dayFlag = 0;

unsigned long int start, finished, elapsed;

int value;

void runTime()
{

     if (dayFlag == 0)
     {
          value = digitalRead (furnace);

          if (value == 0)
          {
               Serial.println("Motor ON");
               start = millis();
               dayFlag = 0;
          }

          if (value == 1)
          {
               Serial.print("");
               Serial.println("Motor OFF");
               finished = millis();
               elapsed = finished - start;
               dayFlag = 1;
          }
     }
}

void setup ()
{
     Serial.begin(9600);
     pinMode (furnace, INPUT_PULLUP);

     if (!dayFlag)
     {
          attachInterrupt (digitalPinToInterrupt (furnace), runTime, CHANGE);

     }

}

void loop ()
{
     delay(0);

     if (dayFlag)
     {
          Serial.print("Total elapsed milliseconds:  ");
          Serial.println(elapsed);
          Serial.println("");
          elapsed = 0;
     }
     dayFlag = 0;

}

Circuit used for testing:

William

Is there a particular reason why you are using an interrupt when it is not strictly needed ?

Does the Serial.print() work in the ISR ? It depends on interrupts which are automatically disabled in ISRs

Why are the timing variables declared as float when millis() returns an unsigned long ?

Why the delay()s and shouldn't the value of millis() be saved before the delay()s if you insist on using them ?

@ UKHeliBob Thank you for the questions and encouragement to continue...

I am an enthusiast; not a programmer by profession, learning by examples works best.

William

Is there a particular reason why you are using an interrupt when it is not strictly needed ?

Does the Serial.print() work in the ISR ? It depends on interrupts which are automatically disabled in ISRs

Why do repeatedly attach the interrupt in loop() and not just once in setup(), particularly as you never detach it ?

UKHeliBob:
Is there a particular reason why you are using an interrupt when it is not strictly needed ?

Does the Serial.print() work in the ISR ? It depends on interrupts which are automatically disabled in ISRs

Why do repeatedly attach the interrupt in loop() and not just once in setup(), particularly as you never detach it ?

I believe that is the style of programming they teach at the University of Copy and Paste.

odometer:
I believe that is the style of programming they teach at the University of Copy and Paste.

If you are referring to my post, then it comes from the University of I have asked two of these questions before and got no answer.

UKHeliBob:

odometer:

UKHeliBob:
Is there a particular reason why you are using an interrupt when it is not strictly needed ?

Does the Serial.print() work in the ISR ? It depends on interrupts which are automatically disabled in ISRs

Why do repeatedly attach the interrupt in loop() and not just once in setup(), particularly as you never detach it ?

I believe that is the style of programming they teach at the University of Copy and Paste.

If you are referring to my post, then it comes from the University of I have asked two of these questions before and got no answer.

What I meant was: I believe that the reasom the the OP's (i.e. Techno500's) code looks the way it does is because he learned to program at the University of Copy and Paste.

As for using Serial.print() in an ISR: I've heard that you shouldn't, but why exactly shouldn't you? What are the consequences of using Serial.print() in an ISR? (I don't actually believe you should do it; I'm just playing devil's advocate here.)

Trap #4: Doing a serial print, or delay, inside an interrupt service routine.

See Trap #4

What the heck, read the whole discussion.