Skip to content

FEATURE: Embedded general-purpose stopwatch timer #36

Description

@FabioRoss

What problem are you trying to solve?

DovesLapTimer measures lap and sector times precisely. But there are intervals
the user wants to time that aren't laps or sectors:

  • How long has the driver been in the pit lane? (independent of service sector
    crossing times — the user might want a "pit lane timer" that the mechanic
    can start/stop manually)
  • How long since the session started?
  • Countdown display: "X seconds until mandatory pit window opens"
  • Time a refuelling stop with a button press

All of these need a start/stop/read timer. Users implement this themselves with
unsigned long timerStart = millis() and arithmetic, but it's error-prone and
duplicated across projects. A clean embedded timer with state machine semantics
makes this trivial.

DovesLapTimer already manages multiple time references internally. Adding a
user-accessible one-slot general-purpose timer costs almost nothing.

Proposed solution

Embed a minimal stopwatch timer with a 3-state machine:

struct TimerState {
  unsigned long startTimestamp;
  unsigned long stopTimestamp;
  uint8_t state;  // 0 = idle, 1 = running, 2 = stopped (result available)
};

// Public member — user can start/stop/reset directly
TimerState timer;

ACCESSOR METHOD:

unsigned long getTimerElapsed() const;
// Returns: millis() - startTimestamp  if state == 1 (running)
//          stopTimestamp - startTimestamp  if state == 2 (stopped)
//          0  if state == 0 (idle)

CONTROL METHODS:

void timerStart() { timer.startTimestamp = millis(); timer.state = 1; }
void timerStop()  { timer.stopTimestamp = millis(); timer.state = 2; }
void timerReset() { timer.state = 0; timer.startTimestamp = 0; timer.stopTimestamp = 0; }
bool timerIsRunning() const { return timer.state == 1; }

(Alternatively, expose the TimerState struct as a public member and let the
user manage it directly — simpler API surface if control methods feel heavy.)

Alternatives considered

  1. User manages their own unsigned long timerStart = millis() — current approach.
    Works but: no state machine, no "already stopped" semantics, no convenient
    result-available state.

  2. Use lastServiceSectorTime (Issue FEATURE: Service sector detection (pit lane, refuelling, tyre stops) #30) — tracks service stop duration, but
    only fires on defined crossing lines. Can't be triggered by a button press.

  3. Arduino's built-in millis() — the underlying mechanism, but no wrapper.
    The value of the embedded timer is the state machine semantics, not the math

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions