#include <Button_SL.hpp>
using ulong = unsigned long int;
//////////////////////////////////////////////////////////////////////////////
/// @brief Helper class for non-blocking execution of
/// code sections at certain intervals.
///
//////////////////////////////////////////////////////////////////////////////
class Interval {
public:
//////////////////////////////////////////////////////////////////////////////
/// @brief Determine whether the specified interval has expired.
///
/// @param duration Interval duration
/// @return true when the interval has elapsed
/// @return false interval not elapsed yet
//////////////////////////////////////////////////////////////////////////////
bool operator()(const ulong duration) {
if (false == isStarted) { return start(false); }
return (millis() - timeStamp >= duration) ? start(true) : false;
}
private:
bool start(bool state = false) {
isStarted = !state; // Set the value to true on the first call
timeStamp = millis();
return state;
}
private:
bool isStarted {false}; // Flag = true if the first Operator() call has been made.
ulong timeStamp {0};
};
// Combines a button object and an additional flag into a data structure.
// The additional flag is required to save an autorelease status after a long button press.
struct ButtonWrapper {
Btn::ButtonSL btn;
bool hasReleased;
};
//////////////////////////////////////////////////
// Global constants and variables
//////////////////////////////////////////////////
#define LOWLEVEL_TRIGGER // Comment out it trigger level is HIGH
#ifdef LOWLEVEL_TRIGGER
enum Trigger : uint8_t {on = LOW, off = HIGH};
#else
enum Trigger : uint8_t {on = HIGH, off = LOW};
#endif
constexpr uint8_t BUTTON_PIN {4};
constexpr uint8_t RELAIS_PIN {5};
constexpr uint8_t SIGNAL_PIN {6};
constexpr ulong INTERVAL_ON_MS {1000};
constexpr ulong INTERVAL_WAIT_MS {5000};
enum class Status : uint8_t { once, onceOn, intervalSignal, interval, intervalOn, intervalWait, off };
ButtonWrapper bwrp {
{BUTTON_PIN, 3000}, // Long Press is >= 3000 ms
false
};
Interval interval;
//////////////////////////////////////////////////
// Functions
//////////////////////////////////////////////////
Status checkButton(ButtonWrapper &fBwrp, Status fState) {
switch (fBwrp.btn.tick()) {
case Btn::ButtonState::shortPressed:
fState = Status::once;
fBwrp.hasReleased = false; // If the autoRelease was triggered by a long button press, this flag is reset here.
break;
case Btn::ButtonState::longPressed:
// If longPressed has been triggered but the button is still not released,
// do not evaluate any more longPressed status so that the
// program continues to run normally.
if (false == fBwrp.hasReleased) {
fState = Status::intervalSignal;
fBwrp.hasReleased = true;
}
break;
default: break;
}
return fState;
}
//////////////////////////////////////////////////
// Main Program
//////////////////////////////////////////////////
void setup() {
pinMode(RELAIS_PIN, OUTPUT);
pinMode(SIGNAL_PIN, OUTPUT);
digitalWrite(RELAIS_PIN,Trigger::off);
bwrp.btn.begin();
bwrp.btn.setDebounceTime_ms(40); // 30 milliseconds are default
bwrp.btn.releaseOn(); // Releases button as soon as the time for a long button press has been reached.
}
void loop() {
static Status state {Status::off};
static bool isIntervalOn {false};
state = checkButton(bwrp, state);
// If interval switching is active, a short press of the button cancels it.
if (true == isIntervalOn && state == Status::once) {
digitalWrite(RELAIS_PIN, Trigger::off);
digitalWrite(SIGNAL_PIN, LOW);
isIntervalOn = false;
interval(0);
state = Status::off;
}
switch (state) {
case Status::once:
digitalWrite(RELAIS_PIN, Trigger::on);
state = Status::onceOn;
[[fallthrough]];
case Status::onceOn:
if (interval(INTERVAL_ON_MS) == true) {
digitalWrite(RELAIS_PIN, Trigger::off);
state = Status::off;
}
break;
case Status::intervalSignal:
digitalWrite(SIGNAL_PIN, HIGH);
state = Status::interval;
[[fallthrough]];
case Status::interval:
digitalWrite(RELAIS_PIN, Trigger::on);
isIntervalOn = true;
state = Status::intervalOn;
[[fallthrough]];
case Status::intervalOn:
if (interval(INTERVAL_ON_MS) == true) {
digitalWrite(RELAIS_PIN, Trigger::off);
state = Status::intervalWait;
}
break;
case Status::intervalWait:
if (interval(INTERVAL_WAIT_MS) == true) { state = Status::interval; }
break;
default: break;
}
}
Kurzer Tastendruck: Relais für 1 Sekunde an.
Langer Tastendruck (3 Sek.): Intervall 1 Sek. an 5 Sek. aus.
Kurzer Tastendruck im Intervall-Modus, schaltet diesen aus.