Renamed 'iterating through a series of objects' - original title: OOP, this pointer questions

I can't crack this. I tried just the code not wrapped in a function and compiler did not like that. Within a function it compiles but, with no way to call the function I can't see the object count. Added to the header file:

class Multi_timer {

private:
public:
  static Multi_timer *first;  // set location of first base class
  Multi_timer *next = nullptr;
  static int objCount;

  // if the first pointer is null then this is the first instance in the list

  void maintainList() {
    if (first == nullptr) {
      first = this;
      ++objCount;
    } else {  // additional instantiations fall here
      // 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;
        ++objCount;
      }
      ptr->next = this;
    }
    Serial.print("instantiated objects - ");
    Serial.println(objCount);
  }

I added some prints just to see how many objects were created but nothing displays. I read this as the function isn't being called. I tried several ways of calling this function but got only error messages. The rest of the code works - the timers operate as before.

Since there are two constructors I'm back to this

I'm having trouble resolving these two things.

Thanks for your patience.

p.s. the count is only added as a diagnostic - to be removed once it's working

This is still niggling at me. I have two base class constructors, one takes one argument, the other takes two arguments. Then all the derived class constructors.

I hope I can get there before next Tuesday!

Still blocked.  Referring to the .cpp file below, if the list maintenance code (line 24-42, Arduino IDE) is in there I get this error:

C:\Users\User\AppData\Local\Temp\ccUpfd1h.ltrans0.ltrans.o: In function `__base_ctor ':
C:\Users\User\OneDrive\Documents\Arduino\multi_timer_update_loop_added\Multi_timer_V2_delegate_constructor/Multi_timer.cpp:17: undefined reference to `vtable for Multi_timer'
C:\Users\User\OneDrive\Documents\Arduino\multi_timer_update_loop_added\Multi_timer_V2_delegate_constructor/Multi_timer.cpp:17: undefined reference to `vtable for Multi_timer'
collect2.exe: error: ld returned 1 exit status

exit status 1

Short version - : undefined reference to `vtable for Multi_timer'

I tried to read up on that error but it's all pretty obscure.

If these lines are commented out a clean compile results.  The delegate call in the two-argument constructor is commented out as are all lines with 'flash' in the .ino file.  The idea being to test just the list code with no distractions.

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

  //
  // setup pointers to list of objects
  //
    static Multi_timer *first;
    Multi_timer *next = nullptr;
    static int counter;

    if (first == nullptr) {
      first = this;
      ++counter;
    } 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;
      }

      // Serial.println("blank");
      ptr->next = this;
      ++counter;
   }
}

// two-argument constructor for flasher type

// one-argument constructor called as delegate
Multi_timer::Multi_timer(unsigned long pre, unsigned long onTime)
/*: Multi_timer(pre), _OnTime(onTime)*/
{
  _Preset = pre;
  _OnTime = onTime;
  _Control = false;
}

//  ===== Access functions for timer status/controls

int Multi_timer::getCount() {
  return;
}

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

That's 300+ lines of distraction (for just the .cpp file). Any chance you can pare it down to a small MRE that throws the same compiler error?

Also, once the code is sufficiently small, you can put class declaration, class definition, and application code in a single file for simple viewing, editing, and compiling ... just like I did for the first code I provided in Post #25.

I pared down the .cpp and .ino files per @gfvalvo before I saw @Delta_G's response.  Adding an assignment to reset() in the .h file seems to have cured the vtable problem.  I saw the suggestion pointing to virtual files (linked in post #71) but, I hadn't knowingly added any and I never suspected that the previously trouble-free reset() was the culprit.  Thank you @Delta_G !

Note to self: Listen to the compiler!!

Here are the reduced files, which compile cleanly ( :man_dancing:yay!):

.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 a minimal two timers

#include "Multi_Timer.h"

// These timers debounce the switches

OnDelay_tmr buttonDelayA(1000);
OnDelay_tmr buttonDelayB(1000);

const byte buttonA = 3;
const byte buttonB = 4;
const byte ledA = 6;
const byte ledB = 7;

void setup() {
  Serial.begin(115200);
  pinMode(buttonA, INPUT_PULLUP);
  pinMode(buttonB, INPUT_PULLUP);

  // All LEDs are common cathode

  pinMode(ledA, OUTPUT);
  pinMode(ledB, OUTPUT);
  Serial.println("started");
}

void loop() {

  buttonDelayA.update();
  buttonDelayB.update();

  buttonDelayA.setEnable(!digitalRead(buttonA));
  digitalWrite(ledA, buttonDelayA.isRunning());

  buttonDelayB.setEnable(!digitalRead(buttonB));
  digitalWrite(ledB, buttonDelayB.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;

  //
  // setup pointers to list of objects
  //
  static Multi_timer *first;
  Multi_timer *next = nullptr;
  static int counter;

  if (first == nullptr) {
    first = this;
    ++counter;
  } 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;
    }

    // Serial.println("blank");
    ptr->next = this;
    ++counter;
  }
}

// two-argument constructor for flasher type

// one-argument constructor called as delegate
Multi_timer::Multi_timer(unsigned long pre, unsigned long onTime)
/*: Multi_timer(pre), _OnTime(onTime)*/
{
  _Preset = pre;
  _OnTime = onTime;
  _Control = false;
}

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

/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   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

I still have a separate xxx.update() for each timer and the timers work as desired - one-second flash on LED.

edit: In this version the delegate call is still disabled

Three days and still no joy.  I couldn't work out an object to invoke UPDATE_ALL (named as such to avoid case confusion) so I'm calling it from one of the timer objects.

buttonDelayB.UPDATE_ALL();

Here are the three current sketch files -

.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 a minimal two timers
// still has separate update() for each timer

#include "Multi_Timer.h"

// These timers debounce the switches

OnDelay_tmr buttonDelayA(1000);
OnDelay_tmr buttonDelayB(1000);
//UPDATE_ALL updateAllTimers();

const byte buttonA = 3;
const byte buttonB = 4;
const byte ledA = 6;
const byte ledB = 7;


void setup() {
  Serial.begin(115200);
  pinMode(buttonA, INPUT_PULLUP);
  pinMode(buttonB, INPUT_PULLUP);

  // All LEDs are common cathode

  pinMode(ledA, OUTPUT);
  pinMode(ledB, OUTPUT);
  Serial.println("started");
}

void loop() {

  //buttonDelayA.update();
  // buttonDelayB.update();
  buttonDelayB.UPDATE_ALL();

  buttonDelayA.setEnable(!digitalRead(buttonA));
  digitalWrite(ledA, buttonDelayA.isRunning());

  buttonDelayB.setEnable(!digitalRead(buttonB));
  digitalWrite(ledB, buttonDelayB.isRunning());

}  // end of loop()

.cpp

// filename: Multi_Timer.cpp  6/18/19
// this version compiles,

#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;
  //
  // setup pointers to list of objects to enable one call to
  // an 'updateall' function rather than each timer making a
  // separate call to 'update'
  //
  static Multi_timer *first;
  Multi_timer *next = nullptr;
  static int counter;
  if (first == nullptr) {
    first = this;
    ++counter;
  } 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;
    }
    // Serial.println("blank");
    ptr->next = this;
    ++counter;
  }
}

// two-argument constructor for flasher type
// one-argument constructor called as delegate to incorporate
// the list pointer setup code
Multi_timer::Multi_timer(unsigned long pre, unsigned long onTime)
/*: Multi_timer(pre), _OnTime(onTime)*/  // temporarily disabled
{
  _Preset = pre;
  _OnTime = onTime;
  _Control = false;
}

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;
  //Serial.println("update called");
  return  _Done;  // exit to caller

}  // end update function
   //
   // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   // // Adding the update all feature
   // //
static void Multi_timer::UPDATE_ALL() {
  // UPDATE_ALL defined
  Serial.print("running UPDATE_ALL\t");
 // Serial.println(count1++);
  // Start at first pointer and walk the list until you get to the end.
  //Multi_timer *first;  invokes 'undefined reference to Multi_timer::first if uncommented
  for (Multi_timer *ptr = first; ptr != nullptr; ptr = ptr->next) {
    ptr->update();
    Serial.println("for loop");
  }

  //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

.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


error messages, abbreviated

In function UPDATE_ALL': Multi_timer.cpp:130: undefined reference to Multi_timer::first'
collect2.exe: error: ld returned 1 exit status
exit status 1

I placed print statements in UPDATE_ALL and update but only UPDATE_ALL gets printed.

Any advice?

And, you need to remove it as a local static variable in this constructor:

Multi_timer::Multi_timer(unsigned long pre) {
  _Preset = pre;
  _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'
  //
  static Multi_timer *first;
1 Like

This line now appears in the .cpp file at line number 27. Check me on this - this line creates a pointer called first with scope of Multi_timer and assigns it the value of nullptr.

The offending line no longer appears in the constructor.

Current code:

.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 a minimal two timers
// still has separate update() for each timer

#include "Multi_Timer.h"

// These timers debounce the switches

OnDelay_tmr buttonDelayA(1000);
OnDelay_tmr buttonDelayB(1000);
//UPDATE_ALL updateAllTimers();

const byte buttonA = 3;
const byte buttonB = 4;
const byte ledA = 6;
const byte ledB = 7;


void setup() {
  Serial.begin(115200);
  pinMode(buttonA, INPUT_PULLUP);
  pinMode(buttonB, INPUT_PULLUP);

  // All LEDs are common cathode

  pinMode(ledA, OUTPUT);
  pinMode(ledB, OUTPUT);
  Serial.println("started");
}

void loop() {

  //buttonDelayA.update();
  // buttonDelayB.update();
  buttonDelayB.UPDATE_ALL();

  buttonDelayA.setEnable(!digitalRead(buttonA));
  digitalWrite(ledA, buttonDelayA.isRunning());

  buttonDelayB.setEnable(!digitalRead(buttonB));
  digitalWrite(ledB, buttonDelayB.isRunning());

}  // end of loop()

.cpp

// filename: Multi_Timer.cpp  6/18/19
// this version compiles,

#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;
  //
  // 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 *Multi_Timer::first = nullptr; // corrected per post #76
  Multi_timer *next = nullptr;
  static int counter;
  if (first == nullptr) {
    first = this;
    ++counter;
  } 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;
    }
    // Serial.println("blank");
    ptr->next = this;
    ++counter;
  }
}

// two-argument constructor for flasher type
// one-argument constructor called as delegate to incorporate
// the list pointer setup code
Multi_timer::Multi_timer(unsigned long pre, unsigned long onTime)
/*: Multi_timer(pre), _OnTime(onTime)*/  // temporarily disabled
{
  _Preset = pre;
  _OnTime = onTime;
  _Control = false;
}

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;
  //Serial.println("update called");
  return _Done;  // exit to caller

}  // end update function
   //
   // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   // // Adding the update all feature
   // //
static void Multi_timer::UPDATE_ALL() {
  // UPDATE_ALL defined
  Serial.print("running UPDATE_ALL\t");
  // Serial.println(count1++);
  // Start at first pointer and walk the list until you get to the end.
  //Multi_timer *first;  invokes 'undefined reference to Multi_timer::first if uncommented
  for (Multi_timer *ptr = first; ptr != nullptr; ptr = ptr->next) {
    ptr->update();
    Serial.println("for loop");
  }

  //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

.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

Current errors:

.cpp:27:16: error: 'Multi_Timer' has not been declared
static Multi_timer *first;

cpp:27:35: error: qualified-id in declaration before '=' token
static Multi_timer *first;

cpp:28:16: warning: unused variable 'next' [-Wunused-variable]
Multi_timer *next = nullptr;

I also had an error for 'static linkage' on UPDATE_ALL but I corrected that.

Maybe it's ignorance but, I'm not concerned right now the the unused variable error. I figure that will resolve when the other issues are taken care of.

Thanks for the reply. So like this?

Multi_timer *Multi_Timer::first = nullptr; // corrected per post #76

Multi_timer::Multi_timer(unsigned long pre) {
  _Preset = pre;
  _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;
  static int counter;
  if (first == nullptr) {
    first = this;
    ++counter;
  } 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;
    }
    // Serial.println("blank");
    ptr->next = this;
    ++counter;
  }
}

Yields error:

.cpp:20:14: error: 'Multi_Timer' has not been declared
_Preset = pre;

image

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

2 Likes

Congratulations!

A word of caution... destroying a Multi_timer will leave the heap or stack corrupt (static / global instances in the Arduino world are only destroyed if main returns; which generally never happens).

I don't think this next variable is ever used; can be removed...
Multi_timer *next = nullptr;

Search for that exact string. It's in the Multi_timer::Multi_timer method.

A proper destructor to clean up the links upon deletion should fix that. I provided one in my sample code back in Post #54.

1 Like

Get rid of this, it's serving no purpose. It has nothing to do with any class, it's just a prototype for a free function.

I can relate!

Yes, commenting out those two statements has no effect on the result.

1 Like

Thanks!

From @gfvalvo's post #25


    BaseClass(const BaseClass &other) = delete;
    BaseClass &operator=(const BaseClass &other) = delete;

What are these two lines doing?

Thank you @Delta_G.  Since copying is being prevented I assume copying, in this case, is a bad thing.  Why is that?