Bug in stopwatch example

since i use a small part off the stopwatch example on the arduino site I found out it has a minor bug, with the possibillity of giving a wrong time through the serial port. Since one cannot react on the examples, maybe smart to post it here (maybe they will change it), at least other users might read it. (if it is at the wrong place please move)

The example can be found at:

http://www.arduino.cc/en/Tutorial/Stopwatch


The problem is the way they divide the time into a part before the decimal point (whole seconds) and the fractional part.

Serial.print( (int)(elapsedTime / 1000L) );         // divide by 1000 to convert to seconds - then cast to an int to print
Serial.print(".");                                     // print decimal point
fractional = (int)(elapsedTime % 1000L);               // use modulo operator to get fractional part of time
Serial.println(fractional);                            // print fractional part of time

The problem lies in the fractional part. Since an intiger cannot begin with a zero (this is then ignored) times (millis()) with zero (0) at the first digit of the fractional part (tenths of a second) are wrongly displayed since the 0 is ignored and the other 2 decimals are moved one digit, turning hundreds of a second into tenths and thousands of a second into hundreds.

So 14565 ms is correctly displayed as 14,565 seconds,
but 14065 ms is displayed as 14,65 seconds i.s.o. 14,065 seconds which is halve a second off :o

(and the difference can be max 0,891s)

I only found the problem when displaying the millis() on a 7 segment display and when a zero was displayed at the right of the decimal point, the serial output and the displayed value were not corresponding. So people only using this example through serial port might not find the problem at all (or they read the code better then I do before they use it)

I solved the problem with the following code by adding the zero when the fractional part is smaller than 100, but here are probably more and/or neather solutions to solve it

      Serial.print( (int)(elapsedTime / 1000L) );           // divide by 1000 to convert to seconds - then cast to an int to print
      Serial.print(".");                                    // print decimal point
      fractional = (int)(elapsedTime % 1000L);              // use modulo operator to get fractional part of time
      if (fractional < 100)                                 // if fractional < 100 the 0 is ignored giving a wrong time, so add the zero
      {
      Serial.print("0");                                    // add zero
      Serial.println(fractional);                           // print fractional part of time
      } 
      else
      {
      Serial.println(fractional);                           // print fractional part of time
      }

I know it is only with the serial print function, so when importing the time into another program one will probably use a different method. But i found it worth mentioning since I use the serial print a lot for debugging purposes!

So please take note when using the stopwatch example from the arduino site.

hope it helps some people :smiley:

Good find, timekeeper!

I should point out that your solution still fails, though, when “fractional” is less than 10. For example, 14007 ms would be displayed as 14.07s. Here’s one of many possible fixes:

Serial.print( (int)(elapsedTime / 1000L) );  
Serial.print(".");
fractional = (int)(elapsedTime % 1000L); // use modulo operator to get fractional part of time
if (fractional < 100)        // if fractional < 100 the 0 is ignored giving a wrong time, so add the zero
{
  Serial.print("0"); // add zero
  if (fractional < 10)
    Serial.print("0"); // and another one
}
Serial.println(fractional);  // print fractional part of time

Mikal

;D

you are asolutely right, should have thought about that, but that one just hasn't happened yet :-X

good idea to pull the serial.print out off the else loop... saves me a byte or 2 :P i sometimes overdo my loops a little

Note that I did put this line in the tutorial - but maybe it's not too clear. The idea is that students could fix the tutorial and learn something on the way

     // routine to report elapsed time - this breaks when delays are in single or double digits. Fix this as a coding exercise.

But since mikalhart fixed it so elegantly, I'll deprive all those future generations of students of the work required to fix it, and paste the code in. Please test it and post back here if it doesn't work.

What if remainder is zero? Shouldn't we really display three zero's after decimal point for consistency?

paulb

well, I did read that line but did not think any in the code would be wrong. Then I understand your thinking. But maybe it is not the best place to give exercises on an example site ;) Since starting programmers will copy full examples to see if and how everything works so when their display does not equal their serial print they might search the fault sooner in their wiring or self written lines they added to the code than in the example code.

Off course one should not make it to easy for their students as well :D

The last zero however is not really needed since an intiger can off course be equal to zero so then the last 0 will be displayed and you only have to manually add the 2 zero's before 8-) now you will get one 0 to much, no problem since the value stays the same... but looks not as neat :P

Thanks for changing anyway!