// 2025-08-18
// https://wokwi.com/projects/387286197708679169
// Reifeschrank MK 1.7
// Überwacht Temperatur und Feuchtigkeit in einem Getränkeschrank
// Inkludierte Geräte:
// Befeuchter, Entfeuchter, Kühlschrank, UVC Luftfilter, DHT Sensor, 16x2 LCD Anzeige, 8FachRelais
// Bei Abschnitt "Potentiometer" werden die Maximalen beschrifteten Werte eingegeben.
//Bedienung online:
// Regler Links - Temperatur (0 - 20°C)
// Regler Rechts - Luftfeuchtigkeit (70 - 95%)
// Auf der Tastatur "1" gedrückt halten um Werte einzulesen.
// Sensorwerte mit Klick auf den weißen Sensor link oben simulieren (DHT22)
// Luftbefeuchter kann nur aktiv sein, wenn gewünschter Kühlwert erreicht ist!
// Befeuchter arbeitet 20 Sek und dann 3 min Pause
#define DHTPin 8
#define FridgePin 9
#define HumPin 10
#define DeHumPin 6
#define UVCPin 7
#define checkPotPin 13
#define StatOKPin A5
#define PotTempPin A3 // Zieltemperatur kann bei jeden Displaywechsel neu kalibriert werden
#define PotHumPin A2 // dabei die Taste gedrückt halten und werte an den Potis stellen.
#define HumLed A0 //Befeuchter wird nicht über Stromzufuhr angesteuert. Dadurch braucht die Statusled einen eigenes Relais
//#define LCDPower A1 //Um das Display zu reseten ohne alles ausschalten zu müssen.
//Potentiometer
int lowestTemp = 0; // niedrigster Temperatur Wert des Potentiometers
int highestTemp = 20; //maximaler Wert des Reglers für Temperatur in Celsius
int lowestHum = 70; // niedrigster Temperatur Wert des Potentiometers
int highestHum = 95; // maximaler Wert des Regles für Luftfeuchtigkeit
int dTime = 3000; // wartezeit Zwischen den Anzeigen
//Bibliothek für das LCD Display
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
//Bibliothek für den Temp und Feuchtesensor
#include <DHT.h>
#define DHTTYPE DHT22
DHT dht(DHTPin, DHTTYPE);
float aktTemp;
float aktHum;
//Variablen für die eingabe der Potentiometer
float goalTemp;
float goalHum;
// Variablen für den Betriebsstatus der Geräte
String FridgeStat = "Aus";
String HumStat = "Pause"; //ACHTUNG: " AUS", " EIN" "Pause" aufgrund der Anzeige
String DeHumStat = "Aus";
String UVCStat = "Aus";
//Variablen für die Zeitmessung der Geräte
unsigned long long alltime;
unsigned long lasttime;
unsigned long allesoktime;
unsigned long allFridgetime;
unsigned long startFridgetime;
unsigned long endFridgetime;
unsigned long allHumtime;
unsigned long startHumtime;
unsigned long endHumtime;
unsigned long allDeHumtime;
unsigned long startDeHumtime;
unsigned long endDeHumtime;
unsigned long allUVCtime;
unsigned long startUVCtime;
unsigned long endUVCtime;
bool checkFridge = true;
bool checkHum = true;
bool checkDeHum = true;
bool checkUVC = true;
byte oben0[] = {
B00001,
B00010,
B00100,
B01000,
B11000,
B10101,
B10011,
B10001
};
byte oben1[] = {
B11111,
B01000,
B01000,
B10000,
B10000,
B00100,
B01010,
B00000
};
byte oben2[] = {
B11111,
B00010,
B00010,
B00001,
B00001,
B00100,
B01010,
B00000
};
byte oben3[] = {
B10000,
B01000,
B00100,
B00010,
B00011,
B10101,
B11001,
B10001
};
byte unten0[] = {
B10000,
B10000,
B10000,
B01000,
B00100,
B00010,
B00001,
B00000
};
byte unten1[] = {
B00111,
B01000,
B01010,
B01000,
B00111,
B00000,
B00000,
B11111
};
byte unten2[] = {
B11100,
B00010,
B01010,
B00010,
B11100,
B00000,
B00000,
B11111
};
byte unten3[] = {
B00001,
B00001,
B00001,
B00010,
B00100,
B01000,
B10000,
B00000
};
void setup() {
lcd.createChar(0, oben0);
lcd.createChar(1, oben1);
lcd.createChar(2, oben2);
lcd.createChar(3, oben3);
lcd.createChar(4, unten0);
lcd.createChar(5, unten1);
lcd.createChar(6, unten2);
lcd.createChar(7, unten3);
pinMode(checkPotPin, INPUT_PULLUP);
pinMode(DHTPin, INPUT);
pinMode(PotTempPin, INPUT);
pinMode(PotHumPin, INPUT);
digitalWrite(StatOKPin, HIGH);
pinMode(StatOKPin, OUTPUT);
digitalWrite(FridgePin, HIGH);
pinMode(FridgePin, OUTPUT);
digitalWrite(HumPin, HIGH);
pinMode(HumPin, OUTPUT);
digitalWrite(HumLed, HIGH);
pinMode(HumLed, OUTPUT);
digitalWrite(DeHumPin, HIGH);
pinMode(DeHumPin, OUTPUT);
digitalWrite(UVCPin, HIGH);
pinMode(UVCPin, OUTPUT);
//digitalWrite(LCDPower, LOW); //LOW = aktiv
//pinMode(LCDPower, OUTPUT);
dht.begin();
Serial.begin(9600);
lcd.begin(16, 2);
abfrage(); //diese Abfrage ist wichtig um bei einem Stromausfall Werte bekannt zugeben
}
//zeitumrechner funktion
//wandelt 2 angaben von millisekunden (alt & neu, oder klein & groß)
// in das Format "___D __H __M"
String worktime(unsigned long long x) {
int wertD = x / 86400000;
//ich habe keine Ahnung warum, aber Stunden und Minuten wurden nur korrekt ausgegeben
//nach dem ich die Milliangabe in unsigned long geändert habe....
unsigned long y = x;
int wertH = (y - (wertD * 86400000)) / 3600000;
int wertM = ((y - (wertD * 86400000)) - (wertH * 3600000)) / 60000;
String worktimestring = " ";
if (wertD < 100 && wertD > 10) worktimestring += " ";
if (wertD < 10) worktimestring += " ";
worktimestring += String(wertD) + "D ";
if (wertH < 10) worktimestring += " ";
worktimestring += String(wertH) + "H ";
if (wertM < 10) worktimestring += " ";
worktimestring += String(wertM) + "M";
return worktimestring;
}
float abfrage() {
goalTemp = float(map(analogRead(PotTempPin), 0, 1023, lowestTemp * 10, highestTemp * 10)) / 10.0;
goalHum = float(map(analogRead(PotHumPin), 0, 1023, lowestHum * 10, highestHum * 10)) / 10.0;
}
float setnewgoal() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Neue Werte:");
while (digitalRead(checkPotPin) == LOW) {
abfrage();
Serial.println("Werte einlesen: " + String(goalTemp) + " C - " + String(goalHum) + " %");
lcd.setCursor(0, 1);
lcd.print(" ");
if (goalTemp < 10) lcd.print(" ");
lcd.print(goalTemp, 1);
lcd.print(" C ");
if (goalHum < 10) lcd.print(" ");
lcd.print(goalHum, 1);
lcd.print(" %");
delay(500);
}
lcd.clear();
delay(500);
lcd.begin(16, 2);
delay(500);
Serial.println("Werte gesetzt: " + String(goalTemp) + " C - " + String(goalHum) + " %");
lcd.setCursor(0, 0);
lcd.print("Werte gesetzt:");
lcd.setCursor(0, 1);
lcd.print(" ");
if (goalTemp < 10) lcd.print(" ");
lcd.print(goalTemp, 1);
lcd.print(" C ");
if (goalHum < 10) lcd.print(" ");
lcd.print(goalHum, 1);
lcd.print(" %");
delay(dTime);
}
float show_temp_hum() {
// Anzeige der aktuellen Temperatur
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Temperatur:");
if (aktTemp < 10 && aktTemp > 0) lcd.print(" ");
lcd.print(aktTemp, 1);
lcd.print("C");
lcd.setCursor(0, 1);
lcd.print("Feuchte: ");
if (aktHum < 10) lcd.print(" ");
lcd.print(aktHum, 1);
lcd.print("%");
delay(dTime);
}
float on_or_off() {
// Abfrage der aktuellen Temperatur und Feuchtigkeit
// Serielle Ausgabe der Statuswerte
aktTemp = dht.readTemperature();
delay(200);
aktHum = dht.readHumidity();
Serial.println(String("----------------------------------------"));
Serial.println(String(" Ziel-Werte: " + String(goalTemp) + " C - " + String(goalHum) + " %"));
Serial.println(String("Aktuelle Werte: " + String(aktTemp) + " C - " + String(aktHum) + " %"));
Serial.println(String(" allesoktime: " + worktime(allesoktime)));
Serial.println(String(" alltime: " + worktime(alltime)));
Serial.println(String(" allDeHumtime: " + worktime(allDeHumtime)));
Serial.println(String(" allHumtime: " + worktime(allHumtime)));
Serial.println(String(" allFridgetime: " + worktime(allFridgetime)));
Serial.println(String(" FridgeStat: " + FridgeStat));
Serial.println(String(" HumStat: " + HumStat));
Serial.println(String(" DeHumStat: " + DeHumStat));
Serial.println(String(" UVCStat: " + UVCStat));
// abklären des Overflow von millis nach 49D 17H 2M 47S um die Zeiten +- 10 Sek mitzurechnen
if (lasttime < millis()) {
alltime = alltime + (millis() - lasttime);
lasttime = millis();
} else {
lasttime = millis();
startFridgetime = 0;
startHumtime = 0;
startDeHumtime = 0;
startUVCtime = 0;
}
//Abklären ob etwas eingeschaltet werden muss
//
//Kühlung
if (checkFridge == true) {
if (aktTemp >= goalTemp + 2) {
digitalWrite(FridgePin, LOW);
FridgeStat = "Ein";
startFridgetime = millis();
checkFridge = false;
Serial.println("Kühlung Ein");
}
}
if (checkFridge == false) {
if (aktTemp <= goalTemp) {
digitalWrite(FridgePin, HIGH);
FridgeStat = "Aus";
checkFridge = true;
endFridgetime = millis();
allFridgetime = allFridgetime + (endFridgetime - startFridgetime);
Serial.println("Kühlung Aus");
}
}
delay(200);
//20 Sekunden befeuchten und dann gesperrt für 3 min
//Befeuchter starten sobald Ziel-Temperatur erreicht wurde und Feuchte Zielwert minus 5 ist
// und mindestens 5 min Betriebszeit seit letzten befeuchten.
//Befeuchter beenden sobald Feuchte Zielwert minus 2 erreicht ist, da angenommen wird das es
// noch etwas nachwirkt.
if (checkHum == true && millis() - endHumtime >= 180000) { //Sperrzeit in Milisekunden
if (FridgeStat == "Aus" && aktHum <= (goalHum - 5)) {
//digitalWrite(StatOKPin, HIGH);
digitalWrite(HumPin, LOW);
digitalWrite(HumLed, LOW);
//HumStat = " Ein";
startHumtime = millis();
Serial.println("Befeuchtung Ein");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Befeuchten...");
lcd.setCursor(0, 1);
lcd.print(" 20 Sekunden");
delay(20000); //Befeuchtungsdauer in Milisekunden
lcd.clear();
digitalWrite(HumPin, HIGH);
HumStat = "Pause";
endHumtime = millis();
allHumtime = allHumtime + (endHumtime - startHumtime);
Serial.println("Befeuchtung Aus");
Serial.println("Befeuchter auf Pause");
}
if ( aktHum >= (goalHum - 1)) {
HumStat = " Aus";
digitalWrite(HumLed, HIGH);
}
}
/* if (checkHum == false && millis() - endHumtime >= 120000) {
if (aktHum >= (goalHum - 2) || FridgeStat == "Ein") {
digitalWrite(HumLed, HIGH);
HumStat = " Aus";
checkHum = true;
Serial.println("Befeuchter Aus");
} else {
checkHum = true;
}
}
*/
delay(200);
//Entfeuchter starten sobald Zielfeuchte +7 ist
if (checkDeHum == true) {
if (aktHum >= (goalHum + 7)) {
digitalWrite(DeHumPin, LOW);
DeHumStat = "Ein";
checkDeHum = false;
startDeHumtime = millis();
Serial.println("Entfeuchter Ein");
}
}
//Entfeuchter stoppen sobald Zielfeuchte erreicht ist
if (checkDeHum == false) {
if (aktHum <= goalHum) {
digitalWrite(DeHumPin, HIGH);
DeHumStat = "Aus";
checkDeHum = true;
endDeHumtime = millis();
allDeHumtime = allDeHumtime + (endDeHumtime - startDeHumtime);
Serial.println("Entfeuchter Aus");
}
}
delay(200);
//UVC Luftreiniger und Luftumwälzung 0,2 Kubikmeter in 1 min
//Macht 5 min Pause, läuft dann 60 Sekunden
if (checkUVC == true) {
if (millis() - endUVCtime >= 300000) {
digitalWrite(UVCPin, LOW);
UVCStat = "Ein";
checkUVC = false;
startUVCtime = millis();
Serial.println("Luftfilter Ein");
}
}
if (checkUVC == false) {
if (millis() - startUVCtime >= 60000) {
digitalWrite(UVCPin, HIGH);
UVCStat = "Aus";
endUVCtime = millis();
allUVCtime = allUVCtime + (endUVCtime - startUVCtime);
checkUVC = true;
Serial.println("Luftfilter Aus");
}
}
delay(200);
//Wenn alle Werte passen, also Kühlen/Feuchte/Defeuchte AUS sind, kommt grünes Licht
if (HumStat == " Aus" && DeHumStat == "Aus" && FridgeStat == "Aus") {
digitalWrite(StatOKPin, LOW);
Serial.println("Alles OK");
} else {
digitalWrite(StatOKPin, HIGH);
}
}
void loop() {
// Titel und Logo
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Reifeschrank");
lcd.write(byte(0));
lcd.write(byte(1));
lcd.write(byte(2));
lcd.write(byte(3));
lcd.setCursor(0, 1);
lcd.print("MK 1.6 ");
lcd.write(byte(4));
lcd.write(byte(5));
lcd.write(byte(6));
lcd.write(byte(7));
delay(dTime);
//Neue Werte abfragen wenn die Taste beim Wechsel der Anzeigen gedrückt ist
if (digitalRead(checkPotPin) == LOW) {
setnewgoal();
}
//Anzeigen der gewünschten Temperatur
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Ziel-Temperatur:");
lcd.setCursor(0, 1);
lcd.print(" ");
if (goalTemp < 10) lcd.print(" ");
lcd.print(goalTemp, 1);
lcd.print(" C");
delay(dTime);
//Neue Werte abfragen wenn die Taste beim Wechsel der Anzeigen gedrückt ist
if (digitalRead(checkPotPin) == LOW) {
setnewgoal();
}
//Anzeigen der gewünschten Feuchtigkeit
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Ziel-Feuchte:");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.print(goalHum, 1);
lcd.print(" %");
delay(dTime);
//Neue Werte abfragen wenn die Taste beim Wechsel der Anzeigen gedrückt ist
if (digitalRead(checkPotPin) == LOW) {
setnewgoal();
}
// Read temperature as Fahrenheit (isFahrenheit = true)
// fahrenheit = dht.readTemperature(true);
// Check if any reads failed and exit early (to try again)
while (isnan(aktHum) || isnan(aktTemp)) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Sensor Fehler!");
lcd.setCursor(0, 1);
lcd.print("NOT-AUS");
digitalWrite(UVCPin, HIGH);
digitalWrite(FridgePin, HIGH);
digitalWrite(DeHumPin, HIGH);
digitalWrite(HumPin, HIGH);
while (isnan(aktHum) || isnan(aktTemp)) {
digitalWrite(StatOKPin, LOW);
delay(500);
digitalWrite(StatOKPin, HIGH);
delay(500);
aktTemp = dht.readTemperature();
delay(200);
aktHum = dht.readHumidity();
}
}
on_or_off();
show_temp_hum();
//Neue Werte abfragen wenn die Taste beim Wechsel der Anzeigen gedrückt ist
if (digitalRead(checkPotPin) == LOW) {
setnewgoal();
}
//Display Anzeigen
// Anzeige der Betriebsstatus Seit letzten Reset
// mitzählen von "alltime" passiert in der funktion on_or_off
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Letzter Reset:");
lcd.setCursor(0, 1);
lcd.print(worktime(alltime));
delay(dTime);
//Neue Werte abfragen wenn die Taste beim Wechsel der Anzeigen gedrückt ist
if (digitalRead(checkPotPin) == LOW) {
setnewgoal();
}
on_or_off();
show_temp_hum();
// Kühlung
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(String("K\365hlung: " + FridgeStat));
lcd.setCursor(0, 1);
//diese if Frage dient zum paralellen mitanzeigen der Betriebszeit,
// während ein gerät eingeschaltet ist.
if (FridgeStat == "Ein") {
lcd.print(worktime(allFridgetime + (millis() - startFridgetime)));
} else {
lcd.print(worktime(allFridgetime));
}
delay(dTime);
//Neue Werte abfragen wenn die Taste beim Wechsel der Anzeigen gedrückt ist
if (digitalRead(checkPotPin) == LOW) {
setnewgoal();
}
on_or_off();
show_temp_hum();
// Befeuchter
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(String("Befeuchter:" + HumStat));
lcd.setCursor(0, 1);
if (HumStat == " Ein") {
lcd.print(worktime(allHumtime + (millis() - startHumtime)));
} else {
lcd.print(worktime(allHumtime));
}
delay(dTime);
//Neue Werte abfragen wenn die Taste beim Wechsel der Anzeigen gedrückt ist
if (digitalRead(checkPotPin) == LOW) {
setnewgoal();
}
on_or_off();
show_temp_hum();
// Entfeuchter
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(String("Entfeuchter: " + DeHumStat));
lcd.setCursor(0, 1);
if (DeHumStat == "Ein") {
lcd.print(worktime(allDeHumtime + (millis() - startDeHumtime)));
} else {
lcd.print(worktime(allDeHumtime));
}
delay(dTime);
//Neue Werte abfragen wenn die Taste beim Wechsel der Anzeigen gedrückt ist
if (digitalRead(checkPotPin) == LOW) {
setnewgoal();
}
on_or_off();
show_temp_hum();
// UVC Luftreiniger
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(String("Luftfilter: " + UVCStat));
lcd.setCursor(0, 1);
if (UVCStat == "Ein") {
lcd.print(worktime(allUVCtime + (millis() - startUVCtime)));
} else {
lcd.print(worktime(allUVCtime));
}
delay(dTime);
//Neue Werte abfragen wenn die Taste beim Wechsel der Anzeigen gedrückt ist
if (digitalRead(checkPotPin) == LOW) {
setnewgoal();
}
on_or_off();
show_temp_hum();
//Alles OK Gesamt Zeit:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(String("Alles OK Gesamt:"));
lcd.setCursor(0, 1);
if (FridgeStat == "Aus" && HumStat == " Aus" && DeHumStat == "Aus") {
allesoktime = alltime - allDeHumtime - allHumtime - allFridgetime;
lcd.print(worktime(allesoktime));
} else {
lcd.print(worktime(allesoktime));
}
delay(dTime);
//Neue Werte abfragen wenn die Taste beim Wechsel der Anzeigen gedrückt ist
if (digitalRead(checkPotPin) == LOW) {
setnewgoal();
}
on_or_off();
show_temp_hum();
}