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;
}