Stopwatch with Pause/Lap  6 digit 7 seg LED. HELP!

I added 7 x 100ths to the 'hundredths' each loop. Now it's almost accurate to an hour.. just got to tweak it a little more.

Don't do that! There's a flaw in your Sketch that needs to be fixed. Fixing the flaw will very likely correct the timing error.

Do you want hints so you can try to figure out the problem on your own or do you want a straight-up full description?

I'd like the full-up please, as I have been helping him & I am stumped as to why it would be so inaccurate. I have a 10 minute countdown timer, which I have not timed for accuracy, but works on similar principal (only rolling backwards required a lot more current state checking to decide if the digits should roll or not).

No sweat! Well, I say "no sweat" assuming that the gunk that I try to pass as English prose can be deciphered. :smiley:

Start at the top of loop. The current time is recorded and compared to the previous recorded time checking for a difference of 10 ms...

  currentmillis = millis();  // read the time.
  if (currentmillis - previousmillis >= interval) // 10 milliseconds have gone by
  {

Slowly scroll through loop. You'll notice that there is quite a lot of code. Lots of integer math (which is the correct choice). Lots of if's. Then there are some calls to digitalWrite and shiftOut.

How long do you think all that code takes to run? Let's say it takes 10.001 ms.

We go back around to the top of loop. The current time is recorded and compared to the previous recorded time checking for a difference of 10ms. Because the code in loop took 10.001 ms, the condition is already true and hundredths is incremented. But what happens to the 0.001 ms? Because the "line in the sand" is moved up to the current time...

    previousmillis  = currentmillis;  // save the time for the next comparison

...the 0.001 ms is lost. On a single pass through loop, the 0.001 ms is irrelevant. But, after 1000 passes through loop, the error adds up to a full millisecond. Add enough of those tiny errors and eventually we get to 2 seconds lost out of 5 minutes.

The problem is much easier to visualize if you assume that loop takes 11 milliseconds (or more; like 2 full seconds) to run. In that case, we add one to hundredths but we should have added 1.1.

Does that make sense?


There are at least three solutions to the problem...

The first is to reduce the amount of code in loop. Changing the appropriate variables (like hundredths) to byte will certainly help. But, without very careful analysis and testing we cannot prove that we've solved the problem.

The second solution is to loop while "currentmillis - previousmillis >= interval" is true AND change "previousmillis = currentmillis;" to "previousmillis += interval;". This solution is very likely to fully succeed. If there is still too much work to do, in the worst case, the display will "jump ahead" by one one-hundredth when the error accumulates to a full 10 ms.

The final solution is remove the incrementing code and calculate the various parts each pass through loop. This solution is nice because the display is guarenteed to always be as accurate as possible at the bottom of loop. But, it requires a bunch of fiddly math.

These may help...
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1265097594
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1255643562

Thanks Paul.
I didn't think it was possible for that little bit of code take more than 10mS - I mean that's
.01/(1/16,000,000) which is 160,000 clock cycles - altho I couldn't estiate pi recently :slight_smile: . I'll run the code with an oscilloscope tomorrow and see how long it really is.
(I check every 250ms in my 10:00, 3:00, 1:00 count down timer).

I see your point about only incrementing by 1 hundredth when the elapsed interval may have been 10.001 mS, and then losing that .001.
It seems that perhaps previousMillis should be increased by the amount of (currentMillis-previousMillis) to catch the full amount if time elapsed, vs just interval, which is fixed at 10mS. Unless that is what you are suggesting and I just didn't read it that way. I suppose we could check to see if the difference is more than 2x interval and then throw in the occasional increment by 2 to catch up also.

Thanks also Coding, I think you snuck that in while I was typing my other response and looking up what += did.
Got some testing to try tomorrow ...

Thanks Paul.

Paul? Who's Paul?

I didn't think it was possible for that little bit of code take more than 10mS - I mean that's
.01/(1/16,000,000) which is 160,000 clock cycles

Well, I could easily be wrong. For example: I don't know what shiftOut does or how it's coded. I assumed that it may wait for an acknowledgement.

altho I couldn't estiate pi recently

Ouch!

I'll run the code with an oscilloscope tomorrow and see how long it really is.

Definately a good plan before changing anything.

(I check every 250ms in my 10:00, 3:00, 1:00 count down timer).

Do you have a timer that's showing an error?

I see your point about only incrementing by 1 hundredth when the elapsed interval may have been 10.001 mS, and then losing that .001.
It seems that perhaps previousMillis should be increased by the amount of (currentMillis-previousMillis) to catch the full amount if time elapsed, vs just interval, which is fixed at 10mS.

Work through by hand using a large loop time (like 20 ms). I believe you'll find the error is still there.

I suppose we could check to see if the difference is more than 2x interval and then throw in the occasional increment by 2 to catch up also.

That's essentially how the second suggestion works...

  currentmillis = millis();  // read the time.

  while (currentmillis - previousmillis >= interval)
  {
    // 10 milliseconds (or more) have gone by.

    // Your code to update the counters goes here.

    previousmillis += interval;
  }

"Paul? Who's Paul?"

Sorry about that coding, I had PaulS on my mind, it was 2:00AM, had been reading stuff both of you had posted. Mostly just tired I think.
My pi estimate - I was thinking 22/7, but had 3, 22, 7 going around in my head, somehow got 7/3 = 2.x so it seemed to make sense in distracted way :slight_smile:

I didn't check my timer against anything - it only does minutes, seconds, and I fixed things like when time started it didn't immediately jump down 1 second, but took a second to get there. Or if you stopped time as soon as it changed a second, it didn't immediately drop to the next second when restarted. Those were obvious. I didn't get down into the 10s of mS like this code is doing.

Now, where'd my USB scope end up ...

Hmm, doesn't seem to matter how long I set the interval, with the code as written there is 1mS jitter as to when latchpin goes from High to Low.
The whole time update section & incrementing, rolling over, etc, takes .02mS
The whole shift out sequence only takes 0.8mS.

On to the previousMillis update ...

Okay, I updated the code as follows:

unsigned long currentmillis = 0;
unsigned long previousmillis = 0;
unsigned long interval = 10;
unsigned long elapsedmillis = 0;

byte latchpin = 2; // connect to pin 12 on the 74HC595
byte clockpin = 3; // connect to pin 11 on the 74HC595
byte datapin = 4; // connect to pin 14 on the 74HC595
byte testpin5 = 5;

byte ones_seconds = 0;
byte tens_seconds = 0;
byte ones_minutes = 0;
byte tens_minutes = 0;
byte tenths = 0;
byte hundredths= 0;


int segdisp[10] = {
  63,6,91,79,102,109,125,7,127,111 }; //segment references using 74HC595 Shift Registers
//The above numbers light up different segments of a digit

byte time_update = 0;// added new flag
void setup()
{
  pinMode(latchpin, OUTPUT);
  pinMode(clockpin, OUTPUT);
  pinMode(datapin, OUTPUT);
  pinMode(testpin5, OUTPUT);
  Serial.begin(57600);
}

void loop()
{
  currentmillis = millis();  // read the time.
  elapsedmillis = currentmillis - previousmillis;

  if (elapsedmillis >= interval) // 10 milliseconds have gone by
  {
    previousmillis  = previousmillis + elapsedmillis;  // save the time for the next comparison
    time_update = 1;
//    if (elapsedmillis>10){                       // commented this out to display time below
//      Serial.println (ones_seconds,DEC);
//      Serial.println (elapsedmillis,DEC);
//    }
    // digitalWrite (testpin5, LOW); // used to see how long the nex section took
  }
  // else {digitalWrite (testpin5, HIGH);}  // set flag to upate & shift out

  if (time_update == 1){  // no updating if not at 10ms interval, skip this whole section
    // increment the counters, roll as needed, shift the digits out

    time_update = 0; // reset for next pass thru

    hundredths = hundredths +1;

    if (hundredths == 10){
      hundredths = 0;
      tenths = tenths +1;
    }

    if (tenths == 10){
      tenths = 0;
      ones_seconds = ones_seconds +1;
      Serial.print ( tens_minutes,DEC);  // sending minutes/seconds to the screen
      Serial.print (ones_minutes,DEC);
      Serial.print (tens_seconds,DEC);
      Serial.println(ones_seconds,DEC);
    }

    if (ones_seconds == 10){
      ones_seconds = 0;
      tens_seconds = tens_seconds +1;
    }

    if (tens_seconds == 6){
      tens_seconds = 0;
      ones_minutes = ones_minutes +1;
    }

    if (ones_minutes == 10){
      ones_minutes = 0;
      tens_minutes = tens_minutes +1;
    }
    if (tens_minutes == 6){
      tens_minutes = 0;
    }
    // digitalWrite (testpin5, HIGH); // used to see how long the if's were taking
    // counters are all updated now, just do the shiftout one time here:
    digitalWrite(latchpin, LOW); // send the digits down to the shift registers!
    shiftOut(datapin, clockpin, MSBFIRST, segdisp[hundredths]); // print the % first "hundredths" digit
    shiftOut(datapin, clockpin, MSBFIRST, segdisp[tenths]); // print the tens of hundredths digit
    shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_seconds]); // print the % first "seconds" digit
    shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_seconds]); // print the tens of seconds digit
    shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_minutes]); // print the % first "minute" digit
    shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_minutes]); // print the tens of minutes digit
    digitalWrite(latchpin, HIGH);

  } // end if time to be updated

} // end void loop

am seeing that elapsedmillis = 11 four times a second consistently.
Have it running now, comparing time to my laptop clock, see if they stay in sync together.

Hmm, PC clock not good enough - I get the impression that it drifts a little also, as the two of them don't seem to update once a second together, the display lag between them drifts.

I'm running your code above Robert and it seems to be running almost 2 secs slow after 3 minutes.. :-[

I'm running more tests now.

@Warren Reeve: Give this a try...

unsigned long currentmillis = 0;
unsigned long previousmillis = 0;
unsigned long interval = 10;
unsigned long elapsedmillis = 0;

byte latchpin = 2; // connect to pin 12 on the 74HC595
byte clockpin = 3; // connect to pin 11 on the 74HC595
byte datapin = 4; // connect to pin 14 on the 74HC595
byte testpin5 = 5;

byte ones_seconds = 0;
byte tens_seconds = 0;
byte ones_minutes = 0;
byte tens_minutes = 0;
byte tenths = 0;
byte hundredths= 0;


int segdisp[10] = {
  63,6,91,79,102,109,125,7,127,111 }; //segment references using 74HC595 Shift Registers
//The above numbers light up different segments of a digit

void setup()
{
  pinMode(latchpin, OUTPUT);
  pinMode(clockpin, OUTPUT);
  pinMode(datapin, OUTPUT);
  pinMode(testpin5, OUTPUT);
  Serial.begin(57600);
}

void loop()
{
  currentmillis = millis();  // read the time.
  elapsedmillis = currentmillis - previousmillis;

  while (elapsedmillis >= interval) // 10 milliseconds have gone by
  {
    hundredths = hundredths +1;

    if (hundredths == 10){
      hundredths = 0;
      tenths = tenths +1;
    }

    if (tenths == 10){
      tenths = 0;
      ones_seconds = ones_seconds +1;
      Serial.print ( tens_minutes,DEC);  // sending minutes/seconds to the screen
      Serial.print (ones_minutes,DEC);
      Serial.print (tens_seconds,DEC);
      Serial.println(ones_seconds,DEC);
    }

    if (ones_seconds == 10){
      ones_seconds = 0;
      tens_seconds = tens_seconds +1;
    }

    if (tens_seconds == 6){
      tens_seconds = 0;
      ones_minutes = ones_minutes +1;
    }

    if (ones_minutes == 10){
      ones_minutes = 0;
      tens_minutes = tens_minutes +1;
    }
    if (tens_minutes == 6){
      tens_minutes = 0;
    }
    previousmillis  = previousmillis + interval;  // save the time for the next comparison
  }

  // digitalWrite (testpin5, HIGH); // used to see how long the if's were taking
  // counters are all updated now, just do the shiftout one time here:
  digitalWrite(latchpin, LOW); // send the digits down to the shift registers!
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[hundredths]); // print the % first "hundredths" digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[tenths]); // print the tens of hundredths digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_seconds]); // print the % first "seconds" digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_seconds]); // print the tens of seconds digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_minutes]); // print the % first "minute" digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_minutes]); // print the tens of minutes digit
  digitalWrite(latchpin, HIGH);

} // end void loop

Will try it now Codey (thank you).

After more test Robert that was loosing 5 secs after 12 mins..

Hi Codey, it wont start?? I changed all my latch pin numbers etc to match mine but it is just showing 00:00:00?? :-/

Code I am using;

unsigned long currentmillis = 0;
unsigned long previousmillis = 0;
unsigned long interval = 10;
unsigned long elapsedmillis = 0;

byte latchpin = 8; // connect to pin 12 on the 74HC595
byte clockpin = 12; // connect to pin 11 on the 74HC595
byte datapin = 11; // connect to pin 14 on the 74HC595


byte ones_seconds = 0;
byte tens_seconds = 0;
byte ones_minutes = 0;
byte tens_minutes = 0;
byte tenths = 0;
byte hundredths= 0;


int segdisp[10] = {
  63,6,91,79,102,109,125,7,127,111 }; //segment references using 74HC595 Shift Registers
//The above numbers light up different segments of a digit

void setup()
{
  pinMode(latchpin, OUTPUT);
  pinMode(clockpin, OUTPUT);
  pinMode(datapin, OUTPUT);

 
}

void loop()
{
  currentmillis = millis();  // read the time.
  elapsedmillis = currentmillis - previousmillis;

  while (elapsedmillis >= interval) // 10 milliseconds have gone by
  {
    hundredths = hundredths +1;

    if (hundredths == 10){
      hundredths = 0;
      tenths = tenths +1;
    }

    if (tenths == 10){
      tenths = 0;
      ones_seconds = ones_seconds +1;
      
    }

    if (ones_seconds == 10){
      ones_seconds = 0;
      tens_seconds = tens_seconds +1;
    }

    if (tens_seconds == 6){
      tens_seconds = 0;
      ones_minutes = ones_minutes +1;
    }

    if (ones_minutes == 10){
      ones_minutes = 0;
      tens_minutes = tens_minutes +1;
    }
    if (tens_minutes == 6){
      tens_minutes = 0;
    }
    previousmillis  = previousmillis + interval;  // save the time for the next comparison
  }

  // counters are all updated now, just do the shiftout one time here:
  digitalWrite(latchpin, LOW); // send the digits down to the shift registers!
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[hundredths]); // print the % first "hundredths" digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[tenths]); // print the tens of hundredths digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_seconds]); // print the % first "seconds" digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_seconds]); // print the tens of seconds digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_minutes]); // print the % first "minute" digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_minutes]); // print the tens of minutes digit
  digitalWrite(latchpin, HIGH);

} // end void loop

Sorry about that. Give this one a try...

unsigned long currentmillis = 0;
unsigned long previousmillis = 0;
unsigned long interval = 10;

byte latchpin = 8; // connect to pin 12 on the 74HC595
byte clockpin = 12; // connect to pin 11 on the 74HC595
byte datapin = 11; // connect to pin 14 on the 74HC595


byte ones_seconds = 0;
byte tens_seconds = 0;
byte ones_minutes = 0;
byte tens_minutes = 0;
byte tenths = 0;
byte hundredths= 0;


int segdisp[10] = {
  63,6,91,79,102,109,125,7,127,111 }; //segment references using 74HC595 Shift Registers
//The above numbers light up different segments of a digit

void setup()
{
  pinMode(latchpin, OUTPUT);
  pinMode(clockpin, OUTPUT);
  pinMode(datapin, OUTPUT);


}

void loop()
{
  currentmillis = millis();  // read the time.

  while (currentmillis - previousmillis >= interval) // 10 milliseconds have gone by
  {
    hundredths = hundredths +1;

    if (hundredths == 10){
      hundredths = 0;
      tenths = tenths +1;
    }

    if (tenths == 10){
      tenths = 0;
      ones_seconds = ones_seconds +1;

    }

    if (ones_seconds == 10){
      ones_seconds = 0;
      tens_seconds = tens_seconds +1;
    }

    if (tens_seconds == 6){
      tens_seconds = 0;
      ones_minutes = ones_minutes +1;
    }

    if (ones_minutes == 10){
      ones_minutes = 0;
      tens_minutes = tens_minutes +1;
    }
    if (tens_minutes == 6){
      tens_minutes = 0;
    }
    previousmillis  = previousmillis + interval;  // save the time for the next comparison
  }

  // counters are all updated now, just do the shiftout one time here:
  digitalWrite(latchpin, LOW); // send the digits down to the shift registers!
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[hundredths]); // print the % first "hundredths" digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[tenths]); // print the tens of hundredths digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_seconds]); // print the % first "seconds" digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_seconds]); // print the tens of seconds digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[ones_minutes]); // print the % first "minute" digit
  shiftOut(datapin, clockpin, MSBFIRST, segdisp[tens_minutes]); // print the tens of minutes digit
  digitalWrite(latchpin, HIGH);

} // end void loop

Sorry about that. Give this one a try...

Lol.. no worries. Ok, that 1 is running.. I will report back in 10 mins or so and see what it's like.. :slight_smile:

Hi Codey,
After 17 minutes it is just over 3 seconds slow..??

After 25mins it reads = 4.66 secs slow?? Approx

There are two possibilities left...

  1. Whatever you are using for comparison is not accurate. What are you using? Have you tried something else?

  2. The board you are using has a resonator or oscillator instead of a crystal. What board are you using?

Hi Codey, yes I have tried that.. I was originally timing it from my mobile phone but after all the controversy I though it would be a good idea to double/treble check how accurate it was.
I checked my mobile phone against my PC and it was accurate to it and then I checked the both of them against a hand held stopwatch/lap timer.. all 3 ran the same speed. My arduino is approx 4 weeks old and is an Arduino Uno.

Hope that helps. :-/

I believe that the Uno has a resonator and... (4.66 / (25 * 60)) * 100 = 0.31 % ...is about the typical accuracy for a resonator.

But, you should have someone else confirm what I'm saying.

I also suggest you try to get a few other Uno users to confirm what you're seeing. The shiftOut calls can be replaced with Serial.print calls.