Temperaturänderungserkennung

Guten Tag,
ich habe kaum Erfahrung im Programmieren und bräuchte mal eure Hilfe…
Schonmal im Voraus: Ich benutze einen Arduino Uno mit dem 4 Relays Shield.
Und zwar hab ich eine Temperaturerkennung über einen DS18B20. Bei Unterschreitung einer gewissen Temperatur wird ein Relais geschalten, welches eine Heizung ansteuern soll(Relay1) und eines, welche eine Meldeleuchte zur Erkennung des Schaltzustandes ansteuert(Relay2)
Relay3 zeigt an, ob der Arduino in Betrieb ist bzw. die Anlage eingeschaltet ist.
Das funktioniert auch alles soweit.
Allerdings möchte ich, dass wenn nach 10 Minuten nach dem Einschalten der Heizung kein Temperaturunterschied von beispielsweise 2 Grad vorhanden sein sollte, Relay4 geschalten wird und bestenfalls das Programm stehen bleibt oder initialisiert und beispielsweise über einen Eingangspin zurückgesetzt wird.
Hier das bisherige Programm:


#include <OneWire.h>

#include <DallasTemperature.h>

 

// Pindefinierung DS18B20 und die Relay Shield

#define ONE_WIRE_BUS A2

#define RELAY1 4

#define RELAY2 7

#define RELAY3 8

#define RELAY4 12

 

 

// Initialisierung OneWire-Bus und DallasTemperature

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

 

 

void setup() {

 

// Initialisierung Serieller Monitor

Serial.begin(9600);

 

// Initialisierung Relay Shield

pinMode(RELAY1, OUTPUT);

pinMode(RELAY2, OUTPUT);

pinMode(RELAY3, OUTPUT);

pinMode(RELAY4, OUTPUT);

 

// Start DallasTemperature Sensor

sensors.begin();

}

 

 

void loop() {

 

 

// Lesen Temperatur DS18B20

sensors.requestTemperatures();

float tempC = sensors.getTempCByIndex(0);

 

// Ausgabe Serieller Monitor

Serial.println(tempC);

 

// Schalten Relay1 und Relay2: Steuerung und Anzeige Heizlüfter

if(tempC < 5) {

digitalWrite(RELAY1, HIGH);

digitalWrite(RELAY2, HIGH);

}

 

if(tempC > 10) {

digitalWrite(RELAY1, LOW);

digitalWrite(RELAY2, LOW);

}

 

 

// Schalten Relay3: Anzeige Steuerung Ein

digitalWrite(RELAY3, HIGH);

}


Wofür soll das gut sein? Das Programm könnte doch einfach warten, bis ihm jemand oder etwas anzeigt, daß es weiterlaufen soll.

Wenn "das Programm initialisiert" bedeutet das, dass die 10 Minuten Wartezeit und alles andere vorher vergessen wäre. Das willst du nicht.

#include <OneWire.h>
#include <DallasTemperature.h>



// Pindefinierung DS18B20 und die Relay Shield

#define ONE_WIRE_BUS A2

constexpr byte RELAY1 {4};
constexpr byte RELAY2 {7};
constexpr byte RELAY3 {8};
constexpr byte RELAY4 {12};

unsigned long startHeizung;
constexpr unsigned long heizZeitTimeOut = {1000UL * 60 * 10};
int heizStartTemperatur;
constexpr int checkTempHyterese {2};

// Initialisierung OneWire-Bus und DallasTemperature

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

void setup()
{
  // Initialisierung Serieller Monitor
  Serial.begin(9600);
  // Initialisierung Relay Shield
  pinMode(RELAY1, OUTPUT);
  pinMode(RELAY2, OUTPUT);
  pinMode(RELAY3, OUTPUT);
  pinMode(RELAY4, OUTPUT);
  // Schalten Relay3: Anzeige Steuerung Ein
  digitalWrite(RELAY3, HIGH);
  // Start DallasTemperature Sensor
  sensors.begin();
}


void loop()
{
  // Lesen Temperatur DS18B20
  sensors.requestTemperatures();
  float tempC = sensors.getTempCByIndex(0);
  // Ausgabe Serieller Monitor
  Serial.println(tempC);
  // Schalten Relay1 und Relay2: Steuerung und Anzeige Heizlüfter
  if (tempC < 5)
  {
    if (digitalRead(RELAY1) == LOW)
    {
      digitalWrite(RELAY1, HIGH);
      startHeizung = millis();
      heizStartTemperatur = tempC;
      digitalWrite(RELAY2, HIGH);
    }
    else
    {
      if (millis() - startHeizung > heizZeitTimeOut &&
          tempC < heizStartTemperatur + checkTempHyterese)
      {
        if (digitalRead(RELAY4) == HIGH)
        {
          Serial.println(F("HEIZUNG AUSGEFALLEN!"));
        }
        digitalWrite(RELAY4, LOW);
      }
    }
  }
  if (tempC > 10)
  {
    digitalWrite(RELAY1, LOW);
    digitalWrite(RELAY2, LOW);
  }
}
1 Like

Moin @my_xy_projekt,

sollte vermutlich so heißen :wink:

  if (millis() - startHeizung > heizZeitTimeOut &&
          tempC < heizStartTemperatur + checkTempHyterese)
      {
        if (digitalRead(RELAY4) == HIGH)
        {
          Serial.println(F("HEIZUNG AUSGEFALLEN!"));
        }
        digitalWrite(RELAY4, HIGH);  // <- Hier das Fehler-Relais schalten!!!!

Moin,
:smiley:

Das ist so ein kleiner Gag am Rande....
Die Bedingung lautet: Zeit abgelaufen && temperatur nicht erhöht
Die Reaktion soll sein: Dann mach den Pin LOW (und signalisiere damit, das irgendwas fail ist)
Soweit so gut.
Was mir jetzt auffällt, ist die fehlende Aktivierung des PIN im setup().
Die RelaisShields sind eigentlich LOW-aktiv - der müsste also im setup() HIGH gezogen werden.
Und dann kommt die Bedingung davor zum tragen.
Die sorgt nämlich dafür, das die Meldung nur einmal ausgeführt wird und nicht den SerMOn flooded...

(Gut zu wissen, das wer auch meine Codes liest :slight_smile: )

Wenn ich mich nicht täusche, benutzt der TO die Relaisansteuerung so, dass HIGH gleich "Eingeschaltet" und LOW = "Ausgeschaltet" ist ...

P.S.: Hab's bei Wokwi nachgestellt ... (allerdings mit 10 s statt 10 min) :wink:

Guckst Du hier:

// Schalten Relay3: Anzeige Steuerung Ein

digitalWrite(RELAY3, HIGH);

Dann ists aber richtig falsch.
Wenn die Pegel vertauscht sind, gehört's so:

        if (digitalRead(RELAY4) == LOW)
        {
          Serial.println(F("HEIZUNG AUSGEFALLEN!"));
        }
        digitalWrite(RELAY4, HIGH);

Sonst hätte die Bedingung auf die PIN-Abfrage keinen Effekt.

Jo, das sieht gut aus!!

Du hast die kurze Fassung mit minimalen Änderungen geschrieben, ich habe mal eine längere Ausführung mit StateMachine verbrochen ... :wink:

Sketch
#include <OneWire.h>
#include <DallasTemperature.h>

// Pindefinierung DS18B20 und vom Relay Shield
constexpr byte ONE_WIRE_BUS = A2;
constexpr byte noOfRelays = 4;
enum relayNames {HEIZUNG, HEIZANZEIGE, STEUERUNGSANZEIGE, FEHLERANZEIGE};
byte relayPin[noOfRelays] = {4, 7, 8, 12};

// Parameter und Steuerungsmodi
constexpr unsigned long MessIntervall  =  1000UL;
constexpr unsigned long AlarmIntervall = 10*60*1000UL;
constexpr float minTemp =  5.0;
constexpr float maxTemp = 10.0;
constexpr float differenzTemp = 2.0;
float startTemp;

enum SteuerungsModus {DONOTHING, STARTHEATING, HEATING, STOPHEATING, ERROR};
SteuerungsModus     modus = DONOTHING;
SteuerungsModus lastModus = ERROR;
unsigned long heizungsStart;
unsigned long heizungsSekunden;
float actTemperature;

// Initialisierung OneWire-Bus und DallasTemperature
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

void switchOn(relayNames rel) {
  digitalWrite(relayPin[rel], HIGH);
}

void switchOff(relayNames rel) {
  digitalWrite(relayPin[rel], LOW);
}

void checkTemperature() {
  // Lesen Temperatur DS18B20
  sensors.requestTemperatures();
  actTemperature = sensors.getTempCByIndex(0);
  // Ausgabe Serieller Monitor
  Serial.print("Aktuell: ");
  Serial.print(actTemperature);
  if (modus == HEATING) {
    Serial.print("\tStarttemperatur: ");
    Serial.print(startTemp);
    Serial.print("\t seit ");
    Serial.print(heizungsSekunden);
    Serial.print(" [s]");
  }
  Serial.println();
}

void messung() {
  static unsigned long letzteMessung = 0;
  if (millis() - letzteMessung > MessIntervall) {
    letzteMessung = millis();
    checkTemperature();
  }
}

// {DONOTHING,STARTHEATING, HEATING, STOPHEATING, ERROR};
void stateMachine() {
  static unsigned long lastCheck = 0;
  messung();
  switch (modus) {
    case DONOTHING:
      if (actTemperature < minTemp) {
        modus = STARTHEATING;
      }
      break;
    case STARTHEATING:
      switchOn(HEIZUNG);
      switchOn(HEIZANZEIGE);
      heizungsStart = millis();
      startTemp = actTemperature;
      heizungsSekunden = 0;
      modus = HEATING;
      break;
    case HEATING:
      //Check for temp!
      if (actTemperature > maxTemp) {
        modus = STOPHEATING;
        return;
      };
      if (millis()-lastCheck >= 1000) {
        lastCheck = millis();
        heizungsSekunden++;
      }
      if (millis() - heizungsStart > AlarmIntervall && actTemperature - startTemp < differenzTemp) {
        Serial.println("Error!");
        switchOff(HEIZUNG);
        switchOff(HEIZANZEIGE);
        switchOff(STEUERUNGSANZEIGE);
        switchOn(FEHLERANZEIGE);
        modus = ERROR;
      }
      break;
    case STOPHEATING:
      switchOff(HEIZUNG);
      switchOff(HEIZANZEIGE);
      modus = DONOTHING;
      break;
    case ERROR:
      break;
  }
}

void setup() {
  Serial.begin(115200);
  for (int i = 0; i < noOfRelays; i++) {
    pinMode(relayPin[i], OUTPUT);
  }
  sensors.begin();
  switchOn(STEUERUNGSANZEIGE);
}

void loop() {
  stateMachine();
}

siehe Wokwi: https://wokwi.com/projects/366995199527696385

Mit kleinen Änderungen:

  • Messen der Temperatur nur im Sekundentakt
  • Schalten der Relais nur je einmal bei Bedarf
  • Ausdruck der Starttemperatur und der Sekunden seit Beginn des Heizens
  • Im Fehlerfall alle Relais aus, bis auf die Fehleranzeige
  • Die Steuerung ist im Fehlerfall nur durch ein Reset wieder zu starten (hier fehlt eine automatische Rücksetz-Bedingung oder ein Knöpfchen ...)
1 Like

Moin, @sabriadr,

wenn Du magst, gerne mal in den Post #9 reinschauen...

:wink:

Ich wollte abwarten, was da noch kommt ....
Habe vergeblich gewartet. :wink:

1 Like

Guten Morgen,
vielen Dank für die schnelle Hilfe, ich bin erst jetzt dazu gekommen, das Programm aufzuspielen.
Funktioniert alles soweit, aber könntet ihr mir eventuell noch mit dem Resetknopf helfen? :slight_smile:

Wenn es wirklich komplett von vorn losgehen soll, kann man durchaus den Reset-Pin verwenden.
Der Sketch fängt dann wie beim Einschalten mit setup() wieder an.

Schon klar, aber dazu muss der Resetknopf dann natürlich irgendwie zugänglich sein (falls das Ganze eingehäust wird), oder ein Knöpfchen wird nach außen gelegt.

Letztlich bleibt die "Standard-Methode", Stecker raus, warten, Stecker rein :wink:

Bei einer ggf. nicht überwachten Steuerung würde ich vorsichtshalber noch den Einbau einer automatische Abschaltung prüfen. Dazu müsste man aber mehr über die Umstände der Anwendung wissen ...

Daher sprach ich vom Reset Pin, an den man einen Taster anschließen kann

Dann sind wir uns doch einig :slight_smile:

hier fehlt eine automatische Rücksetz-Bedingung oder ein Knöpfchen ...

Ja das klingt ist gut, danke euch.
Mir ist nun aufgefallen, dass der Arduino beim Starten nicht automatisch abschält, wenn er von Beginn an heizt… Gibt es da eine Möglichkeit?

Ich würde zusätzlich noch eine z.B. im einfachsten Fall zeitgesteuerte Abschaltung vorsehen, um ein dauerhaftes Heizen zu vermeiden, falls ein Sensorfehler auftritt.

Da ein Temperatursensor nicht viel kostet, könnte man auch auf Redundanz setzen.

Wie sieht's Du das?

Normalerweise wird ein Digitalpin mit pinMode(Pin, OUTPUT) auf LOW initialisiert.

Damit sollte das Heizungsrelais nach einem Restart nicht anziehen.

Die folgenden Zeilen an geeigneter Stelle erzeugen auch den bisher angenommenen Einschaltzustand.

digitalWrite(RELAY1, LOW);
digitalWrite(RELAY2, LOW);
digitalWrite(RELAY3, HIGH);
digitalWrite(RELAY4, LOW);

Solltest Du aber den Ruhekontakt des Relais für die Heizung verwenden, wäre das nicht ok.

Das hätte aber die Folge, dass die Heizung auch dann immer läuft, wenn die Steuerung stromlos ist...

Wo wäre denn die geeignete Stelle?:sweat_smile: