Änderungen im Setup Programm startet neu

Ich möchte werte von Sensoren in eine Datei schreiben und die Überschrift zunächst im “Setup” schreiben.

#include <RTClib.h>
/* für den Kartenleser */
#include <SD.h>
const int chipSelect = 10;
// File für SD Karte
File dataFile;
// DS3231 RTC Modul
RTC_DS3231 rtc;

String timestamp;

void setup() {
  Serial.begin(9600);

  // SD-Karte initialisieren
  if (!SD.begin(chipSelect)) {
    Serial.println("SD Karte konnte nicht initialisiert werden.");
    return;
  }
  Serial.println("SD Karte bereit.");

  // Neue Datei erstellen oder bestehende öffnen
  dataFile = SD.open("daten.csv", FILE_WRITE);

  if (dataFile) {
    dataFile.println("Datei erstellen im Setupteil des Sketches");
    //  dataFile.println(millis());        // Zeile kann entfallen war zum Test da Programm immer neu startete
    dataFile.print("Datum,Uhrzeit,"),  // Überschrift der Tabelle
    dataFile.print("Temp innen(C),Luftfeuchtigkeit (%),Taupunkt (C),Luftdruck (hPa),");
    dataFile.print("Temp außen(C),Luftfeuchtigkeit (%),Taupunkt (C),");
    dataFile.println("Lüfter ein,Lüfter aus,Lüfterzeit");
    dataFile.close();
    Serial.println("Erstellen der Datei");
  } else {
    Serial.println("Fehler beim Erstellen der Datei.");
  }
  // DS3231 initialisieren
  if (!rtc.begin()) {
    Serial.println("Fehler beim Initialisieren des DS3231 RTC Moduls!");
    while (1)
      ;
  }
}
void loop() {
  stempel();          // Zeitstempel erstellen
  Karte_schreiben();  // Daten auf SD Karte schreiben

  Serial.println(timestamp);
  delay(1000);
}
void stempel() {

  // Datum und Uhrzeit vom RTC Modul abrufen
  DateTime now = rtc.now();

  // Datum und Uhrzeit formatieren (z.B. "2025-09-15 14:30:01")
  timestamp = String(now.day()) + "-" + String(now.month()) + "-" + String(now.year()) + " " + String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second());
}
void Karte_schreiben() {
  // Neue Datei erstellen oder bestehende öffnen
  dataFile = SD.open("daten.csv", FILE_WRITE);

  if (dataFile) {
    dataFile.println(timestamp);
    dataFile.close();
  } else {
    Serial.println("Fehler beim Öffnen der Datei.");
  }
}

das geht

die zeilen des Setup in ein zweites Program geht nicht ???

in ein laufendes Programm kopiert geht nicht ???
```cpp

// Dieser Code benötigt zwingend die folgenden Libraries:
#include "DHT.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <avr/wdt.h>
#include <RTClib.h>
/* für den Kartenleser */
#include <SD.h>
const int chipSelect = 10;
// File für SD Karte
File dataFile;
// DS3231 RTC Modul
RTC_DS3231 rtc;

String timestamp;


#define RELAIPIN 4  // Anschluss des Lüfter-Relais
#define DHTPIN_1 5  // Datenleitung für den DHT-Sensor 1 (innen)
#define DHTPIN_2 6  // Datenleitung für den DHT-Sensor 2 (außen)

#define RELAIS_EIN LOW
#define RELAIS_AUS HIGH
bool rel;

#define DHTTYPE_1 DHT11  // DHT 22 geändert DHT11
#define DHTTYPE_2 DHT11  // DHT 22 geändert DHT11

// *******  Korrekturwerte der einzelnen Sensorwerte  *******
#define Korrektur_t_1 0  // Korrekturwert Innensensor Temperatur
#define Korrektur_t_2 0  // Korrekturwert Außensensor Temperatur
#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 10.0   // Minimale Innentemperatur, bei der die Lüftung aktiviert wird
#define TEMP2_min -10.0  // Minimale Außentemperatur, bei der die Lüftung aktiviert wird

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

bool fehler = true;

void setup() {
  wdt_enable(WDTO_8S);  // Watchdog timer auf 8 Sekunden stellen

  pinMode(RELAIPIN, OUTPUT);           // Relaispin als Output definieren
  digitalWrite(RELAIPIN, RELAIS_AUS);  // Relais ausschalten

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

  lcd.init();
  lcd.backlight();
  lcd.setCursor(2, 0);
  lcd.print(F("Teste 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();


    // SD-Karte initialisieren
  if (!SD.begin(chipSelect)) {
    Serial.println("SD Karte konnte nicht initialisiert werden.");
    return;
  }
  Serial.println("SD Karte bereit.");

  // Neue Datei erstellen oder bestehende öffnen
  dataFile = SD.open("daten.csv", FILE_WRITE);

  if (dataFile) {
    dataFile.println("Datei erstellen im Setupteil des Sketches");
    dataFile.println(millis());        // Zeile kann entfallen war zum Test da Programm immer neu startete
    dataFile.print("Datum,Uhrzeit,"),  // Überschrift der Tabelle
    dataFile.print("Temp innen(C),Luftfeuchtigkeit (%),Taupunkt (C),Luftdruck (hPa),");
    dataFile.print("Temp außen(C),Luftfeuchtigkeit (%),Taupunkt (C),");
    dataFile.println("Lüfter ein,Lüfter aus,Lüfterzeit");
    dataFile.close();
    Serial.println("Erstellen der Datei");
  } else {
    Serial.println("Fehler beim Erstellen der Datei.");
  }
  // DS3231 initialisieren
  if (!rtc.begin()) {
    Serial.println("Fehler beim Initialisieren des DS3231 RTC Moduls!");
    while (1)
      ;
  }
}

void loop() {

  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 = dht1.readHumidity() + Korrektur_h_2;     // Außenluftfeuchtigkeit auslesen und unter „h2“ speichern
  float t2 = dht1.readTemperature() + Korrektur_t_2;  // Außentemperatur auslesen und unter „t2“ speichern

  if (fehler == true)  // Prüfen, ob gültige Werte von den Sensoren kommen
  {
    fehler = false;
    if (isnan(h1) || isnan(t1) || h1 > 100 || h1 < 1 || t1 < -40 || 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 < -40 || t2 > 80) {
      Serial.println(F("Fehler beim Auslesen vom 2. Sensor!"));
      Serial.println(h2);
      Serial.println(t2);
      Serial.println(h1);
      Serial.println(t1);
      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(RELAIPIN, RELAIS_AUS);  // Relais ausschalten
    lcd.setCursor(0, 3);
    lcd.print(F("CPU Neustart....."));
    while (1)
      ;  // Endlosschleife um das Display zu lesen und die CPU durch den Watchdog neu zu starten
  }
  wdt_reset();  // Watchdog zurücksetzen

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

  // Werteausgabe auf Serial Monitor
  Serial.print(F("Sensor-1: "));
  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: ");
  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("S1: "));
  lcd.print(t1);
  lcd.write((uint8_t)0);  // Sonderzeichen °C
  lcd.write(('C'));
  lcd.write((uint8_t)1);  // Sonderzeichen |
  lcd.print(h1);
  lcd.print(F(" %"));

  lcd.setCursor(0, 1);
  lcd.print(F("S2: "));
  lcd.print(t2);
  lcd.write((uint8_t)0);  // Sonderzeichen °C
  lcd.write(('C'));
  lcd.write((uint8_t)1);  // Sonderzeichen |
  lcd.print(h2);
  lcd.print(F(" %"));

  lcd.setCursor(0, 2);
  lcd.print(F("Taupunkt 1: "));
  lcd.print(Taupunkt_1);
  lcd.write((uint8_t)0);  // Sonderzeichen °C
  lcd.write(('C'));

  lcd.setCursor(0, 3);
  lcd.print(F("Taupunkt 2: "));
  lcd.print(Taupunkt_2);
  lcd.write((uint8_t)0);  // Sonderzeichen °C
  lcd.write(('C'));

  delay(6000);  // Zeit um das Display zu lesen
  wdt_reset();  // Watchdog zurücksetzen

  lcd.clear();
  lcd.setCursor(0, 0);

  float DeltaTP = Taupunkt_1 - Taupunkt_2;

  if (DeltaTP > (SCHALTmin + HYSTERESE)) rel = true;
  if (DeltaTP < (SCHALTmin)) rel = false;
  if (t1 < TEMP1_min) rel = false;
  if (t2 < TEMP2_min) rel = false;

  if (rel == true) {
    digitalWrite(RELAIPIN, RELAIS_EIN);  // Relais einschalten
    lcd.print(F("Lueftung AN"));
  } else {
    digitalWrite(RELAIPIN, RELAIS_AUS);  // Relais ausschalten
    lcd.print(F("Lueftung AUS"));
  }

  lcd.setCursor(0, 1);
  lcd.print("Delta TP: ");
  lcd.print(DeltaTP);
  lcd.write((uint8_t)0);  // Sonderzeichen °C
  lcd.write('C');

  delay(4000);  // Wartezeit zwischen zwei Messungen
  wdt_reset();  // Watchdog zurücksetzen
}

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 software_Reset()  // Startet das Programm neu, nicht aber die Sensoren oder das LCD
{
  asm volatile("  jmp 0");
}
```

Was geht den nicht?

offensichtlich wird das Programm immer wieder gestartet

ich schreibe die “millis” in die Tabelle

1143
Datum Uhrzeit Temp innen(C)
Datei erstellen im Setupteil des Sketches
1143
Datum Uhrzeit Temp innen(C)
Datei erstellen im Setupteil des Sketches
1143
Datum Uhrzeit Temp innen(C)
Datei erstellen im Setupteil des Sketches
1164
Datum Uhrzeit Temp innen(C)
Datei erstellen im Setupteil des Sketches
Datum Uhrzeit Temp innen(C)
15-9-2025 10:52:4
Datei erstellen im Setupteil des Sketches
Datum Uhrzeit Temp innen(C)
15-9-2025 10:52:6
15-9-2025 10:52:7

Was kommt den auf der Seriellen Konsole?

Baue zum testen das mal aus

Wozu braucht man so was?

Da kann der WD zuschlagen

Habe die Zeilen auskommentiert #post3

Teste Sensoren..

SD Karte bereit.

Erstellen der DateiTeste Sensoren..

Teste Sensoren..

hab dann noch

// DS3231 initialisieren

// if (!rtc.begin()) { Serial.println("Fehler beim Initialisieren des DS3231 RTC Moduls!");

// while (1)

auskommentiert

Auf was für einem Controller soll das laufen?.

das Programm lief auf einem UNO

ohne SD karte

Dafür ist der speicher momentan knapp.
AAABER:

Ändere mal das Komma am Ende in ein Semikolon!

Kompletter Code, ohne Warnung, dass der Speicher knapp wird:


// Dieser Code benötigt zwingend die folgenden Libraries:
#include "DHT.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <avr/wdt.h>
#include <RTClib.h>
/* für den Kartenleser */
#include <SD.h>
const int chipSelect = 10;
// File für SD Karte
File dataFile;
// DS3231 RTC Modul
RTC_DS3231 rtc;

String timestamp;

#define RELAIPIN 4  // Anschluss des Lüfter-Relais
#define DHTPIN_1 5  // Datenleitung für den DHT-Sensor 1 (innen)
#define DHTPIN_2 6  // Datenleitung für den DHT-Sensor 2 (außen)

#define RELAIS_EIN LOW
#define RELAIS_AUS HIGH
bool rel;

#define DHTTYPE_1 DHT11  // DHT 22 geändert DHT11
#define DHTTYPE_2 DHT11  // DHT 22 geändert DHT11

// *******  Korrekturwerte der einzelnen Sensorwerte  *******
#define Korrektur_t_1 0  // Korrekturwert Innensensor Temperatur
#define Korrektur_t_2 0  // Korrekturwert Außensensor Temperatur
#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 10.0   // Minimale Innentemperatur, bei der die Lüftung aktiviert wird
#define TEMP2_min -10.0  // Minimale Außentemperatur, bei der die Lüftung aktiviert wird

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

bool fehler = true;

void setup()
{
  wdt_enable(WDTO_8S);  // Watchdog timer auf 8 Sekunden stellen
  pinMode(RELAIPIN, OUTPUT);           // Relaispin als Output definieren
  digitalWrite(RELAIPIN, RELAIS_AUS);  // Relais ausschalten
  Serial.begin(9600);  // Serielle Ausgabe, falls noch kein LCD angeschlossen ist
  Serial.println(F("Teste Sensoren.."));
  lcd.init();
  lcd.backlight();
  lcd.setCursor(2, 0);
  lcd.print(F("Teste 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();

  // SD-Karte initialisieren
  if (!SD.begin(chipSelect))
  {
    Serial.println(F("SD Karte konnte nicht initialisiert werden."));
    return;
  }

  Serial.println(F("SD Karte bereit."));
  // Neue Datei erstellen oder bestehende öffnen
  dataFile = SD.open("daten.csv", FILE_WRITE);

  if (dataFile)
  {
    dataFile.println(F("Datei erstellen im Setupteil des Sketches"));
    dataFile.println(millis());        // Zeile kann entfallen war zum Test da Programm immer neu startete
    dataFile.print(F("Datum,Uhrzeit,"));  // Überschrift der Tabelle
    dataFile.print(F("Temp innen(C),Luftfeuchtigkeit (%),Taupunkt (C),Luftdruck (hPa),"));
    dataFile.print(F("Temp außen(C),Luftfeuchtigkeit (%),Taupunkt (C),"));
    dataFile.println(F("Lüfter ein,Lüfter aus,Lüfterzeit"));
    dataFile.close();
    Serial.println(F("Erstellen der Datei"));
  }
  else
  {
    Serial.println(F("Fehler beim Erstellen der Datei."));
  }

  // DS3231 initialisieren
  if (!rtc.begin())
  {
    Serial.println(F("Fehler beim Initialisieren des DS3231 RTC Moduls!"));

    while (1)
      ;
  }
}

void loop()
{
  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 = dht1.readHumidity() + Korrektur_h_2;     // Außenluftfeuchtigkeit auslesen und unter „h2“ speichern
  float t2 = dht1.readTemperature() + Korrektur_t_2;  // Außentemperatur auslesen und unter „t2“ speichern

  if (fehler == true)  // Prüfen, ob gültige Werte von den Sensoren kommen
  {
    fehler = false;

    if (isnan(h1) || isnan(t1) || h1 > 100 || h1 < 1 || t1 < -40 || 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 < -40 || t2 > 80)
    {
      Serial.println(F("Fehler beim Auslesen vom 2. Sensor!"));
      Serial.println(h2);
      Serial.println(t2);
      Serial.println(h1);
      Serial.println(t1);
      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(RELAIPIN, RELAIS_AUS);  // Relais ausschalten
    lcd.setCursor(0, 3);
    lcd.print(F("CPU Neustart....."));

    while (1)
      ;  // Endlosschleife um das Display zu lesen und die CPU durch den Watchdog neu zu starten
  }

  wdt_reset();  // Watchdog zurücksetzen
  //**** Taupunkte errechnen********
  float Taupunkt_1 = taupunkt(t1, h1);
  float Taupunkt_2 = taupunkt(t2, h2);
  // Werteausgabe auf Serial Monitor
  Serial.print(F("Sensor-1: "));
  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(F("Sensor-2: "));
  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("S1: "));
  lcd.print(t1);
  lcd.write((uint8_t)0);  // Sonderzeichen °C
  lcd.write(('C'));
  lcd.write((uint8_t)1);  // Sonderzeichen |
  lcd.print(h1);
  lcd.print(F(" %"));
  lcd.setCursor(0, 1);
  lcd.print(F("S2: "));
  lcd.print(t2);
  lcd.write((uint8_t)0);  // Sonderzeichen °C
  lcd.write(('C'));
  lcd.write((uint8_t)1);  // Sonderzeichen |
  lcd.print(h2);
  lcd.print(F(" %"));
  lcd.setCursor(0, 2);
  lcd.print(F("Taupunkt 1: "));
  lcd.print(Taupunkt_1);
  lcd.write((uint8_t)0);  // Sonderzeichen °C
  lcd.write(('C'));
  lcd.setCursor(0, 3);
  lcd.print(F("Taupunkt 2: "));
  lcd.print(Taupunkt_2);
  lcd.write((uint8_t)0);  // Sonderzeichen °C
  lcd.write(('C'));
  delay(6000);  // Zeit um das Display zu lesen
  wdt_reset();  // Watchdog zurücksetzen
  lcd.clear();
  lcd.setCursor(0, 0);
  float DeltaTP = Taupunkt_1 - Taupunkt_2;

  if (DeltaTP > (SCHALTmin + HYSTERESE))
  {
    rel = true;
  }

  if (DeltaTP < (SCHALTmin))
  {
    rel = false;
  }

  if (t1 < TEMP1_min)
  {
    rel = false;
  }

  if (t2 < TEMP2_min)
  {
    rel = false;
  }

  if (rel == true)
  {
    digitalWrite(RELAIPIN, RELAIS_EIN);  // Relais einschalten
    lcd.print(F("Lueftung AN"));
  }
  else
  {
    digitalWrite(RELAIPIN, RELAIS_AUS);  // Relais ausschalten
    lcd.print(F("Lueftung AUS"));
  }

  lcd.setCursor(0, 1);
  lcd.print(F("Delta TP: "));
  lcd.print(DeltaTP);
  lcd.write((uint8_t)0);  // Sonderzeichen °C
  lcd.write('C');
  delay(4000);  // Wartezeit zwischen zwei Messungen
  wdt_reset();  // Watchdog zurücksetzen
}

float taupunkt(float t, float r)
{
  float a = 0, b = 0;;

  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 software_Reset()  // Startet das Programm neu, nicht aber die Sensoren oder das LCD
{
  asm volatile("  jmp 0");
}

Ich würde in jedem Fall auf STRING verzichten!!

@my_xy_projekt

Vielen Dank das scheint zu gehen

Was ich sehe ist das die print/println Anweisungen mit (F(“Text”) erweitert wurden.

Warum nicht auch die print/println Anweisungen mit (Variablen) ?

Was hat das für einen Sinn ?

Mehr zum F() Makro findest du evtl. unter __FlashstringHelper.

Das erlaubt, feste Texte (direkt aus dem PROGMEM = Flash) auszugeben, ohne eine Kopie davon im RAM zu halten, der auf kleinen avr-Arduinos doch sehr begrenzt ist.
Bei Variablen, die sowieso im RAM liegen, geht das natürlich nicht.

String Objekte , und dann noch ein + , um zwei Strings zu einem dritten zusammenzukopieren, sind tödliche Speicherfresser. Bedenke, dass die SD Library allein schon mindestens den halben RAM eines atmega328 braucht.

Und dass RAM-Mangel verschiedenste Ausprägungen haben kann, wobei ein RESET noch das harmloseste weil am sichersten erkennbare Verhalten ist.

Für dein Vorhaben das schlimmste was es gibt ausgesucht, meine den DHT11.
Kleine Liste von Temp, Feuchte Sensoren,

  1. DHT11, +-5%Rh, +-2°C, Single Wire
  2. DHT22 , +-2%Rh, +-0,5°C, Single Wire
  3. HTU21D, 2%Rh, +-0.3°C, I²C
  4. SHT21, +-2%Rh, +-0,5°C, I²C
  5. BME280, +-3%RH, +-0,5°C I²C dazu noch Luftdruck Messung

Also der DHT11 ist nicht das Gelbe vom EI für dein Vorhaben, zu dem gegen das was die schreiben im Datenblatt Altert sehr schnell, meine 2 haben nur 1J gehalten wenn es geht um Rh und sind sehr empfindlich wen es geht um hohe Luftfeuchtigkeit.

delay(4000);  // Wartezeit zwischen zwei Messungen, 

DHT11 hat Response Time vom 6Sek, sonst kann, darf er dir falsche Werte liefern. Nur wo zu alle 4Sek messen so schnell ändern sich die werte Innen und Außen nicht.
Eigentlich ist nicht mein Projekt, jeder macht wie er meint, will, viel Spaß dabei .

Wurde ESP866 nehmen einziges Problem man braucht ein Pegelwandler für die RTC, und für Display, du hast 58% Speicher belegt, das ist aber nur das was ausgerechnet wurde durch Compiler im "Trocknen Zustand", zur Laufzeit kann das anders aussehen, bei Neustart geht das noch, schlimmer wen der UNO hängen bleibt

Texte belegen keinen weiteren Speicher, sondern nur die Adressen, wo sie zu finden sind.
Du hast soviel Text, dass die IDE Dir bereits eine Warnung ausgibt, dass der Speicher knapp wird.

Den Code bekommt man sicher noch kleiner, wenn nicht für alle Werte die zur Laufzeit gebraucht werden Variablen definiert, sondern zur Laufzeit Funktionen benutzt.

Dann gibts da noch eine Bombe, die Dir evtl. auf die Füße fällt.

Das steht IMHO an der falschen Stelle.
Wenn dataFile nicht gefüllt ist, bleibt die SD-Instanz geöffnet.

Wenn ich später noch Zeit habe, kann ich ja mal sehen, ob ich den besser gebaut bekomme.

@walter_meister

Der Sketch verwendet 304676 Bytes (31%) des Programmspeicherplatzes. Das Maximum sind 958448 Bytes.
Globale Variablen verwenden 27944 Bytes (34%) des dynamischen Speichers, 53976 Bytes für lokale Variablen verbleiben. Das Maximum sind 81920 Bytes.

Das ist mit einem ESP8266, mit einem ESP32 sind es nur 3% :wink:

Ich habs mal zerlegt.
Sieht verrückt aus, ist es auch.
Ich las Dir auf dem SerMon ausgeben, wenn ich erkenne, dass sich de Sekunde geändert hat.
Wenn die RTC dran ist, könnte man die auslesepause auch daraus generieren.

Ich hoffe nix übersehen zu haben.
Es ist noch genügend Speicher da.

Der Sketch verwendet 21738 Bytes (67%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.
Globale Variablen verwenden 1251 Bytes (61%) des dynamischen Speichers, 797 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

// Dieser Code benötigt zwingend die folgenden Libraries:
#include "DHT.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <avr/wdt.h>
#include <RTClib.h>
/* für den Kartenleser */
#include <SD.h>
const int chipSelect = 10;
// File für SD Karte
File dataFile;
// DS3231 RTC Modul
RTC_DS3231 rtc;
DateTime now;
uint8_t lastSecond=255;

constexpr uint8_t RELAIPIN {4};  // Anschluss des Lüfter-Relais
constexpr uint8_t DHTPIN_1 {5};  // Datenleitung für den DHT-Sensor 1 (innen)
constexpr uint8_t DHTPIN_2 {6};  // Datenleitung für den DHT-Sensor 2 (außen)

constexpr bool RELAIS_EIN {LOW};
constexpr bool RELAIS_AUS {!RELAIS_EIN};

#define DHTTYPE  DHT11  // DHT 22 geändert DHT11

// *******  Korrekturwerte der einzelnen Sensorwerte  *******
constexpr uint8_t Korrektur_t_1 {0};  // Korrekturwert Innensensor Temperatur
constexpr uint8_t Korrektur_t_2 {0};  // Korrekturwert Außensensor Temperatur
constexpr uint8_t Korrektur_h_1 {0};  // Korrekturwert Innensensor Luftfeuchtigkeit
constexpr uint8_t Korrektur_h_2 {0};  // Korrekturwert Außensensor Luftfeuchtigkeit
//***********************************************************

constexpr uint8_t SCHALTmin {5};    // minimaler Taupunktunterschied, bei dem das Relais schaltet
constexpr uint8_t HYSTERESE {1};    // Abstand von Ein- und Ausschaltpunkt
constexpr int8_t TEMP1_min {10};   // Minimale Innentemperatur, bei der die Lüftung aktiviert wird
constexpr int8_t TEMP2_min {-10};  // Minimale Außentemperatur, bei der die Lüftung aktiviert wird

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

constexpr uint8_t sensors {2};
struct SENS
{
  DHT s;
  int16_t t;
  uint8_t h;
  int8_t kH;
  int8_t kT;
  int8_t tMin;
};

SENS sens[sensors]
{
  {dht1, 0, 0, Korrektur_h_1, Korrektur_t_1, TEMP1_min,},
  {dht2, 0, 0, Korrektur_h_2, Korrektur_t_2, TEMP2_min,},
};

constexpr uint8_t row {20};
constexpr uint8_t col {4};
LiquidCrystal_I2C lcd(0x27, row, col);  // LCD: I2C-Addresse und Displaygröße setzen

constexpr uint32_t dataIntervall {10000}; // 10 Sekunden
uint32_t lastDataTime;

void setup()
{
  Serial.begin(9600);  // Serielle Ausgabe, falls noch kein LCD angeschlossen ist
  Serial.println(F("Teste Sensoren.."));
  pinMode(RELAIPIN, OUTPUT);           // Relaispin als Output definieren
  digitalWrite(RELAIPIN, RELAIS_AUS);  // Relais ausschalten
  lcd.init();
  lcd.backlight();
  lcd.setCursor(2, 0);
  lcd.print(F("Teste 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);
  SDInit();
  for (byte b = 0; b < sensors; b++)
  { sens[b].s.begin();}

  // DS3231 initialisieren
  if (!rtc.begin())
  {
    Serial.println(F("Fehler beim Initialisieren des DS3231 RTC Moduls!"));

    while (1);
  }
}

void loop()
{
  if (millis() - lastDataTime > dataIntervall)
  {
    lastDataTime = millis();
    getSensorData();
    printDisplay();
    actor();
  }

  now = rtc.now();

  if (now.second() != lastSecond)
  {
    lastSecond = now.second();
    Serial.print(now.hour());
    Serial.print(':');
    Serial.print(now.minute());
    Serial.print(':');
    Serial.println(now.second());
  }
}
//
//
void serialDisplay()
{
  // Werteausgabe auf Serial Monitor
  for (byte b = 0; b < sensors; b++)
  {
    Serial.print(F("Sensor-"));
    Serial.print(b + 1);
    Serial.print(F("1: Luftfeuchtigkeit: "));
    Serial.print(sens[b].h);
    Serial.print(F("%  Temperatur: "));
    Serial.print(sens[b].t);
    Serial.print(F("°C  "));
    Serial.print(F("  Taupunkt: "));
    Serial.print(taupunkt(sens[b].t, sens[b].h));
    Serial.println(F("°C  "));
  }

  for (byte b = 0; b < 30; b++)
  { Serial.print('-'); }

  Serial.println("\r\n\r\n");
}
//
void printDisplay()
{
  static uint32_t lastDisplayTime = 0;
  static uint8_t displayType = 0;

  if (millis() - lastDisplayTime > 5000)
  {
    serialDisplay();
    lcd.clear();
    displayType++;

    if (errcheck() == true)
    { displayType = 2; }

    switch (displayType)
    {
      case 0:
        displayOne();
        break;

      case 1:
        displayTwo();
        break;

      case 2:
        displayThree();
        break;

      default:
        displayType = 0;
        break;
    }
  }
}
//
void displayOne()
{
  for (byte b = 0; b < sensors; b++)
  {
    lcd.setCursor(0, b);
    lcd.print('S');
    lcd.print(b + 1);
    lcd.print(": ");
    lcd.print(sens[b].t);
    lcd.write((uint8_t)0);  // Sonderzeichen °C
    lcd.print('C');
    lcd.write((uint8_t)1);  // Sonderzeichen |
    lcd.print(sens[b].h);
    lcd.print(F(" %"));
    lcd.setCursor(0, b + 2);
    lcd.print(F("Taupunkt "));
    lcd.print(b + 1);
    lcd.print(": ");
    lcd.print(taupunkt(sens[b].t, sens[b].h));
    lcd.write((uint8_t)0);  // Sonderzeichen °C
    lcd.print('C');
  }
}
//
void displayTwo()
{
  lcd.setCursor(0, 0);
  lcd.print(F("Lueftung "));

  if (digitalRead(RELAIPIN) == RELAIS_EIN)
  { lcd.print("AN"); }
  else
  { lcd.print(F("AUS")); }

  lcd.setCursor(0, 1);
  lcd.print(F("Delta TP: "));
  lcd.print(taupunkt(sens[0].t, sens[0].h) - taupunkt(sens[1].t, sens[1].h));
  lcd.write((uint8_t)0);  // Sonderzeichen °C
  lcd.write('C');
}
//
void displayThree()
{
  for (byte b = 0; b < sensors; b++)
  {
    if (isnan(sens[b].h) || isnan(sens[b].t) || sens[b].h > 100 || sens[b].h < 1 || sens[b].t < -40 || sens[b].t > 80)
    {
      Serial.print(F("Fehler beim Auslesen vom "));
      Serial.print(b + 1);
      Serial.println(F(". Sensor!"));
      lcd.setCursor(0, b + 1);
      lcd.print(F("Fehler Sensor "));
      lcd.print(b + 1);
    }
    else
    {
      lcd.setCursor(0, b + 1);
      lcd.print(F("Sensor "));
      lcd.print(b + 1);
      lcd.print(F("in Ordnung"));
    }
  }
}
//
float taupunkt(float t, float r)
{
  float a = 7.5, b = 237.3;

  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 actor()
{
  bool val = false;
  float DeltaTP = taupunkt(sens[0].t, sens[0].h) - taupunkt(sens[1].t, sens[1].h);

  if (DeltaTP > (SCHALTmin + HYSTERESE))
  { val = true; }

  if (DeltaTP < (SCHALTmin))
  { val = false; }

  for (byte b = 0; b < sensors; b++)
  {
    if ((sens[b].t < sens[b].tMin) || isnan(sens[b].h) || isnan(sens[b].t) || sens[b].h > 100 || sens[b].h < 1 || sens[b].t > 80)
    { val = false; }
  }

  if (val == true)
  { digitalWrite(RELAIPIN, RELAIS_EIN); }  // Relais einschalten
  else
  { digitalWrite(RELAIPIN, RELAIS_AUS); }
}

bool errcheck()
{
  bool ret = false;

  for (byte b = 0; b < sensors; b++)
  {
    if (isnan(sens[b].h) || isnan(sens[b].t) || sens[b].h > 100 || sens[b].h < 1 || sens[b].t < -40 || sens[b].t > 80)
    { ret = true;}
  }

  return ret;
}
//
//
void SDInit()
{
  // SD-Karte initialisieren
  Serial.print(F("SD Karte "));

  if (!SD.begin(chipSelect))
  {
    Serial.println(F("konnte nicht initialisiert werden."));
    return;
  }

  Serial.println(F("bereit."));
  // Neue Datei erstellen oder bestehende öffnen
  dataFile = SD.open("daten.csv", FILE_WRITE);

  if (dataFile)
  {
    dataFile.println(F("Datei erstellen im Setupteil des Sketches"));
    dataFile.println(millis());        // Zeile kann entfallen war zum Test da Programm immer neu startete
    dataFile.print(F("Datum,Uhrzeit,"));  // Überschrift der Tabelle
    dataFile.print(F("Temp innen (C),Luftfeuchtigkeit (%),Taupunkt (C),"));
    dataFile.print(F("Luftdruck (hPa),"));
    dataFile.print(F("Temp außen (C),Luftfeuchtigkeit (%),Taupunkt (C),"));
    dataFile.println(F("Lüfter ein,Lüfter aus,Lüfterzeit"));
    Serial.println(F("Erstellen der Datei"));
  }
  else
  {
    Serial.println(F("Fehler beim Erstellen der Datei."));
  }

  dataFile.close();
}
//
//
void getSensorData()
{
  for (byte b = 0; b < sensors; b++)
  {
    sens[b].h = sens[b].s.readHumidity() + sens[b].kH;
    sens[b].t = sens[b].s.readTemperature() + sens[b].kT;
  }
}

das ° Zeichen gibt es im HD44780U Zeichensatz A00 eigentlich auf 0xDF. Da brauchst du kein Custom Char. Eine Ausgabe mit lcd.write(0xDF) sollte reichen.

Hallo

für die Berechnung des Taupunktes
gab es die unterschiedliche
Berechnung Anhalt des 0 Grad Punktes

float taupunkt(float t, float r) {float a = 0, b = 0;
if (t >= 0) {a = 7.5;b = 237.3;} else if (t < 0) {a = 7.6;b = 240.7;}

Dies fällt jetzt weg ! der else Zweig

float taupunkt(float t, float r) {float a = 7.5, b = 237.3;
if (t < 0) {a = 7.6;b = 240.7;}

Habe ich das richtig verstanden das du die
Variablen a & b nicht als
constexpr uint8_t a{7.5},b{237.3};
deklarierst
da die ihren Wert ändern können.

Ich muss dazu sagen das ich den Code stumpf aus dem
www kopiert habe und mich langsam an die Programmierung
heran taste.

Bitte seit nachsichtig !

ist ein Integer Wert.

ist ein Float Wert.

Schau dir dringend mal die Datentypen in C++ an.

Grob gesagt ja.
nicht uint8_t sondern float
Und ja, die sind jetzt vorbelegt.
Wenn t<0 zutrifft, dann muss der Wert noch geändert werden können.

Durch die Vorbelegung brauchst Du den else-Zweig nicht.