#include SevSeg sevseg; // --- Pin-Definitionen --- const byte hallPin = 2; const byte modeButtonPin = A1; const byte resetButtonPin = A2; const byte ledSpeed = A3; const byte ledTime = A4; const byte ledDistance = A5; enum Mode { GESCHWINDIGKEIT, FAHRZEIT, FAHRSTRECKE }; Mode currentMode; // --- Variablen --- volatile unsigned long pulseCount = 0; volatile unsigned long distancePulses = 0; volatile unsigned long lastPulseTime = 0; // Für Entprellung (µs) unsigned long startMillis = 0; bool timeStarted = false; unsigned long elapsedMillis = 0; // ⚠️ Rad-Durchmesser in Metern anpassen! const float radDurchmesser = 0.011; // z.B. 11 mm const float umfang = radDurchmesser * 3.1416; // Radumfang bool lastModeButton = LOW; bool lastResetButton = LOW; void setup() { // SevSeg Setup byte numDigits = 4; byte digitPins[] = {3, 4, 5, 6}; byte segmentPins[] = {7, 8, 9, 10, 11, 12, 13, A0}; bool resistorsOnSegments = true; bool updateWithDelays = false; bool leadingZeros = true; bool disableDecPoint = false; sevseg.begin(COMMON_CATHODE, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint); sevseg.setBrightness(90); pinMode(hallPin, INPUT_PULLUP); pinMode(modeButtonPin, INPUT_PULLUP); pinMode(resetButtonPin, INPUT_PULLUP); pinMode(ledSpeed, OUTPUT); pinMode(ledTime, OUTPUT); pinMode(ledDistance, OUTPUT); attachInterrupt(digitalPinToInterrupt(hallPin), hallISR, FALLING); // --- Startmodus: Geschwindigkeit --- currentMode = GESCHWINDIGKEIT; digitalWrite(ledSpeed, HIGH); digitalWrite(ledTime, LOW); digitalWrite(ledDistance, LOW); sevseg.setNumber(0, 1); // Anzeige mit "0.0" starten // Tasterzustände zum Start einlesen -> verhindert ungewolltes Umschalten lastModeButton = !digitalRead(modeButtonPin); lastResetButton = !digitalRead(resetButtonPin); } void loop() { bool modePressed = !digitalRead(modeButtonPin); // wegen INPUT_PULLUP if (modePressed && !lastModeButton) { currentMode = (Mode)((currentMode + 1) % 3); resetAll(); } lastModeButton = modePressed; bool resetPressed = !digitalRead(resetButtonPin); // wegen INPUT_PULLUP if (resetPressed && !lastResetButton) { resetAll(); } lastResetButton = resetPressed; switch (currentMode) { case GESCHWINDIGKEIT: showSpeed(); break; case FAHRZEIT: showTime(); break; case FAHRSTRECKE: showDistance(); break; } sevseg.refreshDisplay(); } // --- Interrupt-Funktion mit Entprellung (5 ms Sperrzeit) --- void hallISR() { unsigned long currentTime = micros(); if (currentTime - lastPulseTime > 5000) { // >5 ms Abstand pulseCount++; distancePulses++; if (!timeStarted) { timeStarted = true; startMillis = millis(); } lastPulseTime = currentTime; } } // --- Anzeige zurücksetzen --- void resetAll() { pulseCount = 0; distancePulses = 0; timeStarted = false; elapsedMillis = 0; startMillis = millis(); if (currentMode == GESCHWINDIGKEIT) { sevseg.setNumber(0, 1); // "0.0" } else { sevseg.setNumber(0, 0); // "0000" } } // --- Anzeige: Modellgeschwindigkeit in km/h --- void showSpeed() { digitalWrite(ledSpeed, HIGH); digitalWrite(ledTime, LOW); digitalWrite(ledDistance, LOW); static unsigned long lastUpdate = 0; static float kmh = 0; const unsigned long updateInterval = 200; // Anzeige-Update alle 200 ms const unsigned long windowMs = 300; // Zählfenster const float alpha = 0.4; // Glättung für EMA const float lowSpeedThreshold = 10.0; // Grenze zw. langsam und schnell const unsigned long timeoutMs = 300; // Zeit bis Abfall auf 0 km/h static unsigned long windowStart = 0; static unsigned long windowPulses = 0; unsigned long now = millis(); // Pulse seit letztem Interrupt holen noInterrupts(); unsigned long pulses = pulseCount; pulseCount = 0; unsigned long lastPulseCopy = lastPulseTime; interrupts(); // Pulse in aktuelles Fenster eintragen windowPulses += pulses; if (now - windowStart >= windowMs) { // --- Fenster fertig --- float dist = (windowPulses / 4.0) * umfang; // 4 Impulse pro Radumdrehung float speed_mps = dist / (windowMs / 1000.0); float currentKmh = speed_mps * 3.6 * 87.0; // --- Bereichsauswahl --- if (currentKmh < lowSpeedThreshold) { kmh = currentKmh; // direkter Wert im unteren Bereich } else { kmh = alpha * currentKmh + (1 - alpha) * kmh; // EMA-Glättung } // Reset Fenster windowPulses = 0; windowStart = now; } // --- Sofortiger Rückfall auf 0, wenn zu lange keine Pulse --- if ((micros() - lastPulseCopy) / 1000 > timeoutMs) { kmh = 0; } // --- Anzeige nur alle updateInterval ms aktualisieren --- if (now - lastUpdate >= updateInterval) { int kmh10 = (int)(kmh * 10); // eine Nachkommastelle sevseg.setNumber(kmh10, 1); lastUpdate = now; } } // --- Anzeige: Fahrzeit MM.SS --- void showTime() { digitalWrite(ledSpeed, LOW); digitalWrite(ledTime, HIGH); digitalWrite(ledDistance, LOW); if (timeStarted) { elapsedMillis = millis() - startMillis; unsigned int totalSec = elapsedMillis / 1000; unsigned int minutes = (totalSec / 60) % 100; unsigned int seconds = totalSec % 60; int value = minutes * 100 + seconds; sevseg.setNumber(value, 2); } else { sevseg.setNumber(0, 0); } } // --- Anzeige: Strecke in Metern mit 1 Nachkommastelle --- void showDistance() { digitalWrite(ledSpeed, LOW); digitalWrite(ledTime, LOW); digitalWrite(ledDistance, HIGH); float dist = (distancePulses / 4.0) * umfang; // 4 Impulse pro Umdrehung int value = (int)(dist * 10); sevseg.setNumber(value, 1); }