#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 if 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 uint8_t DENKEN_HILFT_PIN {7};
constexpr ulong INTERVAL_ON_MS {1000};
constexpr ulong INTERVAL_WAIT_MS {5000};
constexpr ulong INTERVAL_BLINK_MS {251};
enum class Status : uint8_t { onceOn, intervalOn, intervalWait, intervalOff, idle };
ButtonWrapper bwrp {
{BUTTON_PIN, 3000}, // Long Press is >= 3000 ms
false
};
Interval interval;
Interval intervalSigBlue;
// Interval intervalSigGreen;
//////////////////////////////////////////////////
// Functions
//////////////////////////////////////////////////
Status checkButton(ButtonWrapper &fBwrp, Status fState) {
switch (fBwrp.btn.tick()) {
case Btn::ButtonState::shortPressed:
switch (fState) {
case Status::intervalOn: [[fallthrough]];
case Status::intervalWait:
// If the autoRelease was triggered by a long button press, this flag is reset here.
fBwrp.hasReleased = false;
fState = Status::intervalOff;
break;
default:
digitalWrite(DENKEN_HILFT_PIN, HIGH);
digitalWrite(RELAIS_PIN, Trigger::on);
fState = Status::onceOn;
break;
}
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) {
digitalWrite(SIGNAL_PIN, HIGH);
digitalWrite(DENKEN_HILFT_PIN, HIGH);
digitalWrite(RELAIS_PIN, Trigger::on);
fBwrp.hasReleased = true;
fState = Status::intervalOn;
}
break;
default: break;
}
return fState;
}
//////////////////////////////////////////////////
// Main Program
//////////////////////////////////////////////////
void setup() {
pinMode(RELAIS_PIN, OUTPUT);
pinMode(SIGNAL_PIN, OUTPUT);
pinMode(DENKEN_HILFT_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::idle};
state = checkButton(bwrp, state);
switch (state) {
case Status::onceOn:
if (interval(INTERVAL_ON_MS) == true) {
digitalWrite(DENKEN_HILFT_PIN, LOW);
digitalWrite(RELAIS_PIN, Trigger::off);
intervalSigBlue(0);
state = Status::idle;
} else if (true == intervalSigBlue(INTERVAL_BLINK_MS)) {
digitalWrite(DENKEN_HILFT_PIN, !digitalRead(DENKEN_HILFT_PIN));
}
break;
case Status::intervalOn:
// do nothing until the ON-Interval ist over. Then switch all off
if (interval(INTERVAL_ON_MS) == true) {
digitalWrite(DENKEN_HILFT_PIN, LOW);
digitalWrite(RELAIS_PIN, Trigger::off);
intervalSigBlue(0);
state = Status::intervalWait;
} else if (true == intervalSigBlue(INTERVAL_BLINK_MS)) {
digitalWrite(DENKEN_HILFT_PIN, !digitalRead(DENKEN_HILFT_PIN));
}
// if (true == intervalSigGreen(INTERVAL_BLINK_MS)) { digitalWrite(SIGNAL_PIN, !digitalRead(SIGNAL_PIN)); }
break;
case Status::intervalWait:
// Wait until the next "intervalOn" phase. Then switch all on.
if (interval(INTERVAL_WAIT_MS) == true) {
digitalWrite(DENKEN_HILFT_PIN, HIGH);
digitalWrite(RELAIS_PIN, Trigger::on);
state = Status::intervalOn;
}
// if (true == intervalSigGreen(INTERVAL_BLINK_MS)) { digitalWrite (SIGNAL_PIN, !digitalRead(SIGNAL_PIN)); }
break;
case Status::intervalOff:
// Interval mode was interrupted by a short press of the button.
// Switch everything off and do nothing more.
digitalWrite(SIGNAL_PIN, LOW);
digitalWrite(DENKEN_HILFT_PIN, LOW);
digitalWrite(RELAIS_PIN, Trigger::off);
interval(0);
intervalSigBlue(0);
// intervalSigGreen(0);
state = Status::idle;
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.