Update: Working version at post #82
Update #2: Interesting power cycle anecdote at post #110
Thread contributors: alto777, Delta_G, gfvalvo, Coding_Badly, evanmars, dougp (TO)
I wrote a timer library several years ago. An example sketch and the library code follow.
Even though the library works I have a few questions.
-
Is the virtual function Reset( ) correctly placed?
-
This occured to me while composing this post: Is a virtual function even needed in this library?
-
Can the this pointer be somehow used to update all timers in one fell swoop as opposed to a series of calls to xxx.update()? I’ve read some about this (Interrupt in class, ESP32 - #44 by PieterP) but my grasp of the concept is rather tenuous.
-
In class Flasher_tmr is isFlashing() correctly placed or should it be somewhere else?
Sketch I/O is three tactile switches with INPUT_PULLUP connected to 3, 4, A3. Four common cathode LEDs attached to 6-9. Arduino NANO board.
.cpp and .h files exist as tabs in the IDE.
.ino
// demo program for forum thread:
// https://forum.arduino.cc/t/programming-help-switch-off-relays/1239207
//
// target house switch control:
// A pressed = high house
// B pressed = low house
// A and B pressed = both houses
#include "Multi_Timer.h"
// These timers debounce the switches
OnDelay_tmr buttonDelayA(150);
OnDelay_tmr buttonDelayB(150);
// These timers drive the relays for thrower actuation
OnDelay_tmr LEDTmrA(300);
OnDelay_tmr LEDTmrB(300);
OnDelay_tmr TmrBothPressed(300);
// Timer for example of other type
Flasher_tmr FlashTest(2000, 500); // (preset time, on time)
const byte buttonA = 3;
const byte buttonB = 4;
const byte buttonFlash = A3;
const byte ledA = 6;
const byte ledB = 7;
const byte ledBoth = 8;
const byte ledFlash = 9;
bool bothOn;
enum { idle,
aPressed,
bPressed,
bothPressed } sequenceState;
void setup() {
Serial.begin(115200);
pinMode(buttonA, INPUT_PULLUP);
pinMode(buttonB, INPUT_PULLUP);
pinMode(buttonFlash, INPUT_PULLUP);
// All LEDs are common cathode
pinMode(ledA, OUTPUT);
pinMode(ledB, OUTPUT);
pinMode(ledBoth, OUTPUT);
pinMode(ledFlash, OUTPUT);
Serial.print("started");
}
void loop() {
buttonDelayA.update();
buttonDelayB.update();
LEDTmrA.update();
LEDTmrB.update();
TmrBothPressed.update();
FlashTest.update();
buttonDelayA.setEnable(!digitalRead(buttonA));
buttonDelayB.setEnable(!digitalRead(buttonB));
FlashTest.setEnable(!digitalRead(buttonFlash)); // Start/stop flasher timer
digitalWrite(ledFlash, FlashTest.isFlashing());
if (buttonDelayA.isRunning() and buttonDelayB.isRunning()) {
bothOn = true;
sequenceState = bothPressed;
}
if (!bothOn) {
if (buttonDelayA.isDone()) {
sequenceState = aPressed;
} else if (buttonDelayB.isDone()) {
sequenceState = bPressed;
}
}
// Neither switch pressed
if (buttonDelayA.isEnabled() == false and buttonDelayB.isEnabled() == false) {
sequenceState = idle;
bothOn = false;
}
// Turn on LEDs for various states
LEDTmrA.setEnable(sequenceState == aPressed); // Start/stop timer A
digitalWrite(ledA, LEDTmrA.isRunning());
LEDTmrB.setEnable(sequenceState == bPressed); // Start/stop timer B
digitalWrite(ledB, LEDTmrB.isRunning());
TmrBothPressed.setEnable(bothOn);
digitalWrite(ledBoth, TmrBothPressed.isRunning());
} // end of loop()
.cpp
// filename: Multi_Timer.cpp 6/18/19
#include "Multi_Timer.h"
#include "Arduino.h"
//
/*
Multi_timer is the base timer class.
> Sets done status true upon reaching preset.
> Produces a positive-going one-scan pulse 'os' upon reaching
preset value (done status = true) and another one-scan pulse
when done goes false.
> responds to a reset command by setting accumulated value to
zero and resetting _Done and _TimerRunning.
> This version has preset checking removed.
*/
// one-argument constructor for non-flasher types
Multi_timer::Multi_timer(unsigned long pre) {
_Preset = pre;
_Control = false;
}
// two-argument constructor for flasher type
Multi_timer::Multi_timer(unsigned long pre, unsigned long onTime) {
_Preset = pre;
_OnTime = onTime;
_Control = false;
}
// ===== Access functions for timer status/controls
void Multi_timer::setEnable(bool en) {
_Enable = en;
}
void Multi_timer::setReset(bool res) {
_Reset = res;
}
void Multi_timer::setCtrl(bool ctrl) {
_Control = ctrl;
}
bool Multi_timer::isEnabled() {
return _Enable;
}
bool Multi_timer::isReset() {
return _Reset;
}
bool Multi_timer::isDone() {
return _Done;
}
bool Multi_timer::isRunning() {
return _TimerRunning;
}
bool Multi_timer::isIntv() {
return _TimerRunning;
}
bool Multi_timer::isOSRise() {
return _Done_Rising_OS;
}
bool Multi_timer::isOSFall() {
return _Done_Falling_OS;
}
// The virtual function 'Reset' enables the individual
// functionality of the various timer types.
bool reset(); // virtual function
//========================================================
// The 'update' function is the heart of the thing.
// ----------------------------------------------------
// Updates timer accumulated value & conditions flags '_Done',
// '_Done_Rising_OS', '_Done_Falling_OS' and '_TimerRunning'.
//
// Returns boolean status of _Done. update() must be called
// periodically in loop to update the flags and accumulated
// value.
// ====================================================
bool Multi_timer::update() {
_CurrentMillis = millis(); // Get system clock ticks
if (_Enable or _Control) { // timer is enabled to run
_Accumulator = _Accumulator + _CurrentMillis - _LastMillis;
if (_Accumulator >= _Preset) { // timer done?
_Accumulator = _Preset; // Don't let accumulator run away
_Done = true;
}
}
_LastMillis = _CurrentMillis;
if (reset()) { // Call virtual reset function. Reset timer if
// returns true, based on derived class' criteria.
_Done = false;
_Accumulator = 0;
_Control = false; // ensures reset of latched type
}
/*
----- Generate a positive going one-shot pulse on _Done false-to-true transition
*/
_Done_Rising_OS = (_Done and _Done_Rising_Setup); // timer done OS
_Done_Rising_Setup = !_Done;
/*
---- and another positive going one-shot pulse on _Done true-to-false transition
*/
_Done_Falling_OS = (!_Done and _Done_Falling_Setup); // timer not done OS
_Done_Falling_Setup = _Done;
/*
----- Condition the timer running flag.
*/
if ((_Enable or _Control) and !_Done and !_Reset) {
_TimerRunning = true;
} else _TimerRunning = false;
return _Done; // exit to caller
} // end update function
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Define various types of timers which inherit/derive from Multi_timer.
The timers differ in functionality by their reset methods.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
// On Delay Timer class definition
//--------------------------------------------------------------------
// A standard timer which runs when reset is false and enable is true
// It is reset otherwise.
// * current version consumes 60 bytes RAM
//--------------------------------------------------------------------
OnDelay_tmr::OnDelay_tmr(unsigned long pre)
: Multi_timer(pre){};
// Establish reset conditions for ON delay timer
bool OnDelay_tmr::reset() {
return (_Reset or !_Enable);
} // End of OnDelay timer
//=================================================================
//
// Flasher Timer class definition
//-----------------------------------------------------------------
// This timer runs and resets itself automatically when enabled.
// It is basically an enhanced OnDelay timer. The second constructor
// argument specifies an ON time for a special output (_FlashOut)
// unique to this type. If _Enable goes false the timer is reset
// immediately.
//
// onTime must be some fraction of pre
Flasher_tmr::Flasher_tmr(unsigned long pre, unsigned long onTime)
: Multi_timer(pre, onTime){};
bool Flasher_tmr::reset() {
return (!_Enable or _Done);
}
bool Flasher_tmr::isFlashing() {
// 4/2/24 : Moved _FlashOut code from update() to getFlash()
// so that _FlashOut only applies to Flasher_tmr objects.
//
// Code below will turn on a common cathode LED for _OnTime
// milliseconds when timing cycle starts.
if (_Accumulator <= _OnTime) {
_FlashOut = true;
} else _FlashOut = false;
return (_FlashOut and _Enable);
}
// 11/17/18 : Added method to runtime adjust _OnTime
// 12/18/18 : Added forced reset
void Flasher_tmr::setOnTime(unsigned long newOnTime) {
_OnTime = newOnTime;
_Accumulator = _Preset; // Force a reset when new onTime loaded
}
// End of Flasher timer
//==============================================================
// Retentive timer class definition
//--------------------------------------------------------------
// A timer which accumulates when enabled. Accumulated value is
// retained when enable is false. This timer type is reset only
// by making the reset input true.
// * current version consumes 96 bytes RAM
//--------------------------------------------------------------
Retentive_tmr::Retentive_tmr(unsigned long pre)
: Multi_timer(pre){};
// Establish reset conditions for retentive ON delay timer
bool Retentive_tmr::reset() {
return (_Reset);
} //End of Retentive timer
//==============================================================
// Pulse Generator Timer Definition
//-----------------------------------------------------------
// A timer which runs when enabled and not reset. Resets
// itself upon reaching preset then restarts the timing cycle
// automatically as long as enable is true.
// * current version consumes 60 bytes RAM
//------------------------------------------------------------
PulseGen_tmr::PulseGen_tmr(unsigned long pre)
: Multi_timer(pre){};
// Establish reset conditions for pulse generator timer
bool PulseGen_tmr::reset() {
return (_Reset or _Done_Rising_OS);
} //End of class PulseGen_tmr
//============================================================
// Latched /Retentive/ timer class definition
//--------------------------------------------------------------
// A timer which starts when 'Setlatch' is called if reset is
// false. Once started, timer continues, even if _Enable
// subsequently goes false, until preset is reached. This timer
// type is reset only by making the reset input true.
// * current version consumes 92 bytes RAM
//--------------------------------------------------------------
Latched_tmr::Latched_tmr(unsigned long pre)
: Multi_timer(pre){};
// Caller starts Latched timer with a call to Start with strt
// true. Once started the timer runs independently to preset.
// _Control is used to isolate the latched timer from '_Enable'
// in the base class which would override the latch. Because of
// this, a Latched Timer has no need of _Enable at all.
void Latched_tmr::Start(bool strt) {
if (!strt) return;
_Control = true;
}
// Establish reset conditions for self-latching ON delay timer
bool Latched_tmr::reset() {
return (_Reset and _Done);
} // End of class Latched_tmr
//=================================================================
// Watchdog timer
//---------------------------------------------------------------
// Runs when enable is true. A change of state on the 'control'
// input resets the timer and restarts the timing cycle.
// Continuous cycling of the control input at a rate faster than
// the time delay will cause the done status flag to remain
// low indefinitely.
//----------------------------------------------------------------
WatchDog_tmr::WatchDog_tmr(unsigned long pre)
: Multi_timer(pre){};
bool WatchDog_tmr::reset() {
/*
Generate a positive going one-shot pulse whenever control
input undergoes a state change.
*/
_WD_Rising_OS = (_Control and _WD_Rising_Setup);
_WD_Rising_Setup = !_Control;
_WD_Falling_OS = (!_Control and _WD_Falling_Setup);
_WD_Falling_Setup = _Control;
return (_WD_Falling_OS or _WD_Rising_OS or _Reset);
} // End of WatchDog timer
.h
// filename: Multi_Timer.h 6/18/19
//
// Some basic timers with a (it is hoped) simple interface.
#ifndef MULTI_TIMER_H
#define MULTI_TIMER_H
#include "Arduino.h"
class Multi_timer {
public:
Multi_timer(unsigned long); //constructor declaration
Multi_timer(unsigned long, unsigned long); // flasher constructor
void setEnable(bool);
void setReset(bool);
bool isEnabled();
bool isReset();
void setCtrl(bool);
bool isDone();
bool isRunning();
bool isIntv();
bool isOSRise();
bool isOSFall();
long pre;
protected:
virtual bool reset();
public:
// Function to operate timers created under Multi_timer
boolean update();
// private:
// protected:
public:
unsigned long _Preset;
bool _Reset : 1;
bool _Enable : 1;
bool _Done : 1;
bool _Done_Rising_OS : 1;
bool _Control : 1;
unsigned long _Accumulator;
protected:
//unsigned long _Accumulator;
unsigned long _CurrentMillis;
unsigned long _LastMillis;
unsigned long _OnTime;
bool _TimerRunning : 1;
bool _Done_Falling_OS : 1;
bool _Done_Rising_Setup : 1;
bool _Done_Falling_Setup : 1;
bool _FlashOut;
}; //end of base class Multi_timer declarations
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Define various types of timers which inherit/derive from Multi_timer.
The timers differ in functionality mainly by their reset methods.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
// On Delay Timer class definition
//--------------------------------------------------------------------
// A standard timer which runs when reset is false and enable is true
// It is reset otherwise.
// * current version consumes 60 bytes RAM
//--------------------------------------------------------------------
class OnDelay_tmr : public Multi_timer {
public:
OnDelay_tmr(unsigned long);
virtual bool reset();
}; // End of class OnDelay_tmr
//------------------------------------------------------
//==============================================================
// Retentive timer class definition
//--------------------------------------------------------------
// A timer which accumulates when enabled. Accumulated value is
// retained when enable is false. This timer type is reset only
// by making the reset input true.
// * current version consumes 96 bytes RAM
//--------------------------------------------------------------
class Retentive_tmr : public Multi_timer {
public:
Retentive_tmr(unsigned long);
// Establish reset conditions for retentive ON delay timer
virtual bool reset();
}; // End of class retentive timer
//----------------------------------------------------
//==============================================================
// Pulse Generator Timer Definition
//-----------------------------------------------------------
// A timer which runs when enabled and not reset. Resets
// itself upon reaching preset then restarts the timing cycle
// automatically as long as enable is true.
// * current version consumes 60 bytes RAM
//------------------------------------------------------------
class PulseGen_tmr : public Multi_timer {
public:
PulseGen_tmr(unsigned long);
// Establish reset conditions for pulse generator timer
virtual bool reset();
}; //End of class PulseGen_tmr
//-------------------------------------------
//============================================================
// Latched /Retentive/ timer class definition
//--------------------------------------------------------------
// Caller starts Latched timer with a call to Start with strt
// true. Once started the timer runs independently to preset.
// _Control is used to isolate the latched timer from '_Enable'
// in the base class which would override the latch. Because of
// this, a Latched Timer has no need of _Enable at all.
// * current version consumes 92 bytes RAM
class Latched_tmr : public Multi_timer {
public:
Latched_tmr(unsigned long);
// Caller starts Latched timer here with a pulse signal.
void Start(bool);
// Establish reset conditions for self-latching ON delay timer
virtual bool reset();
};
// End of class Latched_tmr
//-----------------------------------------------------
//=================================================================
// Watchdog timer
//---------------------------------------------------------------
// Runs when enable is true. A change of state on the 'control'
// input resets the timer and restarts the timing cycle.
// Continuous cycling of the control input at a rate faster than
// the time delay will cause the done status flag to remain
// low indefinitely.
//----------------------------------------------------------------
class WatchDog_tmr : public Multi_timer {
public:
WatchDog_tmr(unsigned long);
virtual bool reset();
private:
bool _WD_Rising_OS : 1;
bool _WD_Falling_OS : 1;
bool _WD_Falling_Setup : 1;
bool _WD_Rising_Setup : 1;
};
// End of class WatchDog_tmr
//---------------------------------------------
//
//=================================================================
//
// Flasher Timer class definition
//-----------------------------------------------------------------
// This timer runs and resets itself automatically when enabled.
// It is basically an enhanced OnDelay timer. The second constructor
// argument specifies an ON time for a special output (_FlashOut)
// unique to this type. If _Enable goes false the timer is reset
// immediately.
//
// onTime must be some fraction of pre
class Flasher_tmr : public Multi_timer {
public:
Flasher_tmr(unsigned long, unsigned long);
virtual bool reset();
// 4/2/24 : Moved _FlashOut decision from update() to getFlash()
// so that _FlashOut code only applies to Flasher_tmr objects.
//
bool isFlashing();
// 11/17/18 : Added method to runtime adjust _OnTime
// 12/18/18 : Added forced reset
void setOnTime(unsigned long);
};
// end of class Flasher_tmr
//---------------------------------------------
#endif
Thanks for any help.
Class function reset() renamed per Renamed 'iterating through a series of objects' - original title: OOP, this pointer questions - #7 by dougp