Go Down

Topic: Updated stopwatch class on the playground. (Read 10420 times) previous topic - next topic

robertwagner

I ran into a problem calculating tenths/hundredths....  There may be a better workaround, but here is what I came up with.
The problem stems from the fact that millis() is an unsigned long - making their range from 0 to 4,294,967,295 (2^32 - 1).   Many of the other variables I was using were declared as integers - This yields a range of -32,768 to 32,767.  When I went to calculate tenths/hundredths, my result went amiss after 32 seconds.  So, here is what I ended up with so far.

Code: [Select]

int fractional;                     // variable used to store fractional part of time
int fractionalSecs;                 // variable used to store fractional part of Seconds
int fractionalMins;                 // variable used to store fractional part of Minutes
int fractionalHours;                 // variable used to store fractional part of Hours
unsigned long elapsedFrames;                  // elapsed frames for stop watch
unsigned long elapsedSeconds;                 // elapsed seconds for stop watch
unsigned long elapsedMinutes;                 // elapsed Minutes for stop watch
unsigned long elapsedHours;
float elapsedHun;                               //used in tenths/hundredths calculation
float elapsedHun2;                            //used in tenths/hundredths calculation
int elapsedHun3;                               //used in tenths/hundredths calculation


void printtime (
      unsigned long elapsedPTime,  // Time to dispaly
      int printnumberofdigits)   // number of digits to display - 8=HH:MM:SS.TT, 6=HH:MM:SS, 4=MM:SS
    {
     elapsedHours = (elapsedPTime / 3600000L);
     elapsedMinutes = (elapsedPTime / 60000L);
             elapsedSeconds = (elapsedPTime / 1000L);              // divide by 1000 to convert to seconds - then cast to an int to print
             elapsedFrames = (elapsedPTime / 10L);            // divide by 100 to convert to 1/100 of a second - then cast to an int to print
             fractional = (int)(elapsedFrames % frameRate);       // use modulo operator to get fractional part of 1 Second
             fractionalSecs = (int)(elapsedSeconds % 60L);        // use modulo operator to get fractional part of 60 Seconds
             fractionalMins = (int)(elapsedMinutes % 60L);        // use modulo operator to get fractional part of 60 Minutes
             fractionalHours = (int)(elapsedHours % 60L);        // use modulo operator to get fractional part of 60 Minutes     
     elapsedHun = (elapsedPTime - (elapsedSeconds*1000) ) ;   //remove everything but last 3 digits (fractions of a second)
     elapsedHun2 = elapsedHun/10;                                    //you will have millis with a decimal    (we only use last two digits)
     elapsedHun3 = int(elapsedHun2 + 0.5);                         // round this number up or down
//clearLCD();                                         // clear the LDC

if (printnumberofdigits > 4){    //print hours if needed
    if (fractionalHours > 1){
Serial.print(elapsedHours);
Serial.print(":");
}
}


//always display minutes if > 59 seconds
if (elapsedSeconds > 59){
if (fractionalHours > 1){
  if (fractionalMins < 10){                            // pad in leading zeros
         Serial.print("0");                                 // add a zero
      }
}

    Serial.print(fractionalMins);       // convert the int to a string and print a fractional part of 60 Minutes to the LCD
      Serial.print(":");                                 //print a colon.
}

//always display seconds
if (fractionalSecs < 10){                            // pad in leading zeros
      Serial.print("0");                                 // add a zero
      }
     Serial.print(fractionalSecs);

if (printnumberofdigits > 6){   //Print Tenths + hundredths
           // convert the int to a string and print a fractional part of 60 Seconds to the LCD
      Serial.print(".");                                    //print a colon.

  if (fractional < 10){                                // pad in leading zeros
      Serial.print("0");                                 // add a zero
      }     
Serial.print(elapsedHun3);          // convert the int to a string and print a fractional part of 60 Seconds to the LCD
   // Serial.write(158);         //debug
// Serial.print(elapsedHun2);         //debug
}

//assemble seven segment numbers
//  elapsedHours +  fractionalMins + fractionalSecs + elapsedHun3

if (out7 = true){
// take elapsedHun3 and send to tens/hundredths seven segment
if (printnumberofdigits > 6){
      sevensegout(elapsedHun3,fractionalSecs, fractionalMins);       //dump tenths + hundredths to the third shift register
}
else {
         sevensegout(-1,fractionalSecs, fractionalMins);            //just print minutes + seconds
}
if (elapsedSeconds > 0){
// sevensegout(fractionalSecs,dataPins);    //dump seconds to the second shift register
}
if (elapsedSeconds > 59){
// sevensegout(fractionalMins,dataPinm);    //dump minutes to the first shift register
}
}

else {
         sevensegout(-1,fractionalSecs, fractionalMins);
}
out7 = false;   //reset outputpin
//selectLineTwo();
// Serial.print(round(elapsedHun2));
//  selectLineThree();
// Serial.print((elapsedHun2));
    //Serial.write(188);
    //Serial.print(elapsedPTime);
  } //end printtime




robtillaart

refactored, give it a try
Code: [Select]

void printTime(uint32_t t, int digits)
{
  uint32_t x;
  if (digits == 8 || digits == 6) 
  {
  // HOURS
  x = t/ 3600000UL;
  t-= x * 3600000UL;
  print2digits(x);
  Serial.print(':');
  }

  // MINUTES
  x = t/ 60000UL;
  t -= x * 60000UL;
  print2digits(x);
  Serial.print(':');

  // SECONDS
  x = t/1000UL;
  t -= x * 1000UL;
  print2digits(x);

  if (digits == 8)
  {
  // HUNDREDS
  Serial.print('.');
  x = (t+5) /10L;  // rounded hundreds
  print2digits(x);
  }
}

// helper
void print2digits(uint32_t x)
{
  if (x < 10) Serial.print('0');
  Serial.print(x);
}
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

adderall00codine

Great thread the library was really easy to use and I have it running now on my Arduino uni with an OLED display. I am making a quarter mile timer and want to display it as 11.59 say if it took 11.59 seconds, at the moment it is showing in milliseconds, how can i change this? thanks

robtillaart

by posting your code
Then we can see how it works now, and how to change it
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

adderall00codine

Don't worry it turned out to be a simple fix I just had a to manipulate the numbers a little to get it in the right form, thanks for the reply though and its working great now :)

HazardsMind

#35
Nov 19, 2014, 10:42 pm Last Edit: Nov 19, 2014, 10:42 pm by HazardsMind
Robert what is the significance to using enumerators for variables such as,
enum State { RESET, RUNNING, STOPPED };

enum Resolution { MILLIS, MICROS, SECONDS };


as opposed to
#define RESET 0
#define RUNNING 1
#define STOPPED 2
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

pYro_65

#36
Nov 20, 2014, 02:50 am Last Edit: Nov 20, 2014, 02:51 am by pYro_65
It allows minimal type safety. Notice the enum names are used to store the value as well as use it:
Code: [Select]
private:
    enum State _state;
    enum Resolution _res;


Code: [Select]
StopWatch::StopWatch(enum Resolution res)
{
    _res = res;
    switch(_res) {
        case MICROS:


Using an enum type allows you to prevent out of range data as you need to use the named constants in the enum:

Code: [Select]
_res = 5; //Error

But it doesn't stop you writing bad code:
Code: [Select]
_res = (Resolution) 5; //Compiles fine, but out of range.

Enums implcitly convert to an int:
Code: [Select]
int val = _res;

C++11 fixes this with enum classes:
Code: [Select]
enum class Resolution { MILLIS, MICROS, SECONDS };

Elements need to be accessed explicitly to assign:
Code: [Select]
_res = Resolution::SECONDS;

Doing a bad cast still works:
Code: [Select]
_res = (Resolution) 5; //Compiles fine, but out of range.

However the enum does not implicitly cast to an int, so testing will only be done on real values ( _Res == Resolution::MILLIS ). A switch can be used to detect out of range values.
Code: [Select]
    switch(_res) {
        case Resolution::MICROS:     //IN RANGE
        case Resolution::MILLIS:      //IN RANGE
        case Resolution::SECONDS:  //IN RANGE
        default:                             //OUT OF RANGE


And enum classes can be sized to reduce or enlarge their width.
Code: [Select]
enum class Resolution : char { MILLIS, MICROS, SECONDS };

Lol, this is going to look awful for you guys! The preview is about 2 pages long... sorry.

HazardsMind

#37
Nov 20, 2014, 05:54 pm Last Edit: Nov 20, 2014, 06:42 pm by HazardsMind
Is it possible to combine both #define and Enum? Meaning for people who are not familiar with enumerators, such as myself, is it still ok to use a #define to substitute Resolution::MICROS ?



Code: [Select]
#define Millis Resolution::MILLIS
#define Micros Resolution::MICROS
#define Seconds Resolution::SECONDS

switch(_res) {
        case Micros:     //IN RANGE
        case Millis:      //IN RANGE
        case Seconds:  //IN RANGE
        default:

This way it is easy to write and it allows the user to have minimal type safety.
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

Go Up