Help with Robot Race Timer (multiplexed 7-segment LEDs)

I'm building a timer for a robotics race.
Start timer when robot breaks laser light beam.
Finish and stop timer when robot breaks the beam the second time.

My timer works off millis(). I do
start = millis()
...then a continuous loop that does
now = millis()
number = now - start
...then I parse number to get seconds, tenths and hundredths.

I'm reading that millis() has known accuracy problems and cannot be used for an accurate clock. I don't need a long term clock since a few minutes constitutes a long race. My question & concern is - - Can I be sure that two robots with an actual time (say of 9.87 seconds) will both show the same time?? If they both finished with a wrong time (say 9.85 seconds) consistantly, I don't care, but if one finish showed 9.85 and the racer with the same showed 9.86 on the timer, then I don't want that.

If this is possible, I think I'd rather do more work to have an accurate, consistant timer.

What would be the best way to do this??

(For this race I can live with a MAX of 9 minutes, 59 seconds, but I have another race where I have much shorter times, so I think four seven-segments will do it for me.)

I've heard that disabling interrupts will improve the stability on the millis but i might be wrong.

If you disable interrupts, millis() won't work. It uses an interrupt approximately every millisecond to add 1 to a counter.

It should be consistent, and fairly accurate, under the situations you describe. The accuracy would depend on the processor's crystal or resonator accuracy, however even if out by a percent or two, it would be repeatable.

I would use an interrupt to start timing (eg. when the beam is broken) and again to stop timing. That way you should be able to get a pretty accurate start and stop time.

I just tested this setup:

Running this sketch:

unsigned long lastTriggerTime;
volatile unsigned long triggerTime;
volatile boolean triggered;

void isr () 
{
  // wait until we noticed last one
  if (triggered)
    return;

  triggerTime = micros ();
  triggered = true;
}  // end of isr

void setup() 
{
  attachInterrupt(0, isr, RISING);   
  Serial.begin (115200);
  Serial.println ("Started timing ...");  
}  // end of setup

void loop() 
{
  if (!triggered)
    return;
  
  unsigned long elapsed = triggerTime - lastTriggerTime;
  
  lastTriggerTime = triggerTime;
  triggered = false;  // re-arm for next time
  
  Serial.print ("Took: ");
  Serial.print (elapsed);
  Serial.print (" microseconds. ");
  
  unsigned long minutes, seconds, ms;
  
  minutes = elapsed / (1000000L * 60);
  elapsed -= minutes * (1000000L * 60);
  
  seconds = elapsed / 1000000L;
  elapsed -= seconds * 1000000L;
  
  ms = elapsed / 1000;
  elapsed -= ms * 1000;
  
  Serial.print (minutes);
  Serial.print ("m ");
  Serial.print (seconds);
  Serial.print ("s ");
  Serial.print (ms);
  Serial.println ("ms.");
}  // end of loop

Output:

Started timing ...
Took: 3375112 microseconds. 0m 3s 375ms.
Took: 1577852 microseconds. 0m 1s 577ms.
Took: 1267868 microseconds. 0m 1s 267ms.
Took: 1293936 microseconds. 0m 1s 293ms.
Took: 617352 microseconds. 0m 0s 617ms.
Took: 2013680 microseconds. 0m 2s 13ms.
Took: 756448 microseconds. 0m 0s 756ms.
Took: 3785196 microseconds. 0m 3s 785ms.
Took: 12562240 microseconds. 0m 12s 562ms.

I shone a cheap 1W laser pointer onto the LDR. The resistance is low with the light on it (I read about 1.9V on pin D2) which registers as a LOW input. When you break the beam the resistance is high and the pin gets about 3V (being a HIGH). Thus the beam is broken on a RISING interrupt.

The code times the intervals between rising interrupts and reports them to the serial monitor. Of course you could use a LCD or something. You might want to omit very short intervals (eg. less than a second) because that might be "bounce" for where the light passes through a hole in the robot.

eg. After:

 unsigned long elapsed = triggerTime - lastTriggerTime;

Add:

  if (elapsed < 1000000L)
    {
    triggered = false;
    return;  // ignore if less than a second
    }

This should give time to the nearest microsecond, more or less.

You might want to play with the resistor value to make it more reliable (although it was reliable enough in my test).

Also to confirm it works, you could make up two of them (it took me about 10 minutes to assemble). Then check they get the same result for a particular race.

In fact if you had two working for a real race that would give some confidence to people that it is fair.

Another fun thing to do would be to get a thermal printer like this:

It looks complicated to use, but really you just open a serial port and send text.

So you could use that to spit out a "race result ticket".