Time.Alarms Lib

Hab beim “RumSketchen” mit der Time.Alarms Lib etwas rausgefunden:

Wer denkt er braucht die Wartezeit

Alarm.delay(1000); // wait one second between clock display

im loop nicht ist im Irrtum! :o Ohne dieses Delay funktioniert die Lib nicht mehr. Kann man sich sonst totsuchen…

Alarm.delay(0); funktioniert aber auch schon… :wink:

.......
.......
........
void setup()
{
  Serial.begin(9600);
  setTime(8,29,0,1,1,11); // set time to Saturday 8:29:00am Jan 1 2011
  // create the alarms 
  Alarm.alarmRepeat(8,30,0, MorningAlarm);  // 8:30am every day
  Alarm.alarmRepeat(17,45,0,EveningAlarm);  // 5:45pm every day 
  Alarm.alarmRepeat(dowSaturday,8,30,30,WeeklyAlarm);  // 8:30:30 every Saturday 

 
  Alarm.timerRepeat(15, Repeats);            // timer for every 15 seconds    
  Alarm.timerOnce(10, OnceOnly);             // called once after 10 seconds 
}

void  loop(){  
  digitalClockDisplay();
  Alarm.delay(1000); // wait one second between clock display
}

// functions to be called when an alarm triggers:
void MorningAlarm(){
  Serial.println("Alarm: - turn lights off");    
}
..........
...........
..........

Wo Du es schreibst, finde ich es auch im Kleingedruckten: "Alarms and Timers are only checks and their functions called when you use this delay function. You can pass 0 for minimal delay."

Also gut, es mal hervorzuheben :slight_smile:

Mir schießt gerade die Frage durch den Kopf, ob man einen gesetzten Alarm auch löschen kann.

Hallo,

Alarme kann man löschen mit entsprechenden Registerzugriff auf die RTC.
Ob das diese Lib kann sehe ich im Moment nicht, sollte sie aber können, sonst gebe es einen Daueralarm.
Die RTC setzt den Alarm jedenfalls nicht selbst zurück.

Hallo,

aktuell schießt mir in den Kopf, dass die Lib die Alarme auch rein Software technisch machen könnte. Die RTC also nur zum auslesen der Zeit und Datum dient. Dann müßte man nichts an den Registern ändern.

Doc_Arduino:
aktuell schießt mir in den Kopf, dass die Lib die Alarme auch rein Software technisch machen könnte.

Genau das tut sie. Mit der RTC hat das erst mal nichts zu tun. Die Zeitquelle kann alles mögliche sein. Also z.B. auch NTP oder Funkuhr

Noch eine Stolperfalle: die Lib macht standardmäßig nur (glaube ich) maximal 5 Alarme. Wenn man mehr will muss man das im Header einstellen.

iIch hab auch mal mit der lib gespielt. Aber mit händisch geschriebenen Alarmen bin ich besser dean, da man flexibler ist.

Serenifly:
Noch eine Stolperfalle: die Lib macht standardmäßig nur (glaube ich) maximal 5 Alarme. Wenn man mehr will muss man das im Header einstellen.

"Q: How many alarms can be created?
A: Up to six alarms can be scheduled.
The number of alarms can be changed in the TimeAlarms header file (set by the constant dtNBR_ALARMS,
note that the RAM used equals dtNBR_ALARMS * 11)"

"#define dtNBR_ALARMS 6 // max is 255"

Wusste nicht dass das auch automatisch geht - ich habe bei meinem Wecker zwei Alarme manuell programmiert, welche aber über die Tage laufen. D.h. ich habe einen Wochen- und einen Wochenend-Alarm. Dazu kommt noch ein "Urlaubsmodus", welcher beide Alarme überschreibt (wenn man z.b. eine woche nicht da ist, geht man in Urlaubsmodus ohne Weckzeit/Ton, damit der Wecker die Nachbarn nicht jeden Morgen wach bimmelt :smiley: ).

Sprengerventile letzten Herbst eingebuddelt & Empfängersteuerung optimiert.

Jetzt wollte ich eigentlich meine zentrale Funk-Sprengersteuerung mit dem ESPnodeMCU in Betrieb nehmen…

Und:

Plötzlich entdecke ich Fehler über Fehler… grrr…

Ich nutze zur Zeitsteuerung die TimeAlarms Lib.

Nehme ich die Steuerung in Betrieb werden auch die eingestellten Alarmzeiten ausgeführt. Aber nur 1x obwohl Alarm.alarmRepeat angegeben ist. Nächsten Tag also keine Ausführung mehr.

Die Variablen mit StartStunde, Startminute, Dauer, Umlauf sind aber noch richtig eingestellt.

Die Anzahl der maximalen Alarme habe ich in der Lib eingestellt.

#if defined(AVR)
#define dtNBR_ALARMS 100 // max is 255
#else
#define dtNBR_ALARMS 100 // assume non-AVR has more memory
#endif

Das Unterprogramm zur Zeiteinstellung wird auch durchlaufen - das sehe ich in meinem OLED “Protokoll”.

Bin am Verzweifeln… Habt ihr einen Tip für mich?

Der ganze Sketch ist zu groß - ich habe mal den betreffenden Abschnitt eingestellt:

// Alarmzeiten ausrechnen & einstellen -------------
void initAlarmzeiten() {
  //erst mal alte Alarmzeiten löschen
  for (int y=0; y<=255; y++) {
    Alarm.free(y);                     // free up ID - Alarm freigeben, löschen
  }
  int x=0;
  for (int z=0; z<Umlauf; z++) {
    //Serial.println();
    for (int i=1; i<=4; i++) {
      switch (i) {
        case 1:
          Alarm.alarmRepeat(((StartStunde)+((StartMinute+((x)*Dauer))/60)),((StartMinute+((x)*Dauer))%60),0, Sprenger1);  // Schaltzeit einstellen
         break;
        case 2:
          Alarm.alarmRepeat(((StartStunde)+((StartMinute+((x)*Dauer))/60)),((StartMinute+((x)*Dauer))%60),0, Sprenger2);  // Schaltzeit einstellen
         break;
        case 3:
          Alarm.alarmRepeat(((StartStunde)+((StartMinute+((x)*Dauer))/60)),((StartMinute+((x)*Dauer))%60),0, Sprenger3);  // Schaltzeit einstellen
         break;
        case 4:
          Alarm.alarmRepeat(((StartStunde)+((StartMinute+((x)*Dauer))/60)),((StartMinute+((x)*Dauer))%60),0, Sprenger4);  // Schaltzeit einstellen
         break;
      }
      x++;
    }
  }
  Alarm.alarmRepeat(((StartStunde)+((StartMinute+((x)*Dauer))/60)),((StartMinute+((x)*Dauer))%60),0, Sprenger4aus);  // Schaltzeit einstellen
  Alarm.alarmRepeat(14,00,0, UhrzeitNeu);       //Mittags akt. Zeit neu einstellen (Unterprogramm)
  Scroll("Sprenger Zeiten neu  ");              //Unterprogramm aufrufen um String in Display zu schreiben
}

Hmm, schade - keine Tips zur Fehlerfindung?!

Werde wohl nicht drum herumkommen Time.Alarms rauszuschmeißen und Schaltzeiten "per Hand" zu programmieren?!
Hat jemand Tips dazu?

Hallo,

mit dem Codeausschnitt kann man nichts anfangen.
Die einfachste Form zum auslesen wäre …

/*
Arduino Mega2560
I2C RTC DS3231
*/

#include <Wire.h>
#define ds3231_adress  0x68   // Adresse

unsigned int Sekunde, Minute, Stunde, Wochentag, Tag, Monat, Jahr;
char RtcDateTimeBuf[26];  // Buffer zum Zwischenspeichern der formatierten DS3231 Werte

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

void loop() 
{
  RtcRequestEverySecond();
}


void RtcRequestEverySecond()
{
 static unsigned long last_millis = 0;
 
 if (millis()-last_millis < 10000) return;  // Zeit noch nicht erreicht, Funktion abbrechen
 last_millis+=10000;                        // addiere 10sec 
 
 if (read_RTC_DS3231 (ds3231_adress) == true) {    // RTC auslesen, wenn Fehlerstatus "wahr", dann
   Serial.println("DS3231 I2C Busfehler");
 }
 else {
   // Datum und Zeit für Ausgabe formatieren und in globalen String 'RtcDateTimeBuf' speichern
   snprintf(RtcDateTimeBuf,26,"%02d.%02d.%04d ; %02d:%02d:%02d",Tag,Monat,Jahr,Stunde,Minute,Sekunde);   
   Serial.println(RtcDateTimeBuf);
 }  
  
} // Ende "RtcRequestEverySecond"
 
 
 
boolean read_RTC_DS3231 (int i2c_adresse)
{
  boolean error = false;                   // Fehlerstatus setzen
  Wire.beginTransmission(i2c_adresse);     // Connect
  Wire.write(0);                           // Anfrage ab/der Register Nummer
  if (Wire.endTransmission() > 0 )         // war Connect fehlerfrei?
   { 
    error = true;                          // I2C Busfehler
    return error;                          // Abruch
   } 
   
  Wire.requestFrom(i2c_adresse, 7);        // 7 Bytes in Folge anfordern/lesen

  if (Wire.available() > 0 )               // sind Daten vorhanden?
    {
     Sekunde   = bcdToDec(Wire.read() & 0x7F);  // Maske für die ersten 7 Bits alleine
     Minute    = bcdToDec(Wire.read() );
     Stunde    = bcdToDec(Wire.read() & 0x3F);  // Umschaltung auf 24h statt 12h (AM/PM)
                          Wire.read();          // wäre der Wochentag
     Tag       = bcdToDec(Wire.read() );             
     Monat     = bcdToDec(Wire.read() );
     Jahr      = bcdToDec(Wire.read() ) + 2000;
    }    
  return error;  
}

byte bcdToDec(byte val)  // Hilfsfunktion zum Lesen/Schreiben der RTC
// Convert binary coded decimal to decimal number
// Hilfsfunktion für die Echtzeituhr
{
  return ( (val/16*10) + (val%16) );
}

mit Zeitenvergleich bauste dir deine Schaltuhr rein in Software.

jurs hatte dazu auch schon ganze Romane geschrieben.

Hast Recht. Fehler finde ich warscheinlich eh nicht…

Also selber bauen:

Aus dem loop heraus wird jetzt jede volle Minute auf Alarmzeiten geprüft.


if (minute() != MinuteAlt)
{
initAlarmzeiten(); // Unterprogramm Alarmzeitenabfrage nur jede neue Minute
MinuteAlt = minute();
}

Leider erkennt mein Programm die Alarmzeiten nicht. Grrr…

Wenn ich mir mit Scroll die Werte im OLED anzeigen lasse stimmt es: z.B. 10 10 13 13 (soll/istwert für Stunde/Minute)
Aber die If Abfrage ist nie erfüllt. Das Unterprogramm Sprenger1 wird nie aufgerufen. Heul…

Ich mache bestimmt irgend etwas Doofes falsch - aber ich komme nicht drauf.

Scroll(String(hour(), DEC));
Scroll(String( ( (StartStunde)+( (StartMinute+((x)*Dauer)) /60) ), DEC) );
//
Scroll(String(minute(), DEC));
Scroll(String( ((StartMinute+((x)*Dauer))%60) , DEC) );

if ((hour() == ((StartStunde)+((StartMinute+((x)*Dauer))/60))) && (minute() == ((StartMinute+((x)*Dauer))%60)))
{
Sprenger1;
}

// Alarmzeiten ausrechnen & einstellen -------------
void initAlarmzeiten() {
  int x=0;
  for (int z=0; z<Umlauf; z++) {
    //Serial.println();
    for (int i=1; i<=4; i++) {
      switch (i) {
        case 1:
          Scroll(String(hour(), DEC));
          Scroll(String(    (    (StartStunde)+(     (StartMinute+((x)*Dauer))    /60)    ), DEC)    );
          //
          Scroll(String(minute(), DEC));
          Scroll(String(    ((StartMinute+((x)*Dauer))%60)  , DEC)    );
          //
          if ((hour() == ((StartStunde)+((StartMinute+((x)*Dauer))/60))) &&  (minute() == ((StartMinute+((x)*Dauer))%60)))
          {
            Sprenger1;
          }
          //Alarm.alarmRepeat(((StartStunde)+((StartMinute+((x)*Dauer))/60)),((StartMinute+((x)*Dauer))%60),0, Sprenger1);  // Schaltzeit einstellen
         break;
        case 2:
          if ((hour() == ((StartStunde)+((StartMinute+((x)*Dauer))/60))) &&  (minute() == ((StartMinute+((x)*Dauer))%60)))
          {
            Sprenger2;
          }
          //Alarm.alarmRepeat(((StartStunde)+((StartMinute+((x)*Dauer))/60)),((StartMinute+((x)*Dauer))%60),0, Sprenger2);  // Schaltzeit einstellen
         break;
        case 3:
          if ((hour() == ((StartStunde)+((StartMinute+((x)*Dauer))/60))) &&  (minute() == ((StartMinute+((x)*Dauer))%60)))
          {
            Sprenger3;
          }
          //Alarm.alarmRepeat(((StartStunde)+((StartMinute+((x)*Dauer))/60)),((StartMinute+((x)*Dauer))%60),0, Sprenger3);  // Schaltzeit einstellen
         break;
        case 4:
          if ((hour() == ((StartStunde)+((StartMinute+((x)*Dauer))/60))) &&  (minute() == ((StartMinute+((x)*Dauer))%60)))
          {
            Sprenger4;
          }
          //Alarm.alarmRepeat(((StartStunde)+((StartMinute+((x)*Dauer))/60)),((StartMinute+((x)*Dauer))%60),0, Sprenger4);  // Schaltzeit einstellen
         break;
      }
      x++;
    }
  }
  if ((hour() == ((StartStunde)+((StartMinute+((x)*Dauer))/60))) &&  (minute() == ((StartMinute+((x)*Dauer))%60)))
          {
            Sprenger4aus;
          }
  //Alarm.alarmRepeat(((StartStunde)+((StartMinute+((x)*Dauer))/60)),((StartMinute+((x)*Dauer))%60),0, Sprenger4aus);  // Schaltzeit einstellen
  Alarm.alarmRepeat(14,00,0, UhrzeitNeu);       //Mittags akt. Zeit neu einstellen (Unterprogramm)
  Scroll("Sprenger Zeiten neu  ");              //Unterprogramm aufrufen um String in Display zu schreiben

Hallo,

deine Funktion verstehe ich leider nicht. Durch die vielen Klammern auch schlecht lesbar.
Meine Vermutung ist, die Klammersetzung ist nicht wie gewollt und führt zum falschen Ergebnis.
Wenn man die Punkt vor Strich Regel einhält, kann man sich dem Klammerkaos teilweise entziehen.

 if ( (hour() == (x*Dauer/60+StartMinute+StartStunde)) && ... )

Warum prüfst du nicht einfach die aktuelle Uhrzeit mit einer Alarmzeit?
Ein ganz einfaches Bsp., was noch ausgebaut werden muss.
Wenn die RTC mindestens 2x in der Minute abgefragt wird, sollte damit schon etwas passieren.

if ( Alarm_Stunde == RTC_Stunde && Alarm_Minute == RTC_Minute) {
  // mach was
}

Doc_Arduino:
Hallo,

deine Funktion verstehe ich leider nicht. Durch die vielen Klammern auch schlecht lesbar.
Meine Vermutung ist, die Klammersetzung ist nicht wie gewollt und führt zum falschen Ergebnis.
Wenn man die Punkt vor Strich Regel einhält, kann man sich dem Klammerkaos teilweise entziehen.

 if ( (hour() == (x*Dauer/60+StartMinute+StartStunde)) && ... )

Warum prüfst du nicht einfach die aktuelle Uhrzeit mit einer Alarmzeit?
Ein ganz einfaches Bsp., was noch ausgebaut werden muss.
Wenn die RTC mindestens 2x in der Minute abgefragt wird, sollte damit schon etwas passieren.

if ( Alarm_Stunde == RTC_Stunde && Alarm_Minute == RTC_Minute) {

// mach was
}

Ja, das mit den Klammern ist chaotisch. Ich habe es aber reichlich geprüft. Hatte ja auch vorher mit Alarm.Repeat funktioniert.

Das mit dem

if ( Alarm_Stunde == RTC_Stunde && Alarm_Minute == RTC_Minute) {
// mach was
}
mache ich ja eigentlich schon so.
Ist aber etwas tricky weil es keine feste Anzahl von Alarmen gibt. Die gebe ich variabel mit meinem Smartphone per Wifi vor. Das Programm errechnet sich dann die Alarme mit dem etwas undurchschaubarem Code.

Hmm, ich werde nochmal die Klammersetzung prüfen/überarbeiten...

Wird ja immer lustiger.... >:(

Ich kann leider auf meinem ESP keine Ausgabe an den Ser Monitor machen da die Schnittstelle belegt ist.

Ich habe daher mal ein Testprogramm auf meinen Leonardo Pro Micro geladen um damit auf dem ser. Monitor zu überwachen.

Das hier läuft auf dem Micro und wird von der If Abfrage richtig erkannt:

Serial.println(number);
Serial.print("akt:");
Serial.print(hour());
Serial.print(":");
Serial.println(minute());
Serial.print("berechnet:");
Serial.print(((StartStunde)+((StartMinute+((x)*Dauer))/60)));
Serial.print(":");
Serial.println(((StartMinute+((x)*Dauer))%60));
if ((hour() == int(((StartStunde)+((StartMinute+((x)*Dauer))/60)))) && int((minute() == ((StartMinute+((x)*Dauer))%60))))
{
Serial.println("Alarm!");
}

Ich habe die Zeitberechnung als Integerzahl umgewandelt - seit dem geht es auf dem Micro. Alarm.Repeat hat das vorher in der Lib warscheinlich gemacht - jedenfalls ging es ohne extra int.

Leider geht es auch mit der int Umwandlung nicht auf meinem ESP:

if ((hour() == int(((StartStunde)+((StartMinute+((x)*Dauer))/60)))) && int((minute() == ((StartMinute+((x)*Dauer))%60))))
{
Sprenger1;
}

Hallo,

was möchtest du damit überhaupt berechnen? Stunden? Minuten? Sekunden?

(((StartStunde)+((StartMinute+((x)*Dauer))/60))

Ich kann dir zur Fehlersuche nur raten

  • Klammern abspecken
  • Formel zerlegen zum debuggen
  • Datentypen kontrollieren
  • Datentyp Überläufe prüfen

stoni99:
Wird ja immer lustiger.... >:( .......

Leider geht es auch mit der int Umwandlung nicht auf meinem ESP:

if ((hour() == int(((StartStunde)+((StartMinute+((x)*Dauer))/60)))) && int((minute() == ((StartMinute+((x)*Dauer))%60))))
{
Sprenger1;
}

Ahhhh, ohhhh:

Nach einigen hundert grauen Haaren mehr:

Das int ist schon richtig und war auch die Lösung.

Ich habe aber einfach nur die Klammer bei Sprenger1(); vergessen.

Doc_Arduino:
Hallo,

Alarme kann man löschen mit entsprechenden Registerzugriff auf die RTC.
Ob das diese Lib kann sehe ich im Moment nicht, sollte sie aber können, sonst gebe es einen Daueralarm.
Die RTC setzt den Alarm jedenfalls nicht selbst zurück.

Alarm wird rein über die Software gemacht, RTC wird nicht unbedingt benötigt.
Ich aktualisiere die Zeit mit einem NTP-Client, Alarme werden mit der Lib gesetzt - funktioniert soweit.

Hat schon wer herausgefunden wie das geht mit dem Löschen von gesetzen AlarmEvents?
@stoni99: für deine Regnerzeiten neu setzen musst du doch das auch was am Start haben. Wie machst du das?

wenn ich das richtig sehe, kann man den deepsleep auch nicht verwenden wenn man mit dem Smartphone wifizugriff haben will. Oder gibts für den ESP eine Art wakeon(w)lan
@stoni99: deine Regnersteuerung läuft 24/7?

Hallo,

immer wieder schön wenn man mit alten Aussagen zitiert wird die in diesem Thread längst keine Rolle mehr spielen.

@Doc_Arduino: sorry, war keine böse Absicht!