
With the guidance/correction of @gfvalvo and @Delta_G I finally got a working version, including constructor delegation! Thank you both. I didn't even know that delegation was a thing. It still needs some cleaning up but it is functional so I'll take that.
I got over the delegation hurdle with this tutorial. There are other examples on the webs but, this site, for me, breaks things down pretty well. It turns out it does matter which constructor is delegator and which is delegatee.
With delegation one can now instantiate timers which take different numbers of initializers and not duplicate code. Also, UPDATE_ALL can be called directly as a Multi_timer function - not attached to an object. I don't know if it's stylistically kosher but it does work.
The current working version:
.ino
// demo program for forum thread:
// https://forum.arduino.cc/t/renamed-iterating-through-a-series-of-objects-original-title-oop-this-pointer-questions/1244434
//
// demonstrates two OnDelay timers and one Flasher timer
// all updating from one update call.
#include "Multi_timer.h"
// These timers debounce the switches
OnDelay_tmr buttonDelayA(1000);
OnDelay_tmr buttonDelayB(1000);
// demonstrate a flasher timer
Flasher_tmr buttonFlasher(2000, 300); // 300ms on, 1700ms off
const byte buttonA = 3;
const byte buttonB = 4;
const byte buttonFlash = A3;
const byte ledA = 6;
const byte ledB = 7;
const byte ledFlash = 9;
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(ledFlash, OUTPUT);
Serial.println("started");
}
void loop() {
// buttonDelayB.UPDATE_ALL(); // Updates all timers
Multi_timer::UPDATE_ALL();
buttonDelayA.setEnable((digitalRead(buttonA) ? false : true));
digitalWrite(ledA, (buttonDelayA.isRunning() ? HIGH : LOW));
buttonDelayB.setEnable((digitalRead(buttonB) ? false : true));
digitalWrite(ledB, (buttonDelayB.isRunning() ? HIGH : LOW));
buttonFlasher.setEnable((digitalRead(buttonFlash) ? false : true));
digitalWrite(ledFlash, (buttonFlasher.isFlashing() ? HIGH : LOW));
} // end of loop()
.cpp
// filename: Multi_Timer.cpp 6/18/19
// this version has minimal set/get methods for demonstration
#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::first = nullptr; // Initialize pointer variable 'first'
// One-argument constructor delegates to the two-argument constructor
Multi_timer::Multi_timer(unsigned long pre)
: Multi_timer(pre, 0) {
}
// two-argument constructor for flasher type
// one-argument constructors call this as a delegate
Multi_timer::Multi_timer(unsigned long pre, unsigned long onTime)
{
_Preset = pre;
_OnTime = onTime;
_Control = false;
//
// setup pointers to list of objects to enable one call to
// an 'updateall' function rather than each timer making a
// separate call to 'update'
//
Multi_timer *next = nullptr;
if (first == nullptr) {
first = this;
} else {
// Start at the first pointer and walk the list until we
// find one that doesn't have a next
Multi_timer *ptr = first;
while (ptr->next != nullptr) {
ptr = ptr->next;
}
ptr->next = this;
}
}
void Multi_timer::setEnable(bool en) {
_Enable = en;
}
bool Multi_timer::isRunning() {
return _TimerRunning;
}
// 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
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Adding the update all feature to update all existing
// timers by one call from user.
//
void Multi_timer::UPDATE_ALL() {
for (Multi_timer *ptr = first; ptr != nullptr; ptr = ptr->next) {
ptr->update();
}
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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
.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 {
private:
public:
static Multi_timer *first; // set location of first base class
Multi_timer *next;// = nullptr;
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;
int getCount();
protected:
virtual bool reset() = 0;
public:
// Function to operate timers created under Multi_timer
boolean update();
static void UPDATE_ALL();
private:
//static int counter;
// 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