Start a timer when event occurs, stop it if the event stops before timer ends?

johnwasser:
When I want a 1-shot timer, I use (StartTime != 0) to indicate that the timer is running.

When I want to make timers more manageable, I use this MillisTimer class:

/********************** MillisTimer Class Definition **********************/
constexpr uint8_t MAX_TIMER_INSTANCES = 6;

class MillisTimer {
  using funcPtr = void(*)(void);
  public:
    MillisTimer(uint32_t duration, funcPtr action = nullptr, bool autoRepeat = true) : interval(duration), callback(action), repeat(autoRepeat) {
      instances[numInstances++] = this;
    }
    static void update(void) {
      uint32_t currentMillis = millis();
      for (size_t i = 0; i < numInstances; i++) {
        if (currentMillis - instances[i]->lastMillis >= instances[i]->interval and instances[i]->state == TIMER_RUNNING) {
          instances[i]->lastMillis = currentMillis;
          if (!instances[i]->repeat) {
            instances[i]->state = TIMER_STOPPED;
          }
          if (instances[i]->callback) {
            instances[i]->callback();
          }
        }
      }
    }
    bool isRunning(void) {
      return state == TIMER_RUNNING;
    }
    void start(void) {
      lastMillis = millis();
      state = TIMER_RUNNING;
    }
    void stop(void) {
      state = TIMER_STOPPED;
    }
    void setRepeat(void) {
      repeat = true;
    }
    void setOneShot(void) {
      repeat = false;
    }
    static size_t getTimerCount(void) {
      return numInstances;
    }

  private:
    enum {
      TIMER_STOPPED,
      TIMER_RUNNING,
    }state = TIMER_STOPPED;
    uint32_t interval;
    funcPtr callback;
    bool repeat;
    uint32_t lastMillis;
    static size_t numInstances;
    static MillisTimer* instances[MAX_TIMER_INSTANCES];
};
size_t MillisTimer::numInstances = 0;
MillisTimer* MillisTimer::instances[MAX_TIMER_INSTANCES] = {nullptr};

/******************** END MillisTimer Class Definition ********************/
void toggleLed(void);

// you can use a funtion pointer as done here:
MillisTimer ledTimer(250, toggleLed);  // lack of 3rd argument indicates timer repeats (default)
// or you can just define a lambda like done here
MillisTimer serialTimer(10000, [](){
    Serial.println("Serial Timer Expired"); 
    if (ledTimer.isRunning()) {
      ledTimer.stop();  // this timer will stop the led timer after 60 seconds; comment this line to allow LED to blink indefinately
      digitalWrite(13, LOW);  // sets led to OFF
    } else {
      ledTimer.start();
    }
  }, true);  // true explicitly indicates timer repeats
//  }, false);  // false indicates timer does not repeat

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  Serial.print(F("Timers in use:\t"));
  Serial.println(MillisTimer::getTimerCount());
  ledTimer.start();
  serialTimer.start();
}

void loop() {
  MillisTimer::update();
}

void toggleLed(void) {
  digitalWrite(13, !digitalRead(13));
}

then starting, restarting, and stopping timers becomes trivial.

Put it in a library and it really cleans up your code:

.ino

#include "MillisTimer.h"

void toggleLed(void);

// you can use a funtion pointer as done here:
MillisTimer ledTimer(250, toggleLed);  // lack of 3rd argument indicates timer repeats (default)
// or you can just define a lambda like done here
MillisTimer serialTimer(10000, [](){
    Serial.println("Serial Timer Expired"); 
    if (ledTimer.isRunning()) {
      ledTimer.stop();  // this timer will stop the led timer after 60 seconds; comment this line to allow LED to blink indefinately
      digitalWrite(13, LOW);  // sets led to OFF
    } else {
      ledTimer.start(1000);  // you can set a new interval using start()
    }
  }, true);  // true explicitly indicates timer repeats
//  }, false);  // false indicates timer does not repeat

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  Serial.print(F("Timers in use:\t"));
  Serial.println(MillisTimer::getTimerCount());
  ledTimer.start();
  ledTimer.start();
  serialTimer.start();
}

void loop() {
  MillisTimer::update();
}

void toggleLed(void) {
  digitalWrite(13, !digitalRead(13));
}

header

#ifndef MILLISTIMER_H
#define MILLISTIMER_H

#include "Arduino.h"

constexpr uint8_t MAX_TIMER_INSTANCES = 5;

class MillisTimer {
  using funcPtr = void(*)(void);
  public:
    MillisTimer(uint32_t duration, funcPtr action = nullptr, bool autoRepeat = true);
    static void update(void);
    bool isRunning(void);
    void start(void);
    void start(uint32_t newInterval);
    void stop(void);
    void stopAll(void);
    void setInterval(uint32_t newInterval);
    void setRepeat(void);
    void setOneShot(void);
    static size_t getTimerCount(void);
    
  private:
    enum {
      TIMER_STOPPED,
      TIMER_RUNNING,
    }state = TIMER_STOPPED;
    uint32_t interval;
    funcPtr callback;
    bool repeat;
    uint32_t lastMillis;
    static size_t numInstances;
    static MillisTimer* instances[MAX_TIMER_INSTANCES];
};

#endif

implementation file:

#include "MillisTimer.h"

size_t MillisTimer::numInstances = 0;
MillisTimer* MillisTimer::instances[MAX_TIMER_INSTANCES] = {nullptr};

MillisTimer::MillisTimer(uint32_t duration, funcPtr action, bool autoRepeat) : interval(duration), callback(action), repeat(autoRepeat) {
  instances[numInstances++] = this;
}

void MillisTimer::update(void) {
  uint32_t currentMillis = millis();
  for (size_t i = 0; i < numInstances; i++) {
    if (currentMillis - instances[i]->lastMillis >= instances[i]->interval and instances[i]->state == TIMER_RUNNING) {
      instances[i]->lastMillis = currentMillis;
      if (!instances[i]->repeat) {
        instances[i]->state = TIMER_STOPPED;
      }
      if (instances[i]->callback) {
        instances[i]->callback();
      }
    }
  }
}

bool MillisTimer::isRunning(void) {
  return state == TIMER_RUNNING;
}

void MillisTimer::start(void) {
  lastMillis = millis();
  state = TIMER_RUNNING;
}

void MillisTimer::start(uint32_t newInterval) {
  interval = newInterval;
  lastMillis = millis();
  state = TIMER_RUNNING;
}

void MillisTimer::stop(void) {
  state = TIMER_STOPPED;
}

void MillisTimer::stopAll(void) {
  for (size_t i = 0; i < numInstances; i++) {
    instances[i]->stop();
  }
}

void MillisTimer::setInterval(uint32_t newInterval) {
  interval = newInterval;
}

void MillisTimer::setRepeat(void) {
  repeat = true;
}

void MillisTimer::setOneShot(void) {
  repeat = false;
}

size_t MillisTimer::getTimerCount(void) {
  return numInstances;
}