A StopWatch class for Arduino.

Last Modified: November 30, 2013, at 05:02 AM
By: robtillaart
Platform: All

Forum Thread for remarks

Old Forum Thread (read only)

StopWatch library

One of the main applications for the Arduino board is monitoring. And monitoring means measuring. Here I introduce a simple StopWatch class to measure elapsed time.

The StopWatch class is in fact a wrapper around millis() and micros() with a few variables and an internal state. The internal state only holds running or not.

The methods of the class are:

    StopWatch(res);	// constructor, resolution is MICROS, MILLIS
                        // or SECONDS; MILLIS is the default..
    void start();	// start / continue the measurement
    void stop();	// stops the measurement
    long reset();	// resets the measurement
                        // (you can't continue)

    unsigned long value();	// elapsed time since start; 
                        	// depreciated in the future
    unsigned long elapsed();    // elapsed time since start 

    bool isRunning();	// for compatibility with previous version

    enum State state();	// current state of the stopwatch
    enum Resolution resolution();   // chosen time-ticks.

- see .h file for all details.

To start the measurement one calls start() and internally the starttime is stored. The stopwatch is now running. At any time one can fetch the (lap)time by calling elapsed() giving the milliseconds [or micros or seconds] since start(). To stop the StopWatch one calls stop() - obvious - and now a call to value() will give the elapsed time between start() and stop(). Calling start() again and the counting will continue were it left. If one does not want that and want to start over again one needs to call reset() first.

As it is a Class one can also make arrays of stopwatches that measure time in parallel.

Usage

A small sketch shows how it can be used.

#include <StopWatch.h>

StopWatch MySW;
StopWatch SWarray[5];

void setup()
{
  Serial.begin(115200);
  Serial.println("Stopwatch demo");
  Serial.print("Version: ");
  Serial.println(STOPWATCH_LIB_VERSION);

  SWarray[0].start();
}

void loop()
{
  Serial.println(MySW.isRunning());
  delay(100);

  MySW.start();
  Serial.println(MySW.isRunning());  
  Serial.println("START 1");
  for(int i=0; i<5; i++)
  {
    delay(10);
    Serial.println(MySW.elapsed());
  }

  MySW.stop();
  Serial.println(MySW.isRunning());
  Serial.println("STOP");
  for(int i=0; i<5; i++)
  {
    delay(10);
    Serial.println(MySW.elapsed());
  }

  MySW.start();
  Serial.println(MySW.isRunning());    
  Serial.println("START 2");
  for(int i=0; i<5; i++)
  {
    delay(10);
    Serial.println(MySW.elapsed());
  }  
  MySW.reset();
  Serial.println(MySW.isRunning());
  Serial.println("RESET");

  MySW.start();
  Serial.println(MySW.isRunning());
  Serial.println("START 3");
  for(int i=0; i<5; i++)
  {
    delay(10);
    Serial.println(MySW.elapsed());
  }

  switch(MySW.state())
  {
    case StopWatch::RESET: 
      Serial.println("reset");  // e.g. disable stop/reset
      break;
    case StopWatch::RUNNING: 
      Serial.println("running"); // display laptime
      break;
    case StopWatch::STOPPED: 
      Serial.println("stopped"); // display finaltime
      break;
    default: 
      Serial.println("unknown");
      break;
  }

  delay(3000);
  Serial.print(" >>>  laptime loop() : ");
  Serial.println(SWarray[0].elapsed());  
}

There are two global vars, one "standalone" StopWatch and an array of five StopWatches (numbered 0..4 of course). In setup() the serial port is initialized to high speed so the printstatements take not too long. The version number of the library is printed and the first Stopwatch [0] from the array is started to measure the duration of loop(). Note this latter is never stopped. In loop() the standalone Stopwatch is started and stopped and the value displayed to get a feeling how it is used. Also a minimal use of state() is shown

Another sample sketch

#include <StopWatch.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

StopWatch sw_millis;    // MILLIS (default)
StopWatch sw_micros(StopWatch::MICROS);
StopWatch sw_secs(StopWatch::SECONDS);

void setup() {
    lcd.begin(16,2);
    Serial.begin(9600);
    sw_millis.start();
    sw_micros.start();
    sw_secs.start();
}


void loop() {
    Serial.print("sw_millis=");
    Serial.println(sw_millis.elapsed());
    Serial.print("sw_micros=");
    Serial.println(sw_micros.elapsed());
    Serial.print("sw_secs=");
    Serial.println(sw_secs.elapsed());

    lcd.clear();
    lcd.print("s=");
    lcd.print(sw_secs.elapsed());
    lcd.print(" ms=");
    lcd.print(sw_millis.elapsed());
    lcd.setCursor(0, 1);
    lcd.print("us=");
    lcd.print(sw_micros.elapsed());

    delay(1000);
}

Above sketches are just dummy showcases. Far more interesting sketches can be made. e.g. the start and stop can be steered by interrupt or based upon state of digital IO lines.

Notes

The StopWatch Class is based upon millis() [ micros()] so it inherits all of its behavior including overflow after a number of days [minutes in case of MICROS]. Be aware of this if you want to use the class for measurements that take days. For more information see Millis in the reference section.

To use this library, make a folder in your SKETCHBOOKPATH\libaries with the name StopWatch and put the .h and .cpp there. Close all your IDE's and then it is ready to use.

Todo

  • Create a sketch with real switches (START STOP RESET) and an LCD screen
  • Create a sketch using ISR()'s to measure time.
  • Move all code to .h file for inline version (tip coding badly)

History

  • 2011-06-15 added state() and #defines => 0.1.02 => to differentiate between RESET and STOPPED state
  • 2011-07-14 fixed bug in sample code
  • 2012-01-22 Improved version 0.1.03 (thanks to mromani)
             including 1.0 support

Enjoy tinkering,

rob.tillaart@removethisgmail.com

StopWatch.h file

#ifndef StopWatch_h
#define StopWatch_h
// 
//    FILE: StopWatch.h
//  AUTHOR: Rob Tillaart
// PURPOSE: Simple StopWatch library for Arduino
// HISTORY: See StopWatch.cpp
//     URL: http://playground.arduino.cc/Code/StopWatchClass
//
// Released to the public domain
//

#define STOPWATCH_LIB_VERSION "0.1.03"

#if ARDUINO >= 100
    #include "Arduino.h"
#else
    #include "WProgram.h"
#endif

class StopWatch 
{
public:
    enum State { RESET, RUNNING, STOPPED };
    enum Resolution { MILLIS, MICROS, SECONDS };
    StopWatch(enum Resolution res = MILLIS);
    void start();
    void stop(); 
    void reset();
    unsigned long value();
    unsigned long elapsed() { return value(); };
    bool isRunning();
    enum State state();
    enum Resolution resolution() { return _res; };

private:
    enum State _state;
    enum Resolution _res;
    unsigned long _starttime;
    unsigned long _stoptime;
    unsigned long (*_gettime)(void);
    static unsigned long seconds() { return millis()/1000; };
};

#endif
// END OF FILE

StopWatch.cpp

// 
//    FILE: StopWatch.cpp
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.03
// PURPOSE: Simple StopWatch library for Arduino
//
// The library is based upon millis() and therefore
// has the same restrictions as millis() has wrt overflow.
//
// HISTORY: 
// 0.1.00 - 2011-01-04 initial version
// 0.1.01 - 2011-01-04 Added better state
// 0.1.02 - 2011-06-15 Added state() + #defines + lib version
// 0.1.03 - 2012-01-22 Added several improvements
//             By mromani & Rob Tillaart
// 
// Released to the public domain
//

#include "StopWatch.h"

StopWatch::StopWatch(enum Resolution res)
{
    _res = res;
    switch(_res) {
        case MICROS:
            _gettime = micros;
            break;
        case MILLIS:
            _gettime = millis;
            break;
        case SECONDS:
            _gettime = seconds;
            break;
        default:  
            _gettime = millis;
            break;
    }
    reset();
}

void StopWatch::reset()
{
    _state = StopWatch::RESET;
    _starttime = _stoptime = 0;
}

void StopWatch::start()
{
    if (_state == StopWatch::RESET || _state == StopWatch::STOPPED)
    {
        _state = StopWatch::RUNNING;
        unsigned long t = _gettime();
        _starttime += t - _stoptime;
        _stoptime = t;
    }
}

unsigned long StopWatch::value()
{
    if (_state == StopWatch::RUNNING) _stoptime = _gettime();
    return _stoptime - _starttime;
}

void StopWatch::stop()
{
    if (_state == StopWatch::RUNNING)
    {
        _state = StopWatch::STOPPED;
        _stoptime = _gettime();
    }
}

bool StopWatch::isRunning()
{
    return (_state == StopWatch::RUNNING);
}

enum StopWatch::State StopWatch::state()
{
    return _state;
}
// END OF FILE

Share