#include <MD_MAX72xx.h> // https://github.com/MajicDesigns/MD_MAX72XX
#include <MD_Parola.h> // https://github.com/MajicDesigns/MD_Parola
#include <Button_SL.hpp> // https://github.com/DoImant/Button_SL
#include <SPI.h>
namespace gc {
constexpr uint8_t clkPin {13};
constexpr uint8_t dataPin {11};
constexpr uint8_t csPin {10};
constexpr uint8_t kickPin {2};
constexpr uint8_t numCounter {4};
constexpr uint8_t maxDevices {5};
constexpr uint32_t kickDelay_ms {3000};
constexpr uint32_t kickDuration_ms {500};
}
class Interval {
public:
bool operator()(const uint32_t duration) {
if (false == isStarted) { return start(false); }
return (millis() - timeStamp >= duration) ? start(true) : false;
}
void reset() { isStarted = 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.
uint32_t timeStamp {0};
};
class BallKicker {
public:
BallKicker(Btn::Button& btn, uint8_t pin, uint32_t kDelay_ms, uint32_t kDuration_ms)
: btn {btn}, pin {pin}, kDelay_ms {kDelay_ms}, kDuration_ms {kDuration_ms} {}
void begin() { pinMode(pin, OUTPUT); }
void check() { isKickerActive ? kicker() : checkPin(); };
private:
void checkPin() {
if (!btn.tick()) {
timer.reset();
} else if (timer(kDelay_ms)) {
digitalWrite(pin, HIGH);
isKickerActive = true;
}
}
void kicker() {
if (timer(kDuration_ms)) {
digitalWrite(pin, LOW);
isKickerActive = false;
}
}
private:
Btn::Button& btn;
uint8_t pin;
uint32_t kDelay_ms;
uint32_t kDuration_ms;
Interval timer;
bool isKickerActive {false};
};
// KLasse zum speichern und addieren der Punktzahl, mit der sie initialisiert wird
class ScoreCounter {
public:
ScoreCounter(uint16_t points, uint32_t sum = 0) : points {points}, sum {sum} {}
uint16_t operator()() { // Addiere pro Kontakt die initialisierte Punktzahl
sum += points;
return points; // Gib initialisierte Punktzahl zurück. Z.b. für eine andere Addition
}
uint32_t getSum() const { return sum; } // Gebe Summe für die Kontakte * initialisierte Punktzahl zurück
private:
const uint16_t points;
uint32_t sum;
};
//
// Abfrage der Kontaktpins. Diese sind Active-Low konfiguriert (INPUT_PULLUP)
// Kann über die Buttonklasse aber auch auf Active High umkonfigurierte werden.
// Rückgabewert ist der Index des Kontaktpins im Array. (-1) = kein Kontakt festgestellt.
//
template <size_t N> int checkSignals(Btn::ButtonSL (&btn)[N]) {
for (size_t i = 0; i < N; ++i) {
if (btn[i].tick() != Btn::ButtonState::notPressed) {
return i;
}
}
return -1;
}
// Array für die Pinabfrage anlegen. Das Lesen der Pins erfolgt entprellt
// Standard Entprellzeit ist 30ms
Btn::ButtonSL signals[gc::numCounter] {{4}, {5}, {6}, {7}};
// Für jede gewünschte Punktzahl (pro Kontakt) ein Objekt anlegen
// Der Array-Reihenfolge (Index) muss mit dem Button Array identisch sein
ScoreCounter score[gc::numCounter] {10, 100, 1000, 10000};
Btn::Button btnKicker {3};
BallKicker bKicker{btnKicker,gc::kickPin,gc::kickDelay_ms,gc::kickDuration_ms};
// Display
MD_Parola mx = MD_Parola(MD_MAX72XX::PAROLA_HW, gc::csPin, gc::maxDevices);
void setup() {
Serial.begin(115200);
// Initialisieren der Buttonobjekte
for (auto& signal : signals) {
signal.begin();
signal.setDebounceTime_ms(15);
}
btnKicker.begin();
bKicker.begin();
mx.begin();
mx.displayText("", PA_RIGHT, 0, 0, PA_PRINT, PA_NO_EFFECT);
mx.print(0);
}
void loop() {
static uint32_t scoreSum {0}; // Speicher für die Punktzahlsumme
int8_t index = checkSignals(signals); // Abfrage der Kontaktpins
switch(index) {
case 0 ... 3:
scoreSum += score[index]();
mx.print(scoreSum); // Kein Test auf Überlauf Stellenzahl!
Serial.print("Score: ");
Serial.println(scoreSum);
break;
default: break;
}
bKicker.check();
}
10er
100er
1000er
10000er
kicker