//------------------
//- Messwagen      -
//- U. Bohländer   -
//- 10-2025        -
//- Version: 01-21 -
//------------------

#include <SevSeg.h>
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);
}
