Arduino Uno "friert" immer ein

Hallo,

Ich habe mittels einen Arduino Uno versucht einen Brutapparat zu bauen.
Im Prinzip sind es 5 DS18B20 Sensoren und ein DHT11 zur Feuchtemessung,
Und 2 Heizkabel die mit einem Optokoppler gesteuerten Relais geschaltet werden.

Es funktioniert alles einwandfrei nur, nach einer gewissen Zeit (~6 Stunden) friert der Arduino ein und die Regelung funktioniert nicht mehr.
Das heißt es werden keine neuen Werte mehr gemessen, die beiden Relais schalten nicht mehr aus. Und beim Bildschrim schaltet auch die Feuchte anzeige nicht mehr hin und her, wie im Code vorgesehen.
Es werden aber die letzten Temperaturwerte normal angezeigt, aber ohne das sie sich ändern.

Ich habe den Code schon mehrmals durchgesehen, und ich finde keine Stelle die dies Problem verursachen könnte.
Der Brutapparat ist auch schon mehr als 30 Tage am Stück gelaufen, ohne Probleme und jetzt weiß ich nicht woher die Probleme kommen könnten.
Könnte es durch eine neuere Version sein?

Könnte mir vielleicht hier wer weiterhelfen, ich hätte einmal den Code angefügt.


#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <DHT.h>

#define POTI A0
#define LEDPIN LED_BUILTIN
#define RELAISPIN7 7
#define RELAISPIN8 8

int pPotiInput, pAnalogInput;
float pSoll, pIst, pDelta;
const float pTempFenster = 0.8;
bool pHeat, pHysterese, pHeatRelaisOn, pHeatRelaisBottomOn, pHystereseMode, pHystereseOn;
int pHystereseLoopTimer;

float pT1, pT2, pT3, pT4, pT5, pH6, pT3T4Average;
int pReferenzCounter;

const unsigned long cInterval = 100;
const unsigned long cInterval1 = 4000;
unsigned long pCurrentMillis, pLastMillis;
unsigned long pCurrentMillis1, pLastMillis1;

float pSollT5 = 40.0;
const float pSollT5High = 43.0, pSollT5Low = 39.5, pDeltaT5High = 1.6, pDeltaT5Low = 0.2;
bool HumidityDisplay;

//Hysteresensteuerung für Relaistakt
unsigned long pCurrentMillisBlink, pLastMillisBlink;
const unsigned long cIntervalBlinkIntervall = 1000;

LiquidCrystal_I2C lcd(0x27,20,4);

// Data wire is plugged into the Arduino
#define OWB_DS18B20 2
#define OWB_DHT11 3

// Initiat DHT11 Sensor
DHT SensorDHT(OWB_DHT11, DHT11);

// Initiate DS18B20
OneWire oneWire(OWB_DS18B20);
DallasTemperature SensorsDS(&oneWire);

// Addresses of 5 DS18B20s
uint8_t adT1[8] = { 0x28, 0xFF, 0x64, 0x1F, 0x40, 0x5F, 0x16, 0xFA };
uint8_t adT2[8] = { 0x28, 0xFF, 0x64, 0x1F, 0x40, 0x48, 0x83, 0x56 };
uint8_t adT3[8] = { 0x28, 0xFF, 0x64, 0x1F, 0x40, 0x58, 0x91, 0x9B };
uint8_t adT4[8] = { 0x28, 0xFF, 0x64, 0x1F, 0x40, 0x61, 0x1C, 0x75 };
uint8_t adT5[8] = { 0x28, 0xFF, 0x64, 0x1F, 0x40, 0x6D, 0x43, 0xA2 };

void setup()
{
  lcd.init();
  lcd.backlight();

  // Start up the Sensors
  SensorsDS.begin();
  SensorDHT.begin();

  pinMode(LEDPIN, OUTPUT);
  pinMode(RELAISPIN7, OUTPUT);
  pinMode(RELAISPIN8, OUTPUT);

  //Ausgänge initial schreiben
  digitalWrite(RELAISPIN7, true); //TRUE bedeutet auf FALSE setzen
  digitalWrite(RELAISPIN8, true);

  pReferenzCounter = 2;
}
void loop()
{
  //Aktuellen Millisekunden auslesen + Überlaufschutz
  pCurrentMillis = millis();
  if (pCurrentMillis < pLastMillis) {
    pLastMillis = 0;
  }
  pCurrentMillis1 = millis();
  if (pCurrentMillis1 < pLastMillis1) {
    pLastMillis1 = 0;
  }
  pCurrentMillisBlink = millis();
  if (pCurrentMillisBlink < pLastMillisBlink) {
    pLastMillisBlink = 0;
  }


  if ((pCurrentMillis - pLastMillis) >= cInterval) {
    // save the last time
    pLastMillis = pCurrentMillis;

    pAnalogInput = analogRead(POTI);
    pPotiInput = map(pAnalogInput, 0, 1023, 300, 400);
    pSoll = (float)pPotiInput / 10.0;
    //Display Ausgabe
    lcd.setCursor(0,0);
    lcd.print("S: ");
    lcd.print(pSoll, 1);
    lcd.write(0xDF);
  }

  if ((pCurrentMillis1 - pLastMillis1) >= cInterval1) {
    // save the last time
    pLastMillis1 = pCurrentMillis1;

    //Aktuellen Temperaturen auslesen
    SensorsDS.requestTemperatures();
    pT1 = SensorsDS.getTempC(adT1);
    pT2 = SensorsDS.getTempC(adT2);
    pT3 = SensorsDS.getTempC(adT3);
    pT4 = SensorsDS.getTempC(adT4);
    pT5 = SensorsDS.getTempC(adT5);

    //Feuchte Auslesen
    pH6 = SensorDHT.readHumidity();

    //Anzeige Eliminieren wenn kein Sensor gesteckt
    pReferenzCounter = 2;
    if (pT1 < 0.0) {
      pT1 = 0.0;
      pReferenzCounter = pReferenzCounter - 1;
    } else {
      pT1 = pT1 + 0.5;
    }
     if (pT2 < 0.0) {
      pT2 = 0.0;
      pReferenzCounter = pReferenzCounter - 1;
    } else {
      pT2 = pT2 + 0.6;
    }
     if (pT3 < 0.0) {
      pT3 = 0.0;
    } else {
      pT3 = pT3 + 0.8;
    }
     if (pT4 < 0.0) {
      pT4 = 0.0;
    } else {
      pT4 = pT4 + 0.8;
    }
     if (pT5 < 0.0) {
      pT5 = 0.0;
    } else {
      pT5 = pT5 + 0.0;
    }

    //Aktuelle Temperatur bestimmen, nur wenn mindestens 1 Sensor von T1 | T2 gesteckt
    if (pReferenzCounter > 0) {
      pIst = (pT1 + pT2) / pReferenzCounter;
    }
    //Wenn keine Temperaturen zum einlesen sind dann wird Sensor T3 verwendet (Auch nur wenn ein Wert eingelsen wird)
    else if (pT3 > 1.0) {
      pIst = pT3;
    }
    //Sonst wird Sensor T5 verwendet, egal ob er verwendet wird oder nicht
    else {
      pIst = pT5;
    }

    //Delta bestimmen
    pDelta = pSoll - pIst;

    //Linear Regler für T5
    pSollT5 = (pDelta - pDeltaT5Low) * (pSollT5High - pSollT5Low) / (pDeltaT5High - pDeltaT5Low) + pSollT5Low;
    if (pSollT5 <= pSollT5Low) {
      pSollT5 = pSollT5Low;
    }
    else if (pSollT5 >= pSollT5High) {
      pSollT5 = pSollT5High;
    }
    else {
      pSollT5;  //Wert nach linearer Rechnung übernehmen
    }

    //2PunktRegler mit Hysterese
    if ((pDelta <= -0.01) || (pT5 >= pSollT5))
    {
      pHeat = false;  //Kühlen (Temperatur ist zu hoch)
      pHysterese = false;
    }
    else if (pDelta >= pTempFenster) 
    {
      pHeat = true;   //Heizen (Temperatur ist zu gering)
      pHysterese = false;
    }
    else
    {
      pHeat = true ;  //Hysterese Fenster anfangen zu takten
      pHysterese = true;
    }

    //Ist Anzeige
    lcd.setCursor(10,0);
    lcd.print("In: ");
    lcd.print(pIst, 1);
    lcd.write(0xDF);

    lcd.setCursor(0,1);
    lcd.print("T1: ");
    lcd.print(pT1, 1);
    lcd.write(0xDF);

    lcd.print(" T2: ");
    lcd.print(pT2, 1);
    lcd.write(0xDF);

    lcd.setCursor(0,2);
    lcd.print("T3: ");
    lcd.print(pT3, 1);
    lcd.write(0xDF);

    lcd.print(" T4: ");
    lcd.print(pT4, 1);
    lcd.write(0xDF);

    lcd.setCursor(0,3);
    lcd.print("T5: ");
    lcd.print(pT5, 1);
    lcd.write(0xDF);

    if (HumidityDisplay) {
      lcd.print(" H6: ");
      lcd.print(pH6, 0);
      lcd.print("%  ");
    } else {
      lcd.print(" S5: ");
      lcd.print(pSollT5, 1);
      lcd.write(0xDF);
    }
    HumidityDisplay = !HumidityDisplay;
  }

  //Code zur Intervallsteuerung
  if ((pCurrentMillisBlink - pLastMillisBlink) >= cIntervalBlinkIntervall) {
    // save the last time
    pLastMillisBlink = pCurrentMillisBlink;

    if (pHystereseMode == true) {
      // Modus 1 (Benötigt noch mehr Leistung) T, T, T, T, T, T, T, T, T, T, F, F, F, F, F, F
      if (pHystereseLoopTimer < 10) {
        pHystereseOn = true;
        pHystereseLoopTimer = pHystereseLoopTimer + 1;
      }
      else {
        pHystereseOn = false;
        if (pHystereseLoopTimer >= 15) {
          pHystereseLoopTimer = 0;  //LoopTimer auf 0 damit der Zyklus wieder von vorne beginnt
        }
        else {
          pHystereseLoopTimer = pHystereseLoopTimer + 1;
        }
      }
    }
    else {
      // Modus 0 (Benötigt weniger Leistung) T, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F
      if (pHystereseLoopTimer < 2) {
        pHystereseOn = true;
        pHystereseLoopTimer = pHystereseLoopTimer + 1;
      }
      else {
        pHystereseOn = false;
        if (pHystereseLoopTimer >= 15) {
          pHystereseLoopTimer = 0;  //LoopTimer auf 0 damit der Zyklus wieder von vorne beginnt
        }
        else {
          pHystereseLoopTimer = pHystereseLoopTimer + 1;
        }
      }
    }
  }

    //Ausgänge analysieren und beschalten
  if (pHeat == false) {
    pHeatRelaisOn = false;
  }
  else if (pHeat && pHysterese) {
    //Fenstererkennung (Modus auswählen)
    if (pDelta >= (pTempFenster / 2.0)) {
      // Modus 1 (Benötigt noch mehr Leistung)
      pHystereseMode = true;
    }
    else {
      // Modus 0 (Benötigt weniger Leistung)
      pHystereseMode = false;
    }
    pHeatRelaisOn = pHystereseOn;
  }
  else {
    //Dauerhaft einschalten
    pHeatRelaisOn = true;
  }

  //Mittelwert bilden
  pT3T4Average = (pT3 + pT4) / 2.0;
  //Heizkabel am Boden
  if (pT3T4Average < (pSoll - 0.1)) {
    //Heizkabel am Boden soll Heizen
    pHeatRelaisBottomOn = true;
  }
  else if (pT3T4Average > pSoll) {
    //Heizkabel ausschalten
    pHeatRelaisBottomOn = false;
  }


  //Ausgänge schreiben
  digitalWrite(RELAISPIN7, !pHeatRelaisOn);
  digitalWrite(RELAISPIN8, !pHeatRelaisBottomOn);
  digitalWrite(LEDPIN, pHeatRelaisOn);
}

Hallo su210

Herzlich Willkommen im besten Arduino Forum der Welt :slight_smile:

Der einfachste Trick ist das Einlöten von Serial.println("was auch immer"); als Logikanalyzer an verdächtigen Stellen im Programm.

Als ich mir das Programm zum ersten Mal ansah, kam ich bei den Timern nicht mehr weiter. Die sehen recht organisch gewachsen und verknotet aus.

Ich rate mal, du hast ein Problem mit Speicherüberlauf.
Vermutlich wird dein Ram im Betrieb nicht ausreichen.
Zudem ist dei Sketch (SpaghettiCode) sehr unübersichtlich und somit schwer zu lesen. Du solltest der Übersichtlichkeit wegen, besser mit Funktionen arbeiten. Dann lässt sich dein Sketch auch besser debuggen.

Wie weit ist der vom Uno entfernt?

Globale Variablen verwenden 670 Bytes (32%) des dynamischen Speichers
Nur ob der wird Fragmentiert ist andere Frage.

Das sagt ja leider nichts über den Betrieb nach ca. 6 Stunden aus.

Das war auch mein Gedanke, So wie mall gelesen ESP räumen ein wenig den Speicher auf (Hab mall was drüber gelesen, ob stimmt gute Frage) die Arduinos jedoch nicht

Ja, ok....es könnte aber auch das von dir bemerkte I2C-Problem sein. Nur tritt das meist schneller auf.

Egal auch wie, keine Antwort ist auch eine Antwort, mall sehen ob der TO sich meldet.

Zeile 174:

      pSollT5;  //Wert nach linearer Rechnung übernehmen

Fehlt da nicht eine Zuweisung eines Wertes?

Will sagen:
Wenn weder die Bedingung in Zeile 167 if (pSollT5 <= pSollT5Low) noch die Zusatzbedingung in Zeile 170 else if (pSollT5 >= pSollT5High) zutreffen, wird pSollT5 nicht geändert.

Ob der dann alte Wert dazu führt, dass das Programm anscheinend nichts mehr tut, habe ich noch nicht erlesen können.

BTW: Der Kommentar Wert ... übernehmen ist also mindestens irreführend - eher einfach falsch.

Hallo,

Vielen Dank für die Antworten, ich bin nur etwas später dazugekommen.
Ich habe zuerst das Probramm mit Serial.println durchdebuggt, es läuft auch so ohne Probleme.
Was ich aber nicht verstehe ist, das es nicht immer die 6 Stunden sind. Gestern war zum Beispiel erst ein ausfall von 48 Stunden.

Der Code ist leider so wild gewachsen und ich habe immer etwas hinzugefügt. Ich werde versuchen denn noch einmal bessser zu sortieren und Kommentare einzubauen.

Wenn es ein überlauf des RAM wäre, wiese ist das einfrieren des Arduino dann auch nach unterschiedlichen Zeiten?
Und was kann man dagegen tun?

Der Arduino ist direkt mit dem LCD Display mittels Jumperkabel verbunden (20cm).

Wenn er einfriert, ist nicht allein der Display betroffen.
Weil die Relais zum Regulieren der Temperaturen schalten dann auch nicht mehr, darum denke ich das der ganze Arduino stoppt und nach einem Hardware reset (Spannung aus und wieder ein)
Funktioniert wieder alles?

Hallo su210

Verkürze zum Testen diese Kabellänge.
I²C ist für kurze Distanzen auf Platinen entwickelt worden.

das habe ich einmal so ausprogrammiert, weil die map funktion mit floats nicht richtig arbeitet.
Und ich dann wegen den abstürzen des Arduino habe ich die constrain funktion ersetzt mit dieser If Else struktur:

pSollT5 wird in Zeile 166 beschrieben

    pSollT5 = (pDelta - pDeltaT5Low) * (pSollT5High - pSollT5Low) / (pDeltaT5High - pDeltaT5Low) + pSollT5Low;
    if (pSollT5 <= pSollT5Low) {
      pSollT5 = pSollT5Low;
    }
    else if (pSollT5 >= pSollT5High) {
      pSollT5 = pSollT5High;
    }
    else {
      pSollT5;  //Wert nach linearer Rechnung übernehmen
    }

Aber 20cm sind in der Spec. Das sollte problemlos gehen.

Ok, dann mal die richtige Anordnung der Abschlußwiderstände für den I²C Bus überprüfen.

Das kann alles weg, weil es nix macht.

Was ist den die richtige Anordnung der Abschlußwiderstände? Es sind keine Abschlußwiderstände, sondern Pullupwiderstände. Wo sie auf dem Bus sind, ist egal.

Passiert das Einfrieren direkt nach dem schalten? Dann hast du ein EMV Problem.

Als display wird das hier verwendet:

inklusive dem I2C adapter
eigene dedizierte Pull-Up widerstände habe ich nicht verwendet aber ich habe schon einige Projekte so gemacht und da funktioniert alles einwandfrei