[SOLVED] Need a library breakthrough

I’ve been wrestling with this for days. The jumping off point is this thread. I figured the next logical step after getting the class running was to put it into a library.

I’ve read the tutorials. I’ve watched YT videos. I’ve read the technical definitions of .h/.cpp files (got quickly lost in arcana). I’m missing it.

The main file:

#include <Bounce2.h>
#include <PLCtimer.h>

#define BUTTON_PIN 8  // to enable the timer
#define LED_PIN 13
#define RESET_PIN 9

// Instantiate a Bounce object
Bounce debouncer = Bounce();

// create a class called 'PLCtimer'

//---------------
// Make and define timer object(s)
//-----------------
//
// Create timer with a preset value and timer type

PLCtimer activityTimer(2000UL, TON);

void setup() {

  // Configure pushbuttons with an internal pull-up :
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(RESET_PIN, INPUT_PULLUP);

  // After setting up the button, setup the Bounce instance :
  debouncer.attach(BUTTON_PIN);
  debouncer.interval(5); // interval in ms

  //Setup the LED :
  pinMode(LED_PIN, OUTPUT);
  //
  Serial.begin(230400); // open serial monitor comms.
}

void loop() {
  static int count;
  activityTimer.update(); // update timer status
  if (activityTimer.getOSRise()) {
    count++;
    Serial.println(count);
  }
  activityTimer.setEN( digitalRead(BUTTON_PIN));
  activityTimer.setRes( !digitalRead(RESET_PIN));
  if (activityTimer.getDn())
    digitalWrite(LED_PIN, HIGH); // tell when timer is done
  else  digitalWrite(LED_PIN, LOW); // tell when timer is not done

} //end of loop

The .cpp file:

#include "Arduino.h"
#include "PLCtimer.h"

// create a class called 'PLCtimer'

PLCtimer::PLCtimer {

  public:
    
    /*
      ===== Access functions for timer status bits
    */
    void setEN(bool en) {
      _Enable = en;
    }
    byte getEN() {
      return _Enable;
    }
    bool getres() {
      return _Reset;
    }
    void setres(bool res) {
      _Reset = res;
    }
    bool getDn() {
      return _Done;
    }
    bool getTt() {
      return _TimerRunning;
    }
    bool getIntv() {
      return _TimerRunning;
    }
    bool getOSRise() {
      return _OSRise;
    }
    bool getOSFall() {
      return _OSFall;
    }
    unsigned long currentMillis;

    //    Function to operate timers created under PLCtimer
    //    ----------------------------------------
    //    Update timer accumulated value & condition
    //    flag bits 'tt' (timer timing) and 'dn' (done) based
    //    on 'res', 'en', and 'type' variables.
    //    Function returns boolean status of done bit, 'dn'
    //    ===========================

    boolean update() {
      currentMillis = millis();
      if (_Enable) { // timer is enabled to run
        _TimeDelta = currentMillis - _LastMillis;
        _LastMillis = currentMillis;
        _Accumulator = _Accumulator + _TimeDelta;
        if (_Accumulator >= _Preset) { // timer done?
          _Accumulator = _Preset;
          _Done = true;
        }
      }
      else {  // timer not enabled
        _LastMillis = currentMillis;
      }
      /*
        ----- Time to reset?
      */
      if (_Reset == true or (!_Enable and !_Type == RTO)
          or (_Type == OSGEN and _OSRise)) {
        _Done = false;  // show timer as not done
        _Accumulator = 0; // reset accumulated value to zero
      }
      /*
        ----- Generate a positive going one-shot pulse on timer done 0-1 transition
      */
      _OSRise = (_Done and _OSRSetup);
      _OSRSetup = !_Done;
      /*
        ----- and another positive going one-shot pulse on timer done 1-0 transition
      */
      _OSFall = (!_Done and _OSFSetup);
      _OSFSetup = _Done;
      /*
        ----- condition the timer timing bit
      */
      if (_Enable and !_Done and !_Reset) {//experiment to simplify tt logic
        _TimerRunning = true;
      }
      else _TimerRunning = false;
      /*
        if (_Reset) _TimerRunning = false;
        if (!_Enable) _TimerRunning = false;
        if (_Enable and _Done) _TimerRunning = false;
      */
      return _Done;
    }; // end of update Timer operation

  private:
    unsigned long _Preset;
    unsigned long _Accumulator = 0;
    unsigned long _LastMillis = 0;
    byte _Done: 1;
    byte _TimerRunning: 1;
    byte _OSRise: 1;
    byte _OSFall: 1;
    byte _Type: 2; // bit fields for timer status
    byte _OSRSetup: 1;
    bool _OSFSetup: 1;
    bool _Enable: 1;
    byte _Reset: 1;
    unsigned long _TimeDelta;

    /*
       User-added error handler from pert @ Arduino.cc, post #13
       thread http://forum.arduino.cc/index.php?topic=559655.new#new
    */
    void negativeError( void ) __attribute__((error("Negative timer preset not allowed!")));
    void negativePresetCheck(int number) {
      if (number < 0) {
        negativeError();
      }
    }
}; // end of class PLC timer

The .h file:

// header file
#ifndef PLCTIMER_H
#define PLCTIMER_H

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#define TON 0   // accumulates time only when enabled, reset otherwise
    /*             The TON mode will also function as an interval timer
                   by accessing the tt bit instead of the dn bit.
    */
#define RTO 1   // accumulates time when enabled. Value is retained
    /*             when enable is false. */
#define OSGEN 2 // self-resetting one-shot generator - when enabled
    /*             outputs a pulse and resets automatically after  */
    /*             reaching preset

    */ /*
  All types:
  > produce a positive-going one-scan pulse 'os' upon reaching
  preset value.
  > respond to a reset command by setting accumulated value to
  zero and resetting the done and tt bits.
  > set the done bit upon reaching preset.
*/

class PLCtimer {

  public:
    // constructor - permits setting of variables upon instantiation

//    PLCtimer(unsigned long , byte type)
//    {
//      _Preset = pre,  _Type = type;
//      negativePresetCheck(pre); // preset validation
//    };
    // error message
    void negativeError() __attribute__(());
    void negativePresetCheck(int);
    /*
      ===== Access methods for timer status bits
    */
    void setEN(bool );
    bool getEN();
    void setRes(bool);
    bool getDn();
    bool getTt();
    bool getIntv();
    bool getOSRise();
    bool getOSFall();
    bool update();

  private:
    unsigned long _Preset;
    unsigned long _Accumulator;
    unsigned long _LastMillis;
    bool _Type: 1; // bit fields for timer status
    bool _Done;
    bool _Enabled;
    bool _TimerRunning;
    bool _Reset;
    bool _OSRise;
    bool _OSFall;
    bool _OSRSetup;
    bool _OSFSetup;

    unsigned long _TimeDelta;
};
#endif

The error message:

PLCtimer.cpp:6: error: ‘PLCtimer::PLCtimer’ names the constructor, not the type

PLCtimer::PLCtimer {

^

I commented out the preset validation to reduce possible complications. Don’t know if it helped.

Are the #defines in the right place?

What am I doing wrong?

Thanks.

Read this and you’ll be able to fix the problem:
https://www.arduino.cc/en/Hacking/libraryTutorial

dougp:
What am I doing wrong?

See the error message. You try to define everything in a new constructor, instead of in separate class methods.

The tutorial may or may not help you, I'm skeptical.

Try:

PLCtimer::PLCtimer() {

for starters.

Also move all those functions out of the constructor.

pert:
Read this and you’ll be able to fix the problem:
Arduino - LibraryTutorial

DrDiettrich:
See the error message. You try to define everything in a new constructor, instead of in separate class methods.

The tutorial may or may not help you, I’m skeptical.

One of the first places I landed. I’ve studied that a lot and positioned it side-by-side with my code, since it looks pretty bare bones, but I can’t make the translation.

wvmarle:
Try:

PLCtimer::PLCtimer() {

for starters.

Also move all those functions out of the constructor.

With nothing in the parens a new error is generated.:

prototype for ‘PLCtimer::PLCtimer()’ does not match any in class ‘PLCtimer’

#include "Arduino.h"
#include "PLCtimer.h"

// create a class called 'PLCtimer'

PLCtimer::PLCtimer () {
  _Preset = pre;
  _Type = type ;
}
/*
  ===== Access functions for timer status bits
*/
void PLCtimer::setEN(bool en) {
  _Enable = en;
}
bool PLCtimer::getEN() {
  return _Enable;
}

void PLCtimer::setres(bool res) {
  _Reset = res;
}
bool PLCtimer::getDn() {
  return _Done;
}
bool PLCtimer::getTt() {
  return _TimerRunning;
}
bool PLCtimer::getIntv() {
  return _TimerRunning;
}
bool PLCtimer::getOSRise() {
  return _OSRise;
}
bool PLCtimer::getOSFall() {
  return _OSFall;
}
unsigned long currentMillis;

//    Function to operate timers created under PLCtimer
//    ----------------------------------------
//    Update timer accumulated value & condition
//    flag bits 'tt' (timer timing) and 'dn' (done) based
//    on 'res', 'en', and 'type' variables.
//    Function returns boolean status of done bit, 'dn'
//    ===========================

boolean PLCtimer::update() {
  currentMillis = millis();
  if (_Enable) { // timer is enabled to run
    _TimeDelta = currentMillis - _LastMillis;
    _LastMillis = currentMillis;
    _Accumulator = _Accumulator + _TimeDelta;
    if (_Accumulator >= _Preset) { // timer done?
      _Accumulator = _Preset;
      _Done = true;
    }
  }
  else {  // timer not enabled
    _LastMillis = currentMillis;
  }
  /*
    ----- Time to reset?
  */
  if (_Reset == true or (!_Enable and !_Type == RTO)
      or (_Type == OSGEN and _OSRise)) {
    _Done = false;  // show timer as not done
    _Accumulator = 0; // reset accumulated value to zero
  }
  /*
    ----- Generate a positive going one-shot pulse on timer done 0-1 transition
  */
  _OSRise = (_Done and _OSRSetup);
  _OSRSetup = !_Done;
  /*
    ----- and another positive going one-shot pulse on timer done 1-0 transition
  */
  _OSFall = (!_Done and _OSFSetup);
  _OSFSetup = _Done;
  /*
    ----- condition the timer timing bit
  */
  if (_Enable and !_Done and !_Reset) {//experiment to simplify tt logic
    _TimerRunning = true;
  }
  else _TimerRunning = false;
  /*
    if (_Reset) _TimerRunning = false;
    if (!_Enable) _TimerRunning = false;
    if (_Enable and _Done) _TimerRunning = false;
  */
  return _Done;
}; // end of update Timer operation


unsigned long _Preset;
unsigned long _Accumulator = 0;
unsigned long _LastMillis = 0;
byte _Done: 1;
byte _TimerRunning: 1;
byte _OSRise: 1;
byte _OSFall: 1;
byte _Type: 2; // bit fields for timer status
byte _OSRSetup: 1;
bool _OSFSetup: 1;
bool _Enable: 1;
byte _Reset: 1;
unsigned long _TimeDelta;

/*
   User-added error handler from pert @ Arduino.cc, post #13
   thread http://forum.arduino.cc/index.php?topic=559655.new#new
*/
void negativeError( void ) __attribute__((error("Negative timer preset not allowed!")));
void negativePresetCheck(int number) {
  if (number < 0) {
    negativeError();
  }
}
; // end of class PLC timer

prototype for ‘PLCtimer::PLCtimer()’ does not match any in class ‘PLCtimer’

Admittedly, at this point I’m so d**n frustrated with this thing I don’t know if I’m correctly interpreting what you all are giving me.

dougp:
I've studied that a lot

Study it more. You still haven't gotten it.

dougp:
With nothing in the parens a new error is generated.

That means you didn't declare the constructor in the .h file.

Also all those variable declarations belong in the .h, most likely in the private: section. You normally don't have to redeclare them in the .cpp file (that is, until you start to mess around with static functions and interrupts in your class, which adds a whole new layer of complexity).

wvmarle:
That means you didn't declare the constructor in the .h file.

The accompanying header file:

// header file
#ifndef PLCTIMER_H
#define PLCTIMER_H

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#define TON 0   // accumulates time only when enabled, reset otherwise
/*             The TON mode will also function as an interval timer
               by accessing the tt bit instead of the dn bit.
*/
#define RTO 1   // accumulates time when enabled. Value is retained
/*             when enable is false. */
#define OSGEN 2 // self-resetting one-shot generator - when enabled
/*             outputs a pulse and resets automatically after  */
/*             reaching preset

*/ /*
  All types:
  > produce a positive-going one-scan pulse 'os' upon reaching
  preset value.
  > respond to a reset command by setting accumulated value to
  zero and resetting the done and tt bits.
  > set the done bit upon reaching preset.
*/

class PLCtimer {

  public:

    PLCtimer(unsigned long , bool );
    //    {
    //      _Preset = pre,  _Type = type;
    //      negativePresetCheck(pre); // preset validation
    //    };
    // error message
    void negativeError() ;
    void negativePresetCheck(int);
    /*
      ===== Access methods for timer status bits
    */
    void setEN(bool );
    bool getEN();
    void setRes(bool);
    bool getDn();
    bool getTt();
    bool getIntv();
    bool getOSRise();
    bool getOSFall();
    bool update();

  private:
    unsigned long _Preset;
    unsigned long _Accumulator;
    unsigned long _LastMillis;
    unsigned long _TimeDelta;

    bool _Type; // bit fields for timer status
    bool _Done;
    bool _Enabled;
    bool _TimerRunning;
    bool _Reset;
    bool _OSRise;
    bool _OSFall;
    bool _OSRSetup;
    bool _OSFSetup;
};
#endif

dougp:
The accompanying header file:

  public:

PLCtimer(unsigned long , bool );

Which indeed doesn't match the parameter-less function you implement in the .cpp file.

So either remove the parameters here, or add them to the implemented function (and do something useful with them).

@dougp: you really should learn about C++ classes and syntax before you start guessing how something can be made. Programming languages are bad terrain for try-and-error learning.

DrDiettrich:
Programming languages are bad terrain for try-and-error learning.

I know, that way lies madness - hence my examinations of other's work to use as a guide. It should be basically monkey see, monkey do but I'm apparently incapable of even that.

I did successfully put together a class, with help, which is what this library attempt is based on.

So what did you learn already about the syntax of C++ classes?

A class consists of a class declaration, in a header file, and method implementations in a cpp file. Methods are defined like functions, but with :: names. And there are the special constructor and destructor methods, which don’t have a function type.

Methods are defined like functions, but with :: names.

And in particular, the functions for a class are NOT DEFINED INSIDE ANY BRACES, such as you have here:

// create a class called 'PLCtimer'
PLCtimer::PLCtimer {

  public:

    void setEN(bool en) {
      _Enable = en;
    }
    byte getEN() {
      return _Enable;
    }
    bool getres() {
      return _Reset;
    }

That should be something like"

// create a class called 'PLCtimer'

PLCtimer::PLCtimer {
// constructor.  Arduino code shouldn't really have constructors.
}

void PCLtimer::setEN(bool en) {
      _Enable = en;
}

byte PLCtimer::getEN() {
      return _Enable;
}
...

I guess… you could have included code for the functions inside the “class PLCtimer { }” braces (the class definition), but NOT inside the “PLCtimer::PLCtimer { }” braces (which is a function definition)

DrDiettrich:
So what did you learn already about the syntax of C++ classes?

A class consists of a class declaration, in a header file, and method implementations in a cpp file. Methods are defined like functions, but with :: names. And there are the special constructor and destructor methods, which don’t have a function type.

All that. Also, Ralph Bacon makes quite a point of not forgetting the closing semicolon. I think somewhere along the way I erred unknowingly, tried to fix the *apparent *mistake and got lost in the weeds. A big part of not being able to see the trees was of my own making: In order to isolate parts of the code and make small pieces work I created multiple sub-versions - not necessarily a bad thing - which I believe were all using one broken/incorrect header file - definitely a bad thing. I did some housekeeping and deleted *all *other versions and library copies. This cleared things up to the point the code compiled. It didn’t perform correctly but it did compile. I got the initializations straightened out and it now works.

westfw:
And in particular, the functions for a class are NOT DEFINED INSIDE ANY BRACES, such as you have here:

// create a class called 'PLCtimer'

PLCtimer::PLCtimer {

public:

void setEN(bool en) {
      _Enable = en;
    }
    byte getEN() {
      return _Enable;
    }
    bool getres() {
      return _Reset;
    }

The unmatched braces syndrome. :frowning:

westfw:
// constructor. Arduino code shouldn’t really have constructors.

Why is that? I don’t recall an example or video which didn’t include a constructor. It’s my understanding that if the programmer doesn’t explicitly include one the compiler will insert a default constructor.

The current state of affairs:

main file:

#include <Bounce2.h>
#include "PLCtimer.h"

#define BUTTON_PIN 8  // to enable the timer
#define LED_PIN 13
#define RESET_PIN 9

// Instantiate a Bounce object
Bounce debouncer = Bounce();

// create a class called 'PLCtimer'

//---------------
// Make and define timer object(s)
//-----------------
//
// Create timer with a preset value and timer type

PLCtimer activityTimer(2000UL, TON);

void setup() {

  // Configure pushbuttons with an internal pull-up :
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(RESET_PIN, INPUT_PULLUP);

  // After setting up the button, setup the Bounce instance :
  debouncer.attach(BUTTON_PIN);
  debouncer.interval(5); // interval in ms

  //Setup the LED :
  pinMode(LED_PIN, OUTPUT);
  //
  Serial.begin(230400); // open serial monitor comms.
}

void loop() {
  static int count;
  activityTimer.update(); // update timer status
  if (activityTimer.getOSRise()) {
    count++;
    Serial.println(count);
  }
  activityTimer.setEN( digitalRead(BUTTON_PIN));
  activityTimer.setRes( !digitalRead(RESET_PIN));
  if (activityTimer.getDn())
    digitalWrite(LED_PIN, HIGH); // tell when timer is done
  else  digitalWrite(LED_PIN, LOW); // tell when timer is not done

} //end of loop

.cpp file:

#include "PLCtimer.h"
#include "Arduino.h"
// create a class called 'PLCtimer'

PLCtimer::PLCtimer(unsigned long pre, byte type) {
  _Preset = pre, _Type = type;
}
//unsigned long _Preset;
//byte _Type; // bit fields for timer status

/*
   User-added error handler from pert @ Arduino.cc, post #13
   thread http://forum.arduino.cc/index.php?topic=559655.new#new
*/
//void negativeError( void ) __attribute__((error("Negative timer preset not allowed!")));
//void negativePresetCheck(int number) {
//  if (number < 0) {
//    negativeError();
//  }
//}

/*
  ===== Access functions for timer status bits
*/
void PLCtimer::setEN(bool en) {
  _Enable = en;
}
bool PLCtimer::getEN() {
  return _Enable;
}

void PLCtimer::setRes(bool res) {
  _Reset = res;
}
bool PLCtimer::getDn() {
  return _Done;
}
bool PLCtimer::getTt() {
  return _TimerRunning;
}
bool PLCtimer::getIntv() {
  return _TimerRunning;
}
bool PLCtimer::getOSRise() {
  return _OSRise;
}
bool PLCtimer::getOSFall() {
  return _OSFall;
}
unsigned long currentMillis;


//    Function to operate timers created under PLCtimer
//    ----------------------------------------
//    Update timer accumulated value & condition
//    flag bits 'tt' (timer timing) and 'dn' (done) based
//    on 'res', 'en', and 'type' variables.
//    Function returns boolean status of done bit, 'dn'
//    ===========================

boolean PLCtimer::update() {
  currentMillis = millis();
  if (_Enable) { // timer is enabled to run
    _TimeDelta = currentMillis - _LastMillis;
    _LastMillis = currentMillis;
    _Accumulator = _Accumulator + _TimeDelta;
    if (_Accumulator >= _Preset) { // timer done?
      _Accumulator = _Preset;
      _Done = true;
    }
  }
  else {  // timer not enabled
    _LastMillis = currentMillis;
  }
  /*
    ----- Time to reset?
  */
  if (_Reset == true or (!_Enable and !_Type == RTO)
      or (_Type == OSGEN and _OSRise)) {
    _Done = false;  // show timer as not done
    _Accumulator = 0; // reset accumulated value to zero
  }
  /*
    ----- Generate a positive going one-shot pulse on timer done 0-1 transition
  */
  _OSRise = (_Done and _OSRSetup);
  _OSRSetup = !_Done;
  /*
    ----- and another positive going one-shot pulse on timer done 1-0 transition
  */
  _OSFall = (!_Done and _OSFSetup);
  _OSFSetup = _Done;
  /*
    ----- condition the timer timing bit
  */
  if (_Enable and !_Done and !_Reset) {//experiment to simplify tt logic
    _TimerRunning = true;
  }
  else _TimerRunning = false;
  /*
    if (_Reset) _TimerRunning = false;
    if (!_Enable) _TimerRunning = false;
    if (_Enable and _Done) _TimerRunning = false;
  */
  return _Done;
}; // end of update Timer operation

unsigned long _Accumulator = 0;
unsigned long _LastMillis = 0;
byte _Done ;
byte _TimerRunning ;
byte _OSRise ;
byte _OSFall ;
bool _Enable ;
byte _OSRSetup;
byte _OSFSetup;
byte _Reset ;
unsigned long _TimeDelta;

// end of class PLC timer

.h file:

// header file
#ifndef PLCTIMER_H
#define PLCTIMER_H

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#define TON 0   // accumulates time only when enabled, reset otherwise
/*             The TON mode will also function as an interval timer
               by accessing the tt bit instead of the dn bit.
*/
#define RTO 1   // accumulates time when enabled. Value is retained
/*             when enable is false. */
#define OSGEN 2 // self-resetting one-shot generator - when enabled
/*             outputs a pulse and resets automatically after  */
/*             reaching preset

*/ /*
  All types:
  > produce a positive-going one-scan pulse 'os' upon reaching
  preset value.
  > respond to a reset command by setting accumulated value to
  zero and resetting the done and tt bits.
  > set the done bit upon reaching preset.
*/

class PLCtimer {

  public:

    PLCtimer(unsigned long , byte  );
    // error message
    //    void negativeError() ;

    //    void negativePresetCheck(int);
    /*
      ===== Access methods for timer status bits
    */
    void setEN(bool );
    bool getEN();
    void setRes(bool);
    bool getDn();
    bool getTt();
    bool getIntv();
    bool getOSRise();
    bool getOSFall();
    bool update();

  private:
    unsigned long _Preset;
    unsigned long _Accumulator;
    unsigned long _LastMillis;
    unsigned long _TimeDelta;

    byte _Type: 2; // bit fields for timer status
    byte _Done: 1;
    byte _Enable: 1;
    byte _TimerRunning: 1;
    byte _Reset: 1;
    byte _OSRise: 1;
    byte _OSFall: 1;
    byte _OSRSetup: 1;
    byte _OSFSetup: 1;
};
#endif

Next, add back in @pert’s error code.

Thanks for taking time to help!

Here are several errors:

// create a class called 'PLCtimer'
PLCtimer::PLCtimer {
 public:

No class is created here, that's a major difference from the header file.
The "public" were correct in the class declaration (header file), but not here. Include all constructor code after the {, terminate it by }, remove the public and continue with the method implementations.

DrDiettrich:
Here are several errors:

// create a class called 'PLCtimer'

PLCtimer::PLCtimer {
public:



No class is created here, that's a major difference from the header file.
The "public" were correct in the class declaration (header file), but not here. Include all constructor code after the {, terminate it by }, remove the public and continue with the method implementations.

I respectfully submit that the keyword 'public' does not appear in the latest revision of the .cpp file. At least, I cannot find it. The confusing comment is an oversight from an earlier version.

// constructor. Arduino code shouldn't really have constructors.

Why is that?

On Arduino, constructors are called very early in the program, before any microcontroller peripheral initialization is done. So if you succumb to the temptation to do more than initialize variables and allocate memory, Bad Things Might Happen.
So: HardwareSerial, SPI, TwoWire, Stream - no constructors. (I should say - no user-provided constructors; presumably you get a default constructor that will allocate the memory for you.)
The convention is to have a "begin" method instead.

westfw:
So if you succumb to the temptation to do more than initialize variables and allocate memory, Bad Things Might Happen.

Good thing to know. Thanks.

So, since my existing constructor only initializes I would expect no Bad Things await there.

For any who are interested, I’ve successfully added the negative preset check to my library.

main code:

#include <Bounce2.h>
#include "PLCtimer_v2.h"

#define BUTTON_PIN 8  // to enable the timer
#define LED_PIN 13
#define RESET_PIN 9

// Instantiate a Bounce object
Bounce debouncer = Bounce();
//---------------
// Instantiate a timer with a preset value and timer type

PLCtimer activityTimer(-2000UL, OSGEN); // negative preset generates error

void setup() {

  // Configure pushbuttons with an internal pull-up :
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(RESET_PIN, INPUT_PULLUP);

  // After setting up the button, setup the Bounce instance :
  debouncer.attach(BUTTON_PIN);
  debouncer.interval(5); // interval in ms

  //Setup the LED :
  pinMode(LED_PIN, OUTPUT);
  //
  Serial.begin(230400); // open serial monitor comms.
  Serial.print("timer type - ");
  if (activityTimer.getType() == TON) Serial.println("TON");
  if (activityTimer.getType() == RTO) Serial.println("RTO");
  if (activityTimer.getType() == OSGEN) Serial.println("OSGEN");
}

void loop() {
  static int count;
  activityTimer.update(); // update timer status
  if (activityTimer.getOSRise()) {
    count++;
    Serial.println(count);
  }
  activityTimer.setEN( digitalRead(BUTTON_PIN));
  activityTimer.setRes( !digitalRead(RESET_PIN));
  if (activityTimer.getDn())
    digitalWrite(LED_PIN, HIGH); // tell when timer is done
  else  digitalWrite(LED_PIN, LOW); // tell when timer is not done

} //end of loop

.cpp file:

#include "PLCtimer_v2.h"
#include "Arduino.h"

PLCtimer::PLCtimer(unsigned long pre, byte type) {
  _Preset = pre, _Type = type;
  /*
     User-added error handler from pert @ Arduino.cc, post #13
     thread http://forum.arduino.cc/index.php?topic=559655.new#new
     If timer preset resolves to a negative value a compile time error is generated
  */

  negativePresetWarning(pre); // preset validation
}
/*
  ===== Access methods to control/read timer status
*/
void PLCtimer::setEN(bool en) { // start/stop the timer
  _Enabled = en;
}
bool PLCtimer::getEN() {  // is the timer enabled?
  return _Enabled;
}
void PLCtimer::setRes(bool res) { // reset the timer to zero
  _Reset = res;
}
bool PLCtimer::getDn() { // is the timer done?
  return _Done;
}
bool PLCtimer::getTt() {  // is the timer running?
  return _TimerRunning;
}
bool PLCtimer::getIntv() {  // is the timer running?
  return _TimerRunning;
}
bool PLCtimer::getOSRise() {  // has the timer done bit gone true?
  return _OSRise;
}
bool PLCtimer::getOSFall() {  // has the timer done bit gone false?
  return _OSFall;
}
byte PLCtimer::getType() { // diagnostic - return timer type
  return _Type;
}
//    Method to operate timers created under PLCtimer
//    ----------------------------------------
//    Update timer accumulated value & condition
//    flag bools 'tt' (timer timing) and 'dn' (done) based
//    on 'res', 'en', and 'type' variables.
//    Function returns boolean status of done bit, 'dn'
//    ===========================

static unsigned long _currentMillis;

boolean PLCtimer::update() {
  _currentMillis = millis();
  if (_currentMillis != _LastMillis) {  // skip update if millis() not ticked over
    if (_Enabled) { // timer is enabled to run
      _Accumulator +=  _currentMillis - _LastMillis;
      if (_Accumulator >= _Preset) { // timer done?
        _Accumulator = _Preset;
        _Done = true;
      }
    }
  }
  _LastMillis = _currentMillis;
  /*
    -- Generate a one-scan true signal on
    -- timer done false-to-true transition
  */
  _OSRise = (_Done and _OSRSetup);
  _OSRSetup = !_Done;
  /*
    -- and another one-scan true signal on
    -- timer done true-to-false transition
  */
  _OSFall = (!_Done and _OSFSetup);
  _OSFSetup = _Done;

  /*
    -- Time to reset?
  */
  if (_Reset == true or (!_Enabled and !_Type == RTO)
      or (_Type == OSGEN and _Done)) {
    _Done = false;  // show timer as not done
    _Accumulator = 0; // reset accumulated value to zero
  }
  /*
    -- condition the timer timing bit
  */
  if (_Enabled and !_Done and !_Reset) {
    _TimerRunning = true;
  }
  else _TimerRunning = false;

  return _Done;
}; // end of update Timer operation

unsigned long _Accumulator = 0;
unsigned long _LastMillis = 0;
byte _Done ;
byte _TimerRunning ;
byte _OSRise ;
byte _OSFall ;
bool _Enabled ;
byte _OSRSetup;
byte _OSFSetup;
byte _Reset ;

.h file:

// header file
#ifndef PLCTIMER_Hv2
#define PLCTIMER_Hv2

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#define TON 0   // accumulates time only when enabled, reset otherwise.
/*                 The TON mode will also function as an interval timer
                   by accessing the tt bit instead of the dn bit.
*/
#define RTO 1   // accumulates time when enabled. Value is retained
/*                 when enable is false. */
#define OSGEN 2 // self-resetting one-shot generator - when enabled
/*                 outputs a pulse and restarts automatically after  */
/*                 reaching preset.
*/ 
/*
  All types:
  > set the done bit upon reaching preset.
  > produce a positive-going one-scan pulse 'os' upon done bit
  true and another upon done bit false.
  > respond to a reset command by setting accumulated value to
  zero and resetting the done and tt bits.
*/

class PLCtimer {

  public:

    PLCtimer(unsigned long , byte);
    /*
      ===== Access methods for timer status bits
    */
    void setEN(bool );
    bool getEN();
    void setRes(bool);
    bool getDn();
    bool getTt();
    bool getIntv();
    bool getOSRise();
    bool getOSFall();
    bool update();
    byte getType();

    // error stuff
    /*
      User-added error handler functions from pert @ Arduino.cc, post #13
      thread http://forum.arduino.cc/index.php?topic=559655.new#new
      If timer preset resolves to a negative value a compile time error is generated
    */

    void negativeError( void ) __attribute__((error("Negative timer preset not allowed!")));
    void negativePresetWarning(int number) {
      if (number < 0) {
        negativeError();
      }
    }

  private:
    unsigned long _Preset;
    unsigned long _Accumulator;
    unsigned long _LastMillis;

    byte _Type: 2; // bit fields for timer status - can't be a bool
    bool _Done: 1;
    bool _Enabled: 1;
    bool _TimerRunning: 1;
    bool _Reset: 1;
    bool _OSRise: 1;
    bool _OSFall: 1;
    bool _OSRSetup: 1;
    bool _OSFSetup: 1;
};
#endif

I made two observations:

  1. Possible attributes are listed as ‘error’ and ‘warning’. As in
   void negativeError( void ) __attribute__((error("Negative timer preset not allowed!")));

Warning does not work the same way as error. With a negative preset, error gives the desired result:

call to ‘negativeError’ declared with attribute error: Negative timer preset not allowed!

While using warning as the attribute produces the cryptic:

exit status 1
Error compiling for board Arduino/Genuino Uno.

I don’t know enough to say what this means I just thought I’d include it for future reference.

  1. The error code will flag both literal and computed negatives, ie. -1 * 2000. Again, I don’t know if this is significant, just throwin’ it out there.

The big takeaway - if during development multiple versions sprout keep the names of all files involved separate. You can’t just change the name of the .ino and continue to use the same library file names and expect good results.

Thanks again for the help/guidance.

I'd think that this is the wrong way of generating error messages. As is, the error is compiled unconditionally, i.e. the error message occurs regardless of the actual timer value.