//https://github.com/MakeMagazinDE/Taupunktluefter/blob/main/Taupunkt_Lueftung.ino 
//https://forum.arduino.cc/t/hilfe-bei-einer-Implen-pumpensteuerung/509398/7
//https://wokwi.com/arduino/projects/324484140498944595
//24.02.2022

// Dieser Code benötigt die folgenden Libraries:
#include "DHT.h"
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
//#include <avr/wdt.h>

//Messinterval Feuchte in ms
const long interval = 7000; //Intervall für aktualisierung Taupunkte und Logik
unsigned long previousMillis = 0;
int ledState = LOW;

//Taupunkte Speicher
float Taupunkt_1 = 0.0;
float Taupunkt_2 = 0.0;

//RELAIS OUTPUTS (FALSE = EIN)
const int DO_00 = 22; //Lüftergruppe Zuluft Keller
const int DO_01 = 24; //Lüftergruppe Abluft Werkstatt
const int DO_02 = 26; //Lüftergruppe Abluft Keller
const int DO_03 = 28; //Freigabe Luftentfeuchter
const int DO_04 = 30; //Reserve
const int DO_05 = 32; //Reserve
const int DO_06 = 34; //Reserve
const int DO_07 = 36; //12V KondenAtpumpe

//direkte OUTPUTS (TRUE = EIN)
const int DO_08 = 46; //Fehler LED 
const int DO_09 = 44; //Status LED

//INPUTS
const int DI_00 = 23; //LSH Hochschaltpunkt Pumpstation
const int DI_01 = 25; //LSL Tiefschaltpunkt Pumpstation
const int DI_02 = 27; //Reserve für HAND EIN ENTFEUCHTER
const int DI_03 = 29; //Reserve für HAND EIN LÜFTER
const int DI_04 = 31; //Reserve für Türkontakt Werkstatt
const int DI_05 = 33; //Reserve
const int DI_06 = 35; //Reserve
const int DI_07 = 37; //Reserve

//INPUTS DHT21 Sensoren
#define DHTPIN_1 5 // Datenleitung für den DHT-Sensor 1 (innen)
#define DHTPIN_2 4 // Datenleitung für den DHT-Sensor 2 (außen)

#define RELAIS_EIN LOW   //Invertierung der Ausgänge 
#define RELAIS_AUS HIGH

bool trocknen; //trocknen durch Aussenluft möglich
bool fehler = true;//Fehler bei Trocknungs Logik

#define DHTTYPE_1 DHT22 // Auf DHT 21 geändert / vorher  DHTTYPE_1 DHT22 
#define DHTTYPE_2 DHT22 // 

// *******  Korrekturwerte der einzelnen Sensorwerte  *******
#define Korrektur_t_1  0 // Korrekturwert Innensensor Temperatur / geändert auf 0 vorher -3
#define Korrektur_t_2  0 // Korrekturwert Außensensor Temperatur / geändert auf 0 vorher -4
#define Korrektur_h_1  0  // Korrekturwert Innensensor Luftfeuchtigkeit
#define Korrektur_h_2  0  // Korrekturwert Außensensor Luftfeuchtigkeit
//***********************************************************

#define SCHALTmin   5.0 // minimaler Taupunktunterschied, bei dem das Relais schaltet
#define HYSTERESE   1.0 // Abstand von Ein- und Ausschaltpunkt
#define TEMP1_min  7.0 // Minimale Innentemperatur, bei der die Lüftung aktiviert wird / geändert auf 7.5 vorher 10.0
#define TEMP2_min -10.0 // Minimale Außentemperatur, bei der die Lüftung aktiviert wird
#define h1_soll     55.0 //Soll Luftfeuchte im Keller

DHT dht1(DHTPIN_1, DHTTYPE_1); //Der Innensensor wird ab jetzt mit dht1 angesprochen
DHT dht2(DHTPIN_2, DHTTYPE_2); //Der Außensensor wird ab jetzt mit dht2 angesprochen

LiquidCrystal_I2C lcd(0x27,20,4); // LCD: I2C-Addresse und Displaygröße setzen



void setup() {  //Setup 


  pinMode(DO_00, OUTPUT); 
  pinMode(DO_01, OUTPUT);
  pinMode(DO_02, OUTPUT);
  pinMode(DO_03, OUTPUT);
  pinMode(DO_04, OUTPUT);
  pinMode(DO_05, OUTPUT);
  pinMode(DO_06, OUTPUT);
  pinMode(DO_07, OUTPUT);

  pinMode(DO_08, OUTPUT);
  pinMode(DO_09, OUTPUT);
  
  
  digitalWrite(DO_00, RELAIS_AUS); // Relais ausschalten
  digitalWrite(DO_01, RELAIS_AUS);
  digitalWrite(DO_02, RELAIS_AUS);
  digitalWrite(DO_03, RELAIS_AUS);
  digitalWrite(DO_04, RELAIS_AUS);
  digitalWrite(DO_05, RELAIS_AUS);
  digitalWrite(DO_06, RELAIS_AUS);
  digitalWrite(DO_07, RELAIS_AUS);

  digitalWrite(DO_08,LOW);
  digitalWrite(DO_09,LOW);

  pinMode(DI_00, INPUT_PULLUP); 
  pinMode(DI_01, INPUT_PULLUP);
  pinMode(DI_02, INPUT);
  pinMode(DI_03, INPUT);
  pinMode(DI_04, INPUT);
  pinMode(DI_05, INPUT);
  pinMode(DI_06, INPUT);
  pinMode(DI_07, INPUT_PULLUP);



  
  Serial.begin(9600);  // Serielle Ausgabe, falls noch kein LCD angeschlossen ist
  Serial.println(F("Setup Sensoren.."));

  lcd.init();
  lcd.backlight();                      
  lcd.setCursor(2,0);
  lcd.print(F("Setup Sensoren.."));
  
  byte Grad[8] = {B00111,B00101,B00111,B0000,B00000,B00000,B00000,B00000};      // Sonderzeichen ° definieren
  lcd.createChar(0, Grad);
  byte Strich[8] = {B00100,B00100,B00100,B00100,B00100,B00100,B00100,B00100};   // Sonderzeichen senkrechter Strich definieren
  lcd.createChar(1, Strich);
    
  dht1.begin(); // Sensoren starten
  dht2.begin();   
}


//----------------------------------------------------------LOOP START
void loop() {

  //Temp und Luftfeuchte lesen und in Speicher schreiben -> Aktualisiere Werte jeden Zyklus
  float h1 = dht1.readHumidity()+Korrektur_h_1;       // Innenluftfeuchtigkeit auslesen und unter „h1“ speichern
  float t1 = dht1.readTemperature()+ Korrektur_t_1;   // Innentemperatur auslesen und unter „t1“ speichern
  float h2 = dht2.readHumidity()+Korrektur_h_2;       // Außenluftfeuchtigkeit auslesen und unter „h2“ speichern
  float t2 = dht2.readTemperature()+ Korrektur_t_2;   // Außentemperatur auslesen und unter „t2“ speichern
  

  if (fehler == true)  // wenn fehler true Prüfen, ob gültige Werte von den Sensoren kommen
  {
    fehler = false; 
    if (isnan(h1) || isnan(t1) || h1 > 100 || h1 < 1 || t1 < -30 || t1 > 80 )  {
      Serial.println(F("Fehler beim Auslesen vom 1. Sensor!"));
      lcd.setCursor(0,1);
      lcd.print(F("Fehler Sensor 1"));
      fehler = true;
    }else {
     lcd.setCursor(0,1);
     lcd.print(F("Sensor 1 in Ordnung"));
   }
  
    delay(2000);  // Zeit um das Display zu lesen
  
      if (isnan(h2) || isnan(t2) || h2 > 100 || h2 < 1 || t2 < -30 || t2  > 80)  {
        Serial.println(F("Fehler beim Auslesen vom 2. Sensor!"));
        lcd.setCursor(0,2);
        lcd.print(F("Fehler Sensor 2"));
        fehler = true;
      } else {
        lcd.setCursor(0,2);
        lcd.print(F("Sensor 2 in Ordnung"));
     }

    delay(2000);  // Zeit um das Display zu lesen
  }
  if (isnan(h1) || isnan(t1) || isnan(h2) || isnan(t2)) fehler = true;
   
 if (fehler == true) {
    digitalWrite(DO_00, RELAIS_AUS); // Relais ausschalten 
	digitalWrite(DO_08, HIGH); //Fehler LED an
	lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(F("SENSORFEHLER "));
	  lcd.setCursor(0,1);
    lcd.print(F("LUEFTUNG AUS"));

    while (1){ // Endlosschleife
	pumpen();//Pumpfunktion hier auch aufrufen? -Pumpe soll auch laufen wenn Lüftung ein Problem hat
	};   
 }


//Messintervall Taupunkte
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // letzten Ausführungszeitpunkt speichern
    previousMillis = currentMillis;

//**** Taupunkte errechnen********
Taupunkt_1 = taupunkt(t1, h1);
Taupunkt_2 = taupunkt(t2, h2);

    // wenn aus dann ein sonst aus
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // schreibe Status LED 
    digitalWrite(DO_09, ledState);
  }

// Werteausgabe auf Serial Monitor
  Serial.print(F("Sensor 1 -Innen: " ));
  Serial.print(F("Luftfeuchtigkeit: "));
  Serial.print(h1);                     
  Serial.print(F("%  Temperatur: "));
  Serial.print(t1);
  Serial.print(F("°C  "));
  Serial.print(F("  Taupunkt: "));
  Serial.print(Taupunkt_1);
  Serial.println(F("°C  "));

  Serial.print("Sensor 2 -Aussen: " );
  Serial.print(F("Luftfeuchtigkeit: "));
  Serial.print(h2);
  Serial.print(F("%  Temperatur: "));
  Serial.print(t2);
  Serial.print(F("°C "));
  Serial.print(F("   Taupunkt: "));
  Serial.print(Taupunkt_2);
  Serial.println(F("°C  "));
  Serial.println();

  // Werteausgabe auf dem I2C-Display
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(F("I:"));// geändert vorher lcd.print(F("S1: "))
  lcd.print(t1); 
  lcd.write((uint8_t)0); // Sonderzeichen °C
  lcd.write(('C'));
  lcd.setCursor(9,0);
  lcd.write((uint8_t)1); // Sonderzeichen |
  lcd.print(h1);
  lcd.print(F("%"));
  
  lcd.setCursor(0,1);
  lcd.print(F("A:"));// geändert vorher lcd.print(F("S1: "))
  lcd.print(t2); 
  lcd.write((uint8_t)0); // Sonderzeichen °C
  lcd.write(('C'));
  lcd.setCursor(9,1);
  lcd.write((uint8_t)1); // Sonderzeichen |
  lcd.print(h2);
  lcd.print(F("%"));

  lcd.setCursor(0,2);
  lcd.print(F("TPI:"));
  lcd.print(Taupunkt_1); 
  lcd.setCursor(9,2);
  lcd.write((uint8_t)1); // Sonderzeichen |
  lcd.setCursor(10,2);
  lcd.print(F("TPA:"));
  lcd.print(Taupunkt_2); 

  
 //Taupunkt Differenz berechnen 
float DeltaTP = Taupunkt_1 - Taupunkt_2;

//Nach Taupunkt differenz + Hysterese schalten
if (DeltaTP > (SCHALTmin + HYSTERESE))trocknen = true;
if (DeltaTP < (SCHALTmin))trocknen = false;
if (t1 < TEMP1_min )trocknen = false;
if (t2 < TEMP2_min )trocknen = false;

if (trocknen == true)
{

  lcd.setCursor(18,1);
  lcd.print("<-"); 
  digitalWrite(DO_00, RELAIS_EIN); // Relais einschalten
  digitalWrite(DO_01, RELAIS_EIN); // Relais einschalten
  digitalWrite(DO_02, RELAIS_EIN); // Relais einschalten
  lcd.setCursor(9,3);
  lcd.write((uint8_t)1); // Sonderzeichen
  lcd.print(F("TPC L-EIN")); 

} else {
  lcd.setCursor(18,0);
  lcd.print("<-");                              
  digitalWrite(DO_00, RELAIS_AUS);
  digitalWrite(DO_01, RELAIS_AUS); // Relais einschalten
  digitalWrite(DO_02, RELAIS_AUS); // Relais einschalten 
  lcd.setCursor(9,3);
  lcd.write((uint8_t)1); // Sonderzeichen |// Relais ausschalten
  lcd.print(F("TPC L-AUS")); 
  
}

if (trocknen == false && h1 > h1_soll)
{
  digitalWrite(DO_03, RELAIS_EIN);
} else {
  digitalWrite(DO_03, RELAIS_AUS);
}


 pumpen(); //FunktionAufruf KondenAt abpumpen




//CHKIOBAD
if (isnan(h1) || isnan(t1) || h1 > 100 || h1 < 1 || t1 < -30 || t1 > 80 )  { //IOBAD SENSOR 1
      Serial.println(F("Fehler beim Auslesen vom 1. Sensor!"));
      fehler = true;
      
    }
   
  
     // Zeit um das Display zu lesen
  
      if (isnan(h2) || isnan(t2) || h2 > 100 || h2 < 1 || t2 < -30 || t2  > 80)  { //IOBAD SENSOR 2
        Serial.println(F("Fehler beim Auslesen vom 2. Sensor!"));
        fehler = true;
        
      } 










}

//----------------------------------------------------------LOOP ENDE

float taupunkt(float t, float r) {
  
float a, b;
  
  if (t >= 0) {
    a = 7.5;
    b = 237.3;
  } else if (t < 0) {
    a = 7.6;
    b = 240.7;
  }
  
  // Sättigungsdampfdruck in hPa
  float sdd = 6.1078 * pow(10, (a*t)/(b+t));
  
  // Dampfdruck in hPa
  float dd = sdd * (r/100);
  
  // v-Parameter
  float v = log10(dd/6.1078);
  
  // Taupunkttemperatur (°C)
  float tt = (b*v) / (a-v);
  return { tt };  
}


void pumpen() 
{ //definiere Funktion Pumpen

  bool NIVEAU_HOCH  = !digitalRead(DI_00);     //INPUT Schwimmer Oben   
  bool NIVEAU_NIEDR = !digitalRead(DI_01);     //INPUT Schwimmer Unten

 
  static byte StatePump = 0;                  //Status der Statemachine Pumpen, Lokale Variable, static - Arduino-Referenz
  static long timeout = 10000;               //Timeout Pumpen in ms, Lokale Variable
  static long StartZeit = 0;
 
  switch (StatePump){
 
    case 0:
    lcd.setCursor(0,3);
  lcd.print(F("NL P-AUS"));                                   // Init: Voll? Wenn ja Pumpe EIN und springe nach 1
      if (NIVEAU_HOCH && NIVEAU_NIEDR){                            // wenn oberer Pegel erreicht
        digitalWrite(DO_07, RELAIS_EIN);             // Pumpe ein
        StartZeit = millis();                      // Zeit merken
        StatePump = 1;lcd.setCursor(0,3);
  lcd.print(F("NH P-EIN")); 
                                   // nächster State: 1       
      }
    break;                                           //break
 
    case 1: 
    lcd.setCursor(0,3);
  lcd.print(F("NH P-EIN"));                                   	// Pumpen: Pumpe ist EIN, warte auf Niveau_Niedrig LOW
      if (!NIVEAU_HOCH && !NIVEAU_NIEDR){                           // wenn unterer Pegel erreicht
        digitalWrite(DO_07, RELAIS_AUS);              // Pumpe aus
        StatePump = 0;
        lcd.setCursor(0,3);
  lcd.print(F("NL P-AUS"));                            // nächster State: Init              
      }
      if(millis()-StartZeit > timeout){            // Wenn Timeout, setze Fehler case
        digitalWrite(DO_07, RELAIS_AUS);           // Pumpe aus
        digitalWrite(DO_08, HIGH);             	   // Fehler LED an
        StatePump = 2;                             // nächster Status: Fehler       
      }
    break;                                           //break
 
    case 2:
        lcd.setCursor(0,3);
  lcd.print(F("NF P-AUS"));				//in Fehler gehen     
}                                                                                            
     
 
     
  }// end switch