#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>

// LCD Display Initialisierung
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Keypad Initialisierung
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {9,8,7,6};
byte colPins[COLS] = {5,4,3, 12};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

// Pins
const int relayPin = 10;
const int flowSensorPin = 2;

// Variablen
volatile int pulseCount = 0;      // Anzahl der Impulse
float calibrationFactor = 0.01;    // Kalibrierungsfaktor
float flowRate = 0;               // Liter pro Minute
float totalLiters = 0;            // Gesamte geflossene Liter
float targetLiters = 0;           // Zielmenge
bool relayState = false;          // Relaiszustand
bool paused = false;              // Pausenstatus
bool inCalibrationMode = false;   // Kalibrierungsmodus
unsigned long lastMillis = 0;     // Zeitmanagement für LCD-Updates
int debounceTime = 1000;          // Debounce-Zeit für lange Tasten
unsigned long keyPressTime = 0;   // Zeitpunkt des Tastendrucks
unsigned long lastFlowCalculation = 0;  // Zeitpunkt der letzten Berechnung
int pulseCountLastSecond = 0;         // Anzahl der Impulse in den letzten 3 Sekunden


// Setup
void setup() {
  lcd.init();
  lcd.backlight();
  lcd.print("System Start...");

  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, LOW);

  pinMode(flowSensorPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(flowSensorPin), pulseCounter, RISING);  //Falling wenn normal high und Signal low

  delay(2000);
  lcd.clear();
}

// Hauptprogramm
void loop() {
  char key = keypad.getKey();
  if (key) {
    handleKey(key);
  }

  // Flussrate berechnen und anzeigen
  if (millis() - lastMillis > 1000) {
    lastMillis = millis();
    calculateFlow();
    updateLCD();
    checkRelay();
  }
}


// Funktion: Durchflussrate berechnen
void calculateFlow() {
  unsigned long currentMillis = millis();
  
  // Wenn mehr als 1 Sekunde vergangen ist
  if (currentMillis - lastFlowCalculation >= 1000) {
    // Berechne die Durchflussrate basierend auf den Impulsen der letzten 3 Sekunden
    flowRate = (pulseCountLastSecond / calibrationFactor) / 3.0; // Liter pro Minute
    totalLiters += flowRate / 60.0 * 1.0;  // Gesamte Litermenge basierend auf den letzten 3 Sekunden
    pulseCountLastSecond = 0;  // Zurücksetzen des Impulszählers
    lastFlowCalculation = currentMillis;  // Zeit der letzten Berechnung speichern
  }
}

// Impulszählung, aufgerufen bei jedem Impuls des Flow-Sensors
void pulseCounter() {
  pulseCountLastSecond++;  // Erhöhe den Impulszähler für die letzten 3 Sekunden
}

// Funktion: LCD aktualisieren
void updateLCD() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Ziel: ");
  lcd.print(targetLiters, 0);
  lcd.print("L");
  lcd.setCursor(0, 1);
  lcd.print("");
  lcd.print(totalLiters, 0);
  lcd.print("L");
  lcd.setCursor(8, 1);
  lcd.print(flowRate, 0);
  lcd.setCursor(11, 1);
  lcd.print("L/min");
}

// Funktion: Taste verarbeiten
void handleKey(char key) {
  if (key >= '0' && key <= '9') {
    if (!inCalibrationMode) {
      targetLiters = targetLiters * 10 + (key - '0');
    } else {
      calibrationFactor = calibrationFactor * 10 + (key - '0');
    }
    updateLCD();
  } else if (key == '#') {
    if (relayState && !paused) {
      paused = true;
      digitalWrite(relayPin, LOW); // Relais ausschalten
    } else if (relayState && paused) {
      paused = false;
      digitalWrite(relayPin, HIGH); // Relais einschalten
    } else if (!relayState) {
      relayState = true;
      paused = false;
      digitalWrite(relayPin, HIGH); // Relais einschalten
    }
    updateLCD();
  } else if (key == '*') {
    totalLiters = 0;
    targetLiters = 0;
    updateLCD();
  } else if (key == '1' || key == '3') {
    if (keyPressTime == 0) {
      keyPressTime = millis();
    } else if (millis() - keyPressTime > debounceTime) {
      inCalibrationMode = true;
      calibrationFactor = 0;
      lcd.clear();
      lcd.print("Kalibrierung");
      lcd.setCursor(0, 1);
      lcd.print("Faktor: ");
      lcd.print(calibrationFactor);
    }
  }
}

// Funktion: Relais prüfen
void checkRelay() {
  if (totalLiters >= targetLiters && relayState && targetLiters>0) {
    relayState = false;
    paused = false;
    digitalWrite(relayPin, LOW);
    lcd.clear();
    lcd.print("Fertig!");
    delay(2000);
    lcd.clear();
    updateLCD();
  }
}
NOCOMNCVCCGNDINLED1PWRRelay Module