//------------------ //- Messwagen - //- U. Bohländer - //- 10-2025 - //- Version: 01-21 - //------------------ #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 // --- Tasterentprellung --- bool lastModeState = HIGH; bool lastResetState = HIGH; unsigned long lastModeChange = 0; unsigned long lastResetChange = 0; const unsigned long debounceDelay = 50; // Entprellzeit (ms) const unsigned long holdDelay = 200; // Mindestdruckzeit (ms) 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 } // --- Entprelltes Lesen eines Tasters --- bool readButton(byte pin, bool &lastState, unsigned long &lastChange, unsigned long delayMs = debounceDelay) { bool reading = digitalRead(pin); if (reading != lastState && millis() - lastChange > delayMs) { lastChange = millis(); lastState = reading; if (reading == LOW) return true; // aktiver Druck } return false; } void loop() { // --- Tasterhandling mit Entprellung & Mindestdruckzeit --- static unsigned long modePressStart = 0; static unsigned long resetPressStart = 0; // MODE-Taster bool modePressed = digitalRead(modeButtonPin) == LOW; if (modePressed && modePressStart == 0) modePressStart = millis(); if (!modePressed && modePressStart != 0) { if (millis() - modePressStart >= holdDelay) { currentMode = (Mode)((currentMode + 1) % 3); resetAll(); } modePressStart = 0; } // RESET-Taster bool resetPressed = digitalRead(resetButtonPin) == LOW; if (resetPressed && resetPressStart == 0) resetPressStart = millis(); if (!resetPressed && resetPressStart != 0) { if (millis() - resetPressStart >= holdDelay) { resetAll(); } resetPressStart = 0; } // --- Modusausgabe --- 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" } digitalWrite(ledSpeed, currentMode == GESCHWINDIGKEIT); digitalWrite(ledTime, currentMode == FAHRZEIT); digitalWrite(ledDistance, currentMode == FAHRSTRECKE); } // --- 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; const unsigned long windowMs = 300; const float alpha = 0.2; const float lowSpeedThreshold = 10.0; const unsigned long timeoutMs = 300; 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) { float dist = (windowPulses / 4.0) * umfang; float speed_mps = dist / (windowMs / 1000.0); float currentKmh = speed_mps * 3.6 * 87.0; if (currentKmh < lowSpeedThreshold) { kmh = currentKmh; } else { kmh = alpha * currentKmh + (1 - alpha) * kmh; } windowPulses = 0; windowStart = now; } // Sofortiger Rückfall auf 0 bei Stillstand if ((micros() - lastPulseCopy) / 1000 > timeoutMs) { kmh = 0; } if (now - lastUpdate >= updateInterval) { int kmh10 = (int)(kmh * 10); 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; int value = (int)(dist * 10); sevseg.setNumber(value, 1); }