//Deklaration der benötigten Projekte
	#include <Arduino.h>


//End of Deklaration der benötigten Projekte

//Deklaration der Funktionen
	void f_SetupSerial();
	void f_Main();
  void f_CheckTimer();
	void f_CheckSensor();
	void f_CheckSensorValue();
	void f_Interrupt_Triggered();
	void f_ResetAmpel();
 	void f_setAmpel();
  void f_WasserGeben();
  void f_WasserStoppen();
	int getSensorAnalogValue();
	int getSensorDigitalValue();

  
//End of Deklaration der Funktionen

	//Deklaration der Konstanten
		String  strSetupProjektName = "ESP32 - Feuchtigkeitssensor";
		String  strSetupProjektVersion = "Version 0.2";

// Modulspezifisch
		//Arduino Board
			#define GREEN_LED 23     // Pin 37 GPIO23 VSPI MOSI für die grüne LED
			#define BLUE_LED 22      // Pin 36 GPIO22 I2CSCL    für die blaue LED
			#define RED_LED 21       // Pin 33 GPIO21 I2C SDA   für die rote LED
			#define SENSOR_PIN_A0 34 // Pin 5 GPIO34 ADC6 für den analogen Bodenfeuchtesensor
			#define SENSOR_PIN_D0 35 // Pin 6 GPIO35 ADC7 für den digitalen Sensor-Ausgang
			#define OUTPUT2RELAIS_IN1 2 // GPIO2  für den Relais Input 1
//End of Deklaration der Konstanten

//Deklaration initiales Setzten der Variabeln
	int intSensorAnalogValue = 0;   // Variable für den analogen Sensorwert
	int intSensorDigitalValue = 0;  // Variable für den digitalen Sensorwert
	String strFeuchtigkeit = ""; // Trocken / ideal / feucht
	String  strSerielOutput = ""; // String für die serielle Ausgabe
 	bool boolSensorInterrupt = false; // Boolean, fuer die Entprellung des SchwellWertAusgang des FeuchtigkeitsSensor
 	bool boolWasser_geben = false;            // globale Variabel um Wasser zu geben
 	int intFeuchtigkeitOKlow = 1900;  // SensortWert unterer Schwelle, ab dem es nicht mehr als zu feucht ist
 	int intFeuchtigleitOKhigh = 2200; // SensorWert oberer Schwelle, ab dem es dann trocken wird
	
  // Timer für Diverses
    unsigned long lngStartMainLoopMillis;
    unsigned long lngCurrentMainLoopMillis;
    const unsigned long lngMainLoopPeriod = 5000;  //Periode für das Aufrufen des MainLoops. the value is a number of milliseconds
    bool boolMainLoopPeriodAbgelaufen;

    unsigned long lngStartCheckWasserGebenMillis;
    unsigned long lngCurrentCheckWasserGebenMillis;
    const unsigned long lngCheckWasserGebenPeriod = 60000; // Periode nach welcher geprüft werden soll, ob es Wasser benötigt.
    bool boolCheckWasserGebenPeriodAbgelaufen;

    unsigned long lngStartLastInterruptMillis;
    unsigned long lngCurrentLastInterruptMillis;
    const unsigned long lngLastInterruptPeriod = 15000; // Periode wie lange kein weiterer Interrupt zugelassen wird.
    bool boolLastInterruptPeriodAbgelaufen;

    unsigned long lngStartWasserGebenMillis;
    unsigned long lngCurrentWasserGebenMillis;
    const unsigned long lngWasserGebenPeriod = 5000; // Periode, wie lange das Relais für die Pumpe oder Ventil angesteurt werden soll
    bool boolWasserGebenPeriodeAktiv = false;

//End of Deklaration initiales Setzten der Variabeln



void setup() {
  f_SetupSerial();
 
  Serial.println ("Setup Pinout's Board");  
 
  pinMode(BLUE_LED, OUTPUT);			// Setzt den PIN für die blaue LED als Ausgang.
  pinMode(RED_LED, OUTPUT);				// Setzt den PIN für die rote LED als Ausgang.
  pinMode(GREEN_LED, OUTPUT);			// Setzt den PIN für die grüne LED als Ausgang.

  pinMode(SENSOR_PIN_A0, INPUT);		// Setzt den PIN für die Feuchtigkeitssensor A0 als Eingang.
  attachInterrupt(digitalPinToInterrupt(SENSOR_PIN_D0),f_Interrupt_Triggered,RISING);
  pinMode(SENSOR_PIN_D0, INPUT);		// Setzt den PIN für die Feuchtigkeitssensor D0 als Eingang.

  digitalWrite(OUTPUT2RELAIS_IN1, LOW); // Definieren des Ausgangs sogleich auf LOW, dass nach pinMode der Ausgang nicht das Relais schaltet.
  pinMode(OUTPUT2RELAIS_IN1, OUTPUT);	// Setzt den PIN für das Relais D1 als Ausgang.

  Serial.println ("Setup Ampel");  
  f_ResetAmpel();
  f_Main();
  Serial.println ("Setup Done");
  Serial.println ("Starte MainLoop");

  lngStartLastInterruptMillis = millis();


}

// Mainloop mit MainLoopZähler
void loop() {
 
  // Prüfen der Perioden
  f_CheckTimer();
  
  if (boolMainLoopPeriodAbgelaufen)
    {
      f_Main();
    
      lngStartMainLoopMillis = lngCurrentMainLoopMillis; // Reset MainloopTimer
    }

  if (boolCheckWasserGebenPeriodAbgelaufen or (boolLastInterruptPeriodAbgelaufen and boolSensorInterrupt))
    {
      if (boolLastInterruptPeriodAbgelaufen and boolSensorInterrupt) // Interrupt nur akzeptieren wenn die Periode abgelaufen ist
        {
          Serial.println ("Interrupt ausgeloest");
          lngStartLastInterruptMillis = lngCurrentLastInterruptMillis; // Reset InterruptTimer
        }
      
      if (boolWasser_geben and not boolWasserGebenPeriodeAktiv) // insofern es nötig ist zu Wässer werden die Flags gesetzt
        {
          Serial.println ("Setze Wasser geben ");
          boolWasserGebenPeriodeAktiv = true;
          boolCheckWasserGebenPeriodAbgelaufen = false;
        }
  
      boolSensorInterrupt = false; // allfälliger Interrupt reseten da eben ein Check statt fand
      lngStartCheckWasserGebenMillis = lngCurrentCheckWasserGebenMillis;
      
    }
  if (boolWasserGebenPeriodeAktiv)
      {

        f_WasserGeben();
        boolWasser_geben = false;
      }
      else
      {
        f_WasserStoppen();
      }
}

// Function Main
void f_Main(){

  f_CheckSensor();
	f_CheckSensorValue();
  f_setAmpel();
	f_prnSerielOutput();

}


// Function Check Timers und setze entsprechende Flags
void f_CheckTimer(){
  // Check Mainloop Timer, um die Sensoren abzufragen.
  lngCurrentMainLoopMillis = millis();  //get the current "time" (actually the number of milliseconds since the program started)
  if (lngCurrentMainLoopMillis - lngStartMainLoopMillis >= lngMainLoopPeriod)  //test whether the period has elapsed
    {
      boolMainLoopPeriodAbgelaufen = true;
    }
    else
    {
      boolMainLoopPeriodAbgelaufen = false;
    }

  // Check, ob der Timer abgelaufen ist, dass ein Interrupt wieder akzeptiert werden soll
  lngCurrentLastInterruptMillis = millis();
  if (lngCurrentLastInterruptMillis - lngStartLastInterruptMillis >= lngLastInterruptPeriod)  //test whether the period has elapsed
    {
      boolLastInterruptPeriodAbgelaufen = true;
    }
    else
    {
      boolLastInterruptPeriodAbgelaufen = false;
      boolSensorInterrupt = false; // Rücksetzen des Interrupt-Flags solange der Timer nicht abgelaufen ist.
    }

  // Check, ob die SperrDauer fürs erneute Wassser geben abgelaufen ist
  lngCurrentCheckWasserGebenMillis = millis();
  if (lngCurrentCheckWasserGebenMillis - lngStartCheckWasserGebenMillis >= lngCheckWasserGebenPeriod)  //test whether the period has elapsed
    {
      boolCheckWasserGebenPeriodAbgelaufen = true;
    }
    else
    {
      boolCheckWasserGebenPeriodAbgelaufen = false;
    }
  
  // Check, ob die Dauer fürs Wasser geben abgelaufen ist
  if (boolWasserGebenPeriodeAktiv)
  {
    if (digitalRead(OUTPUT2RELAIS_IN1) == LOW) // einmaliger Durchlauf
      {
        Serial.println ("Reset lngStartWasserGebenMillis");
        lngStartWasserGebenMillis = millis();
      }    
    
    lngCurrentWasserGebenMillis = millis();  //get the current "time" (actually the number of milliseconds since the program started)  
    if ((lngCurrentWasserGebenMillis - lngStartWasserGebenMillis >= lngWasserGebenPeriod))  //test whether the period has elapsed
      {
        boolWasserGebenPeriodeAktiv = false;
      }
      else
      {
        boolWasserGebenPeriodeAktiv = true;
      }
  }
}




// Function Serielle USB Schnittstelle konfigurieren
void f_SetupSerial() {
	Serial.begin(115200);				// Begin der seriellen Kommunikation
	Serial.println ("*********************************");
	Serial.println ("Projekt INFO");
	Serial.println ("Projekt Name: " + strSetupProjektName);
	Serial.println ("Projekt Vers: " + strSetupProjektVersion);
	Serial.println ("*********************************");
}

//Function Prüfe Sensor
void f_CheckSensor(){
	intSensorAnalogValue = getSensorAnalogValue();
	intSensorDigitalValue = getSensorDigitalValue();
}

//Function Werte des Senors prüfen und entscheiden, ob die Erde "feucht", "i.O" oder "trocken" ist
void f_CheckSensorValue(){
    strFeuchtigkeit = "";
    
    if(intSensorAnalogValue >= 0 && intSensorAnalogValue < intFeuchtigkeitOKlow) /* unterer Grenzwert (zu nass) */
    {
	    strFeuchtigkeit = "feucht";
	    strSerielOutput = "Sensor Wert: " + strFeuchtigkeit + " / ";
      boolWasser_geben = false;
    }
    else if(intSensorAnalogValue >= intFeuchtigkeitOKlow && intSensorAnalogValue <= intFeuchtigleitOKhigh) /* Grenzwert i.O. (nicht zu nass und auch nicht zu trocken) */
    {
	    strFeuchtigkeit = "Wert ok";
	    strSerielOutput = "Sensor Wert: " + strFeuchtigkeit + " / ";
      boolWasser_geben = false;
    }
    else if(intSensorAnalogValue >= intFeuchtigleitOKhigh) /* oberer Grenzwert (zu trocken) */
    {
	    strFeuchtigkeit = "trocken";
	    strSerielOutput = "Sensor Wert: " + strFeuchtigkeit + " / ";
      boolWasser_geben = true;
    }	
}

//Liest den analogen Wert des Bodenfeuchtesensors und gibt diesen zurück.
int getSensorAnalogValue(){
  return analogRead(SENSOR_PIN_A0);
} 

//Liest den digitalen Wert des Bodenfeuchtesensors und gibt diesen zurück.
int getSensorDigitalValue(){
  return digitalRead(SENSOR_PIN_D0);
} 

//Function Print SerielOutput
void f_prnSerielOutput(){
    Serial.print (strSerielOutput);
    Serial.print (intSensorAnalogValue);
    Serial.print ("   D0:");
    Serial.print (intSensorDigitalValue);
    Serial.print (" / MainLoopPeriode: ");
    Serial.print (boolMainLoopPeriodAbgelaufen ? "TRUE" : "FALSE");
    Serial.print (" / LastInterruptPeriode: ");
    Serial.print (boolLastInterruptPeriodAbgelaufen ? "TRUE" : "FALSE");
    Serial.print (" / CheckWasserGebenPeriode: ");
    Serial.print (boolCheckWasserGebenPeriodAbgelaufen ? "TRUE" : "FALSE");
    Serial.print (" / WasserGebenPeriodeAktiv: ");
    Serial.print (boolWasserGebenPeriodeAktiv ? "TRUE" : "FALSE");
    Serial.println();

}

// Function wenn Interrupt getriggert wird
// keine weiteren Befehle in dieser Interrupt Routine möglich. Prog stürzt ab
void f_Interrupt_Triggered(){
	if (boolSensorInterrupt)
	{
		// Ignoriere Interrupt auf Grund des Prellens des FeuchtigkeitsSensor Ausgang D0
 
 	}
	else
	{
		boolSensorInterrupt = true;			
	}

} 

//Setzte die Farben der Ampel-LED zurück.
void f_ResetAmpel(){
	analogWrite(RED_LED, 0);
	analogWrite(GREEN_LED, 0);
	analogWrite(BLUE_LED, 0);
	delay(500);
	analogWrite(RED_LED, 255);
	analogWrite(GREEN_LED, 0);
	analogWrite(BLUE_LED, 0);	
	delay(500);
	analogWrite(RED_LED, 0);
	analogWrite(GREEN_LED, 0);
	analogWrite(BLUE_LED, 255);
	delay(500);
	analogWrite(RED_LED, 0);
	analogWrite(GREEN_LED, 255);
	analogWrite(BLUE_LED, 0);
	delay(500);	
	analogWrite(RED_LED, 0);
	analogWrite(GREEN_LED, 0);
	analogWrite(BLUE_LED, 0);
	delay(500);
}

/* Function f_setAmpel LED's
	LED grün	= ok
	LED blau	= feucht
	LED rot		= trocken
*/
void f_setAmpel(){
	if (strFeuchtigkeit == "trocken") 
      {
        analogWrite(RED_LED, 255);
        analogWrite(GREEN_LED, 0);
        analogWrite(BLUE_LED, 0);  
      }
    else if (strFeuchtigkeit == "Wert ok")
      {
        analogWrite(RED_LED, 0);
        analogWrite(GREEN_LED, 255);
        analogWrite(BLUE_LED, 0);
      }
    else if (strFeuchtigkeit == "feucht")
      {
        analogWrite(RED_LED, 0);
        analogWrite(GREEN_LED, 0);
        analogWrite(BLUE_LED, 255); 
      }
    
    else
      {
        analogWrite(RED_LED, 255);
        analogWrite(GREEN_LED, 255);
        analogWrite(BLUE_LED, 255);
      }

    delay(500);
    analogWrite(RED_LED, 0);
    analogWrite(GREEN_LED, 0);
    analogWrite(BLUE_LED, 0);
}

void f_WasserGeben(){
  if (digitalRead(OUTPUT2RELAIS_IN1) == LOW) // nur ausführen wenn der Ausgang noch nicht geschalten wurde
    {
      Serial.println ("Wasser_geben ausloesen");
      digitalWrite(OUTPUT2RELAIS_IN1, HIGH);  /* Wasser geben ein*/
      analogWrite(RED_LED, 255);
      analogWrite(GREEN_LED, 255);
      analogWrite(BLUE_LED, 255);
      lngStartWasserGebenMillis=millis(); // Timer fuers Wasser geben reseten
    }
}

void f_WasserStoppen(){
  if (digitalRead(OUTPUT2RELAIS_IN1) == HIGH ) // nur ausführen, wenn der Ausgang noch nicht ausgeschalten wurde
  {
    Serial.println ("WASSER Stopp");
    digitalWrite(OUTPUT2RELAIS_IN1, LOW);   /* Wasser geben aus */    
    analogWrite(RED_LED, 0);
    analogWrite(GREEN_LED, 0);
    analogWrite(BLUE_LED, 0);
    boolWasserGebenPeriodeAktiv = false;
    boolWasser_geben = false;
    }
}