#include <U8g2lib.h> // u8g2
#include <Wire.h> // u8g2
using ulong = unsigned long int;
using uint = unsigned int;
//////////////////////////////////////////////////////////////////////////////
/// @brief Helper class for debouncing input signals (e.g. push-buttons)
///
//////////////////////////////////////////////////////////////////////////////
class Debounce {
public:
Debounce(unsigned int dbt) : debounceTime {dbt} {}
bool operator()(const uint8_t, const uint8_t);
private:
uint debounceTime;
uint8_t prevPinState;
ulong timeStamp {0};
};
bool Debounce::operator()(const uint8_t pin, uint8_t activePinState = LOW) {
bool isDebounceReady = false;
#ifdef __digitalWriteFast_h_
uint8_t pinState = digitalReadFast(pin);
#else
uint8_t pinState = digitalRead(pin);
#endif
if (pinState != prevPinState) { // Restart the debounce timer with every edge change
timeStamp = millis();
prevPinState = pinState;
} else if (pinState == activePinState && (millis() - timeStamp >= debounceTime)) {
isDebounceReady = true; // No more edge changes within the debounce time
}
return isDebounceReady;
}
//
// global constants
constexpr byte REED_PIN {5};
constexpr byte SCREEN_WIDTH {128}; // OLED display width, in pixels
constexpr byte SCREEN_HEIGHT {64}; // OLED display height, in pixels
constexpr byte X_POS {30};
constexpr byte Y_POS {40};
constexpr ulong CLEAR_DELAY_MS {5000};
//
// globale objects/variables
//
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2 {U8G2_R0, U8X8_PIN_NONE};
enum class State : byte { idle, count, clear } state;
Debounce debounce {10}; // Debounce Time 10ms / active low is default
uint lastActionTime {0};
uint lastSecond {0};
char buffer[6] {"00:00"};
//
// Functions
//
void display(byte x, byte y, const char fBuffer[]) {
u8g2.firstPage();
do { u8g2.drawStr(x, y, fBuffer); } while (u8g2.nextPage());
}
void setup() {
pinMode(REED_PIN, INPUT_PULLUP); // Taster, später Reed Kontakt
state = State::idle;
u8g2.begin();
u8g2.setFont(u8g2_font_logisoso22_tn);
display(X_POS, Y_POS, buffer);
}
void loop() {
switch (state) {
case State::idle:
if (true == debounce(REED_PIN)) { // Timer gestartet
lastActionTime = millis();
state = State::count;
}
break;
case State::count: {
uint seconds = (millis() - lastActionTime) / 1000UL;
uint minutes = seconds / 60;
seconds = seconds % 60;
if (lastSecond != seconds) {
lastSecond = seconds;
snprintf(buffer, sizeof(buffer), "%02d:%02d", minutes, seconds);
display(X_POS, Y_POS, buffer);
}
if (HIGH == digitalRead(REED_PIN)) { // Taster losgelassen
lastActionTime = millis();
state = State::clear;
}
} break;
case State::clear: // Anzeige stehen lassen
if (millis() - lastActionTime > CLEAR_DELAY_MS) {
display(X_POS, Y_POS, "00:00");
state = State::idle;
}
break;
default: break;
}
}