Email nur einmal in der Stunde senden

Hallo Leute,
ich baue gerade eine Temperaturanzeige und Überwachtung für mein Serverrack im Keller.
Soweit hab ich auch alles hinbekommen, nur beim Alarmemail wenn die Temperatur im Rack zu hocch ist scheitere ich.

Das zusenden des Emails funktioniert, ich möchte aber verhindert das icch mich selbst zuspame und daher würde ich gerne einen Zeitffaktor eingeben und das Email immer nur beim überschreiten des zeitfaktors senden.

zb. einmal in der Stunde.

Ich hab versucht das mit millis zu lösen aber irgendwie will das nicht so wie ich will.

Kann mir jemand sagen was ich falsch mache?

Hier der Code:

 //Wenn Racktempertur über 23 Grat Email senden
  if (IndoorTemp > 23) { //23 ist zum testen da ich das im Moment ca als Raumtemperatur habe.

    //Variable für Temperatur zum Senden als Mail
    TempEmailSend = String(IndoorTemp);

    unsigned long currentMillis = millis();
    while (millis() - currentMillis > 10000) { //10 Sekunden zum testen
      EMailSender::EMailMessage message;
      message.subject = "D1 ServerRack Temperaturalarm";
      message.message = "Es hat aktuell " + TempEmailSend + "° Grad im ServerRack und übersteig den Alarmwert!";
      EMailSender::Response resp = emailSend.send("xxxxxxx", message); //Empfägermail
      Serial.println("Sending status: ");
      Serial.println(resp.status);
      Serial.println(resp.code);
      Serial.println(resp.desc);
      delay(100);
    }
  }

Danke euch.

nicht while sondern if.

also eher so

static uint32_t previousMillis = 0; // statische Variable für die letzte Durchführungszeit
if (millis() - previousMillis > 10000UL)
{
  // tu was
  previousMillis = millis();  //letzte Durchführungszeit merken.
}

genereller Tipp:
schau dir noch mal das Beispiel "BlinkWithoutDelay" in der IDE an.
Jede Zeile analysieren und nachvollziehen was sie macht.
So lange bis du den Code auswendig hinschreiben kannst.
Dann klappt es für immer mit den Zeitsteuerungen.

Juhu, danke damit hab ichs hinbekommen.

Ich hatte es mit if bereits versucht, ich hab jetzt aber gesehen das ich die Initialisierung an der falschen Stelle gemacht habe.

Danke für deine Hilfe!!!

Zu früh gefreut :slight_smile:
Wenn ich jetzt eine Stunde einstellen würde dann müsste die Temperatur eine Stundde über der angegebenen Temp liegen damit ein Email gesendet wird.

Das bedeutet ich habe die Abfrage an der falschen stelle oder?

im LOOP

 //Wenn Racktemperatur über 23 Grat Email senden
  if (IndoorTemp > 23) {

    //String Variable für Temp um im Mail zu verwenden
    TempEmailSend = String(IndoorTemp);

    //Nur jede Stunden ein Email senden Spamblock
    if (millis() - previousMillis  > 60000UL) { //360000 = 1h
      
      EMailSender::EMailMessage message;
      message.subject = "D1 ServerRack Temperaturalarm";
      message.message = "Es hat aktuell " + TempEmailSend + "° Grad im ServerRack und übersteig den Alarmwert!";
      EMailSender::Response resp = emailSend.send("xxx", message); //Empfägermail
      Serial.println("Sending status: ");
      Serial.println(resp.status);
      Serial.println(resp.code);
      Serial.println(resp.desc);
      previousMillis=millis();
 
    }
  }

das kommt jetzt nur drauf an, wie du deine Ifs verschachtelst.

aber so wie es jetzt ist, müsste die Temperatur - nach einem Alarm - nach einer Stunde - wieder mal kurz über 23 Grad gehen, damit wiederum ein Alarm ausgelöst wird.

mach dir erst einen Ablaufplan aus dem zweifelsfrei hervorgeht, wie deine Bedinungen sind, dann mach dein Programm.

So hab jetzt ein paar Sachen ausprobiert, leider immernoch das selbe verhalten.

Ich verstehe folgendes nicht:

static uint32_t previousMillis = 0;

Damit initialisiere ich am Programm anfang 0, dadurch müsste doch

 if (millis() - previousMillis  > 20000UL) {

Beim ersten mal durchlaufen immer greifen oder?
Warum wird aber der Teil erst nach Ablauf einer Periode ausgeführt.

auch hier gilt: Zeilenweise durchdenken.
wenn previousMillis = 0 ist, dann dauert es halt genau deinen Intervall, bis das if zuschlagen kann.

Du kannst das umgehen in dem du die Variable einfach schon alt genug initialisierst. Wenn das if das erste mal aufgerufen wird ist die Bedingung dann schon erfüllt

static uint32_t previousMillis = 0-20000UL;

da du nun den Intervall zweimal im Code als magic number hast, wäre eine const expr für den Intervall nun besser.

1 Like

Ok das leuchtet mir ein, und es funktioniert auch :slight_smile:
Mit const expr meinst du ich soll eine Variable anlegen in der ich die Zahl speichere damit ich sie bei einer Änderung an allen Stellen im Code änder und überall die selbe Zahl habe oder?

Sorry ich hab den Programmier Slang :upside_down_face: nicht wirklich drauf und muss das erstmal Übersetzen.

Vielen Dank für die guten Erklärungen und deine Hilfe.

Hi ihr beiden...

ich bin ja kein espler, aber könnte ggfls schief gehen.

Ich formatiere etwas anders und hab nur meine Kommentare drin.
Schaut mal auf die Logik, die sich dahinter verbirgt und ob das nicht ne bessere Alternative wäre....

if (IndoorTemp > 23)
{
  if (TempEmailSend == "") // Wenn der String leer ist
  {
    //Variable für Temperatur zum Senden als Mail
    TempEmailSend = String(IndoorTemp);
    EMailSender::EMailMessage message;
    message.subject = "D1 ServerRack Temperaturalarm";
    message.message = "Es hat aktuell " + TempEmailSend + "° Grad im ServerRack und übersteig den Alarmwert!";
    EMailSender::Response resp = emailSend.send("xxxxxxx", message);
    Serial.println("Sending status: ");
    Serial.println(resp.status);
    if (resp.status != )  // Hier vergleichen auf NICHT erfolgreiches versenden
    {
      TempEmailSend == "";
    }
    else
    {
      previousmillis = millis();
    }
    Serial.println(resp.code);
    Serial.println(resp.desc);
    delay(100);
  }
}
if (millis()-previousmillis>60000)
// Alternativ:
// if ((millis()-previousmillis>60000) || (IndoorTemp<23))
{
       TempEmailSend == "";
}
1 Like

Danke xy,
ich bin je eher eine Pfeife aber ich vermute du meinst mit schief gehen das ich nicht prüfe ob das Email auch wirklich gesendet wurde oder?

Weiters glaube ich zu verstehen das du die Variabel EmailTempSend als Flag benutzt und nach Prüfung des Sendestatus setzt.

Ich glaub das ist eine gute Idee, wenn ich das Ding das nächste mal ausbaue werde ich das ausprobieren!

Eine negative vorzeichenlose Zahl.
Wow!

Irgendwie ist in letzter Zeit, eben die Zeit, für irrationale Zeiten in diesem Forum.

Wie mir scheint, sollte ich auch mal in die Vergangenheit teleportieren und dort Urlaub machen.

sollte ich das jetzt verstehen? :crazy_face:

Muss ich jetzt was am Programm ändern?

Denke nicht oder?

Ich weiß nicht, ob das ein Fehler ist.
Aus rein technischer Sicht wird das funktionieren.

Aber was ich weiß...

  • dass ich nicht auf die Idee kommen würde
  • dass ich ein emotionales Problem damit habe

Wie auch immer, ich werde Umgang damit finden, nur ist mir noch nicht klar, welchen.

Wenn es funktioniert, ist doch gut....
Denn "ich" habe ein Problem damit, nicht du.

#include <INTERVAL.h>

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

void loop() 
{
   INTERVAL(1000UL,0UL)
   {
     //Tu was alle 1000ms
     // starte sofort
     Serial.println(millis());
   }

   INTERVAL(1000UL,8000UL)
   {
     //Tu was alle 1000ms
     // starte nach 8sec
     Serial.println(millis());
   }

   INTERVAL(1000UL)
   {
     // Tu was alle 1000ms
    // starte mit Wartezeit
     Serial.println(1000UL);
   }
}

INTERVAL.zip (2,4 KB)

1 Like

Ok danke ich hab schon mehrmals über das INTERVAL gelesen, habs runtergeladen, ich werds beim nächsten Projekt ausprobieren, vielleicht tu ich mich damit leichter.

#include <limits.h>     // oder alternativ 4294967295UL für UINT_MAX einsetzen

void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.printf("\nSetup:%9d\n", millis());
}

void loop() {
  constexpr uint32_t INTERVAL {10000UL};
  static uint32_t previousMillis {UINT_MAX - INTERVAL};
  if (millis() - previousMillis >= INTERVAL) {
    previousMillis += INTERVAL;
    Serial.printf("Loop:%10d\n", millis());
  }
}
1 Like

Na, da hab ich ja nicht so schlecht geschrieben :wink:
Ja, so war das angedacht.
Ich nehme den Sendestring zweimal. Einmal um überhaupt zu senden und ein zweites Mal um zu prüfen, ob ich überhaupt senden darf.
Denn nur wenn leer, kann ich den neu befüllen.

Leer wird er nur
a) wenn nicht erfolgreich versandt.
b) nach erfolgreichem Versenden bei Beendigung des Timers, der ist komplett aus dem Rest rausgenommen.

Du könntest die beiden Startbedingungen auch in eine Abfrage übernehmen

if ((IndoorTemp > 23) && (TempEmailSend == ""))

verlierst damit aber die Möglichkeit auf >23 ohne TempEmailSend zu reagieren, bzw. bräuchtest dafür ein komplettes zweites Konstrukt.
Na mal sehen, was draus machst und ob das geht...

1 Like

Ja, das ist die bessere Variante.
Selbst wenn auch da der Zeitpunkt vor dem einschalten des Arduinos gesetzt wird.

Warum ist es besser?
Natürlich, da es immer das tut, was man will.

Nachtrag zu: static uint32_t previousMillis = -20000UL;
Habe nämlich zwischenzeitlich den Sprachstandard/Referenz daraufhin abgeprüft, ob ich mit meinen "Emotionen" daneben liege.

Wie man sieht, nimmt es keine Rücksicht auf das Vorzeichen.
Das Ergebnis ist damit "Implementation Defined", da eben die innere Darstellung von vorzeichenbehafteten Zahlen vom Prozessortype abhängt.

Wenn man jetzt annimmt, dass ALLE Prozessoren die Zweierkomplement Darstellung verwenden, dann ist meine emontionale Entrüstung überzogen.

So hab jetzt die Variante von my_xy_projekt in ein neues Programm übernommen.
Ich hab ein paar Dinge ausgebessert, leider wir das Email aber immer versand, es wird die Schleife also immer durchlaufen, somit stimmt etwas mit dem Setzen dder Zeit oder des Flag nicht.

Es waren im Code ein paar Fehler, zumindest hab ich die als Tipfehler eingestuft.
Es waren == anstelle von = beim zuweisen der Variable TempEmailSend.

Ich konnte den Fehler leider nicht herausfinden, vielleicht kann mir jemand sagen was ich hier falsch gemacht habe.

Natürlich könnte ich auch die INTEVALL.h verweden, aber ich wollte erst einen Test mit der variante von ma_xy machen.

Hier mal der aktuell Code:

//PINS am D1 Festlegen
const int sensorPin = A0; //Feuchtigkeitssensor

//Variable definieren
int sensorVal = 0; //Sensorwert
static uint32_t previousMillis = 0; //-3600000UL; 
String TempEmailSend; //Value String Variable für Email


void setup() {
  Serial.begin(115200);
  connection_state = WiFiConnect(ssid, password);
  if (!connection_state) // if not connected to WIFI
    Awaits();          // constantly trying to connect
}

void loop() {

  sensorVal = analogRead(sensorPin); //Sensorwert auslesen
  Serial.println(sensorVal);

  //Wenn Feuchtigkeitsmessung positiv Email senden
  if (sensorVal < 999)  {  // Kontakt wurde am Sensor gemessen 250 < > 1024 

    if (TempEmailSend == "" ) {  // Wenn die Var leer ist

      //Variable für Value zum Senden als Mail
      TempEmailSend = String(sensorVal);
      EMailSender::EMailMessage message;
      message.subject = "D1 Feuchtigkeitsmessung ALARM";
      message.message = "Es wurde ein Feuchtigkeitswert von  " + TempEmailSend + " gemessen, somit muss Wasser am Sensor anliegen!";
      EMailSender::Response resp = emailSend.send("gmx.at", message);  // Empfägermail
      Serial.println("Sending status: ");
      Serial.println(resp.status);

      // Email Sende status prüfen
      if (resp.status != 0)  {
        TempEmailSend = "";
      } else {
        previousMillis = millis();
      }

      Serial.println(resp.code);
      Serial.println(resp.desc);
      delay(100);
    }
  }

  if (millis() - previousMillis > 60000) {  // Eine Stunden sind 3600000 ms
    // Alternativ: if ((millis()-previousMillis>60000) || (sensorVal<999))
    TempEmailSend = "";
    Serial.println("TempEmailSend = leer");
  }

  delay(2000);
}

Danke!

EDIT:
Immer wenn ich hier Poste finde ich den Fehler, komisch.
Der Fehler war ich prüfe verkehrt den Status ab, es muss heissen

if (resp.status != 1)  {

dann funktioniert alles perfekt!

:wink:

Da ist nichts komisches dran.
Im Gegenteil: Dieser Vorgang ist weit verbreitet.

So weit verbreitet, dass er sogar einen eigenen Namen bekommen hat, zu einem Prinzip/Arbeitsanweisung erweitert wurde.

Rubber Duck Debugging