/*
Titel: Downhill Stopwatch
Author: Gregor Mausser
*/
//Bibliotheken
//Display
#include <Wire.h> //I2C Kommunikation
#include <Adafruit_GFX.h> //Grafikbilbliothek
#include <Adafruit_SSD1306.h> //OLED-Display Treiber
//Pins definieren
const int ButtonPin = 1; //Start/Stop Button
const int ButtonResetPin = 10; //Reset Button (mit Interrupt)
const int BuzzerPin = 2; //Aktiver Buzzer
const int RGB_red = 6; //RGB-Led Rot
const int RGB_green = 5; //RGB-Led Grün
const int RGB_blue = 4; //RGB-Led Blau
//Display Variablen
const int ScreenHeight = 64; //Höhe
const int ScreenWidth = 128; //Breite
//Display Objekt erstellen
Adafruit_SSD1306 display(ScreenWidth, ScreenHeight, &Wire, -1);
//Variablen für die Zeitmessung
unsigned long duration; //Gesamte Zeit in ms
int minute; //Minuten Anteil der Zeit
int seconds; //Sekunden Anteil der Zeit
int milliseconds; //Millisekunden Anteil der Zeit
unsigned long starttime = 0; //Startzeit der Messung
unsigned long endtime = 0; //endzeit der Messung
bool timerRunning = false; //Zeitmessung aktiv?
bool stopMeasure = false; //Wurde die Messung gestoppt?
unsigned long showStart = 0;
unsigned long notShowStart = 0;
unsigned long showMeasurements = 0;
unsigned long notShowMeasurements = 0;
int setting_endtime = 5; //Anzeigedauer der Endzeit in Sekunden
int setting_countdown = 10; //Dauer der Startfrequenze
int showingSTART = 5; //wie lange "Start" angezeigt wurde
bool setting_buzzer = true; //Buzzer aktiv
//Variablen für millis (Button Debounce)
bool lastReading = HIGH;
bool stableState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 30;
//Interup Variable
volatile bool resetPressed = false;
//States erstellen
enum mode { READY,
START,
TIME_MEASURE,
STOP };
//state definieren
enum mode state = READY;
//Interrupt Funktion
void IRAM_ATTR myISR() {
resetPressed = true;
}
void setup() {
Serial.begin(115200);
//Ein- und Ausgäge definieren
pinMode(BuzzerPin, OUTPUT);
pinMode(ButtonPin, INPUT_PULLUP);
pinMode(ButtonResetPin, INPUT_PULLUP);
pinMode(RGB_red, OUTPUT);
pinMode(RGB_green, OUTPUT);
pinMode(RGB_blue, OUTPUT);
//Interrupt
attachInterrupt(
digitalPinToInterrupt(ButtonResetPin),
myISR,
FALLING
);
//Display initialisieren
Wire.begin();
//Wenn Display nicht erkannt wird
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("Display Fehler!"));
while (true);
}
//Dispaly reinigen
display.clearDisplay();
display.display();
}
//Funktion zum Text zentrieren
void printCentered(const char* text, int textSize, int y) {
int charWidth = 6 * textSize;
int x = (ScreenWidth - (int)strlen(text) * charWidth) / 2;
if (x < 0) x = 0;
display.setTextSize(textSize);
display.setTextColor(WHITE);
display.setCursor(x, y);
display.print(text);
}
//Funktion für den Countdown/Startfrequenze
void StartFrequence() {
for (int i = setting_countdown; i >= 1; i--) {
if (i > 5) {
display.clearDisplay();
char buffer[4];
sprintf(buffer, "%d", i);
printCentered(buffer, 3, 10);
display.display();
digitalWrite(RGB_red, HIGH);
delay(1000);
}
else {
display.clearDisplay();
char buffer[4];
sprintf(buffer, "%d", i);
printCentered(buffer, 3, 10);
display.display();
//RGB-Led auschalten und 1/2 sec warten
digitalWrite(RGB_red, LOW);
delay(350);
//RGB-Led einschalten
digitalWrite(RGB_red, HIGH);
if (setting_buzzer == true) {
//Buzzer piepsen
if (i == 1) {
tone(BuzzerPin, 880, 300); //höherer Ton beim letzten Blinken
}
else {
tone(BuzzerPin, 440, 80);
}
}
//1/2 sec warten
delay(650);
}
}
//RGB auschalten
digitalWrite(RGB_red, LOW);
}
//Funktion um auf Knopfdruck in den nächsten State zu kommen (millis)
void button_pushed(mode next_state) {
bool reading = digitalRead(ButtonPin);
if (reading != lastReading) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != stableState) {
stableState = reading;
if (stableState == LOW) {
state = next_state; //in den nächsten State wechseln
}
}
}
lastReading = reading;
}
void loop() {
//Interrupt überprüfen
if (resetPressed == true) {
timerRunning = false;
stopMeasure = false;
Serial.println("Zeitmessung zurückgesetzt");
resetPressed = false;
state = READY; //in den State READY springen
}
//Debug1: State ausgeben
Serial.print("State: ");
Serial.println(state);
switch (state) {
//State "READY"
//nächster State wenn der Start Knopf (ButtonPin) gedrückt wurde
case READY:
timerRunning = false;
display.clearDisplay();
printCentered("Ready", 2, 5);
printCentered("to", 2, 25);
printCentered("Race", 2, 45);
display.display();
button_pushed(START);
break;
//State "START"
//nächster State wenn die Startfrequenze vorbei ist
case START:
stopMeasure = false;
//Display reinigen
display.clearDisplay();
display.display();
//Startfrequenze aufrufen
StartFrequence();
state = TIME_MEASURE;
break;
//State "TIME_MEASURE"
//nächster State wenn Stop Knopf (ButtonPin) gedrückt wurde
case TIME_MEASURE:
// starttime und Display nur EINMAL setzen
if (!timerRunning) {
showStart = millis();
starttime = millis();
timerRunning = true;
//Dispaly reinigen
display.clearDisplay();
display.display();
}
notShowStart = millis();
if (notShowStart - showStart <= showingSTART * 1000) {
//Mittig "Start!" anzeigen
display.clearDisplay();
printCentered("Start!", 3, 20);
display.display();
digitalWrite(RGB_green, HIGH);
}
else {
//Mittig "Start!" anzeigen
display.clearDisplay();
display.display();
digitalWrite(RGB_green, LOW);
}
button_pushed(STOP);
break;
//State "STOP"
//nächter State wenn die Zeit eine gewisse Zeit angezeigt wurde
case STOP:
if (!stopMeasure) {
digitalWrite(RGB_green, LOW);
showMeasurements = millis();
endtime = millis();
//Zeit berechnen
duration = endtime - starttime;
minute = (duration / 1000) / 60;
seconds = (duration / 1000) % 60;
milliseconds = duration % 1000;
stopMeasure = true;
}
notShowMeasurements = millis();
if (notShowMeasurements - showMeasurements <= setting_endtime * 1000) {
{
char timeStr[12];
sprintf(timeStr, "%d:%02d.%03d", minute, seconds, milliseconds);
display.clearDisplay();
//Endzeit
printCentered(timeStr, 2, 25);
display.display();
button_pushed(START);
}
}
else {
stopMeasure = false;
state = READY;
}
break;
//Default
default:
display.clearDisplay();
printCentered("Error!", 1, 0);
printCentered("Restarting...", 1, 24);
display.display();
delay(3000);
state = READY;
break;
}
//Macht den Ablauf flüssiger bzw. entschleunigt Loop
delay(10);
}
Loading
esp32-c3-devkitm-1
esp32-c3-devkitm-1
Loading
ssd1306
ssd1306
Start
Reset