Wecker: Zeit bis zum nächsten Alarm berechnen

Hi, ich hab hier mal eine kleine Problemstellung, und hoffe ein paar Tipps bekommen zu können :slight_smile:

Ich bastel ja immernoch an meinem Wecker mit TFT-Display. Das Projekt ist schon recht weit gekommen, aber ein paar letzte Herausforderungen bleiben noch.

Die größte davon wäre, zu berechnen wieviel Zeit bis zum nächsten Alarm bleibt...

Momentan siehts so aus, dass ich 3 Weckzeiten einstellen kann.
Jede der 3 Weckzeiten speichert die eingestellte Stunde und Minute in eigenen INT-Variablen, außerdem die Wochentage, an denen der Wecker klingeln soll (Mo-So, einzeln anwählbar) in einer BYTE-Variable. Sprich wenn er Sonntag, Montag und Dienstag klingeln soll sind die Bits 0, 1 und 2 auf 1 und die restlichen auf 0. Außerdem gibts noch für jeden Wecker eine Variable in der steht, ob er überhaupt eingeschaltet ist.
Die aktuelle Uhrzeit wird auch in INT-Variablen gespeichert, jedesmal wenn ich sie von der RTC auslese.

Es sind also sämtliche Informationen in übersichtlichen Variablen verfügbar.
Prinzipiell wäre die Berechnung auch kein Problem, kompliziert wird es nur dadurch, dass er eben nicht zwangsläufig jeden Tag klingelt, sondern die aktiven Wochentage frei auswählbar sind.
Und genau an der Stelle endet mein Horizont... Vielleicht denk ich auch nur zu kompliziert?

Ich bin über jeden Tipp dankbar :slight_smile: Evtl auch über leichte Schläge auf den Hinterkopf, wenn sie denn meinen Denkapparat in Schwung bringen ^^

Einen Alarm auszulösen ist dann wiederum kein Problem, dazu muss ich ja nur ist-und sollzeit vergleichen.

Und ja ich weiß, die Funktion ist für den Wecker an sich nicht wichtig, ich möchte sie aber trotzdem gerne haben, einfach für den komfort :slight_smile:

Habe ich dich richtig verstanden du bist dir nicht sicher wie du die Abfrage machen sollst welcher Wecker wann klingeln soll?

Abfrage Wecker 1:

IF (Werktag1 != Aktueller Tag) return;
else
{
Heute muss der Wecker Klingeln ! !

IF (WerkStunde1 != Aktuelle Stunde) return;
else
{
Diese Stunde muss Geweckt werden
IF (WerkMinute1 == Aktuelle Minute) Jetzt Wecken;
} // IF WerkStunde1
} // IF Werktag1

kann auch sie das ich dich falsch verstandene habe.

und das hast du in deinem Loop drin so das es ständig geprüft wird. ,-)

Nein da hast du mich falsch verstanden :wink:

Was ich gerne hätte ist letzten Endes eine Anzeige auf dem Display in der Richtung "Der nächste Wecker klingelt in 42 Stunden". Dürfte natürlich auch gerne heißen "1 Tag, 17 Stunden". Das ist dann völlig egal. Die Umrechnung und Ausgabe ist ja dann wieder kein Problem mehr, mein Problem ist eben nur zu berechnen, wieviel Zeit noch bis zum nächsten Alarm bleibt. Eine Art Countdown.

Meine Ansätze dafür sind irgendwie alle zu kompliziert und würden in seitemlangen Code enden...

Ah Ok, Sorry.

Frage woher weist du denn welche Urzeit gerade ist und welcher Tag gerade ist?
Wenn ich zu meinem Arduino sage sag mir mal Tag und Urzeit dann Schaut mich das nur doof an.

Wird über die RTC-Libary von der RTC ausglesesen und dann in Variablen gespeichert, etwa so

  DateTime datetime=RTC.now();
  new_day = datetime.day(),DEC;
  new_month = datetime.month(),DEC;
  new_year = datetime.year(),DEC;
  new_dow = datetime.dayOfWeek(),DEC;
  new_hour = datetime.hour(),DEC;
  new_min = datetime.minute(),DEC;
  new_sec = datetime.second(),DEC;

Ah, Cool.

Ich kenne mich mit so was nicht so gut aus.

Was mir dabei aber einfällt ist, kannst du das nicht in einen Zeitstempel umrechnen?

Dein Zeitstempel ist dann z.B. immer reine Woche lang

Zeitstempel = 7 Tage a 24h a 60 min a 60 Sekunden
Zeitstempel = 604.800 Sekunden

Jetzt kannst du deine Werkzeiten auch als Zeitstempel hinterlegen.

Beispiel:

Weken1 = Mo., 1 Uhr 15 Min und 0 Sekunden

Zeitstempel.Weken1 = 4500 Sekunden // 1h 15 min = 75 min a 60 Sekunde

Jetzt kannst du gut umrechnen Welcher Wecker schon vorbei ist und wie viele Sekunden du bis zum Nächsten Wecker hast, die Sekunden kannst du dann wider in Tage und Stunden etc. umrechnen.

Hoffe das ich es diesmal richtig verstanden habe.

Die idee mit dem Zeitstempel hatte ich schon, allerdings hätte ichs auf Minuten umgerechnet :wink: Aber das ist ja prinzipiell zweitrangig.

Das ganze aber auf wöchentlicher Basis zu machen könnte eine gute Möglichkeit sein, da muss ich mir mal ein bisschen den Kopf dran zerbrechen...
Dabei sollte ich dann aber die Woche mit Sonntag beginnen lassen, auch wenn sie überall anders bei Montag beginnend angezeigt wird. Das dürfte die ganze Sache ein Stück leichter machen, da die RTC den Sonntag mit 0 ausgibt, den Montag mit 1 usw und den Samstag dann mit 6

Das Schwierige dabei ist nur nach wie vor, mit den Wochentagen klarzukommen.
Weil es ja frei einstellbar ist, an welchen Wochentagen der Wecker klingeln soll. Und die Information so aufzuarbeiten dass ein zuverlässiges Ergebnis rauskommt macht mir Schwierigkeiten.

Das Teil läuft wahrscheinlich schon und du möchtest es nur erweitern, sonst hätte ich gesagt Peichere die Werkzeit in einem Arrey und nimm direkt den Zeitstempel denn du Hinterlegst.

Für die Anzeige der Werkzeit kannst du dann Weiderum eine Umrechnung machen in Stunden und Minuten und an welchen Tag das ist.

z.B.
der TimeStempel ist in Minuten

Sonntag: 0:00 Uhr --> TimeStempel = 0
Sonntag: 12:00Uhr --> TimeStempel = 720
Sonntag: 23:59 Uhr --> TimeStempel = 1439

Wenn du jetzt die Werkzeit aufrufst TimeStempel_Werkzeit = 5874
dann wäre das also umgerechnet:

Display ausgab:
Tage = TimeStempel_Werkzeit / 1440
Tage = 4,079

alles hinter dem Komma abschneiden, demnach Tag 4 deiner Woche, (Start ist So.) demnach hast du dann Mittwoch.

dann die Stunden:

Stunden = TimeStempel_Werkzeit - (Tage *1440)
Stunden = TimeStempel_Werkzeit - (4 *1440)
Stunden = (5874 - 5760)/60
Stunden = 144/60
Stunden = 2,4

Auch hier wider das Komma abschneiden
also Stunde 2!

dann hast du also den Rest noch das sind dann minuten.

Minuten = TimeStempel - (Stunden "ohne komma" + Tage "Ohne Komma")
Minute = 24

Demnach ist dann: TimeStempel_Werkzeit = Mittwoch (02:24 Uhr)

Ich wies das man das auch alles in einer Rechnung machen kann, jedoch habe ich keine Ahnung mehr wie das geht, denn in dem Fall ist der Wechsel auf den Nächten "Zähler" oder wie man das nennt nicht bei 100 sondern bei 60.
Das müssten aber die Matte Profis her mal sagen wie das geht.

ich hoffe das man das verstehen kann was ich meine.

@Zeitsklave:

Schau mal die time Lib
dort an.

Mal die Header Datei ansehen, denke dort sind die Funktionen dabei die du suchst.

Danke, die Time lib schau ich mir mal genauer an :slight_smile:

Das Umrechnen an sich ist ja wie gesagt nicht das Problem.

Ich möchte nur programmtechnisch ermitteln, wann der nächste Alarmtermin ist :wink:

ich habe jetzt momentan die Wochentage, an denen der Wecker läuten soll in einem Byte gespeichert. Sieht dann so ähnlich aus:

byte weckzeit_wochentage = B00000010;

Hier ist der Wecker nur für Montag eingeschaltet. Mit

bitRead(weckzeit_wochentage,1);

erfahre ich also ob Montags geweckt werden soll oder nicht (rückgabewert natürlich 1 oder 0, im Beispiel wäre die Ausgabe 1).

Genauso für Sonntag (Mit der Ausgabe 0):

bitRead(weckzeit_wochentage,0);

oder Mittwoch (Ausgabe ebenfalls 0):

bitRead(weckzeit_wochentage,3);

Diese Methode schien mir am günstigsten zu sein, aber das lässt sich ja alles ändern.

Soo, angenommen die Variablen für die Weckzeit sehen folgendermaßen aus:

byte weckzeit_wochentage = B00000010;
int weckzeit_stunde = 8;
int weckzeit_minute = 30;

Gibt dann eine Weckzeit von 08:30 Uhr immer Montags.

← Soweit ist das jetzt kein Problem.

Jetzt nehmen wir mal an, die momentane Uhrzeit ist 18:00 am Samstag.
Sähe dann so aus:

int uhrzeit_stunde = 18;
int uhrzeit_minute = 0;
int wochentag = 6;
int monatstag = 9;
int monat = 5;
int jahr = 2015;

usw, diese Daten kommen in Wirklichkeit natürlich von der RTC.

Jetzt möchte ich eine Ausgabe erhalten, die in etwa so aussieht:

Nächster Wecktermin in 1 Tag, 14 Stunden, 30 Minuten

(Vorausgesetzt ich hab das jetzt im Kopf auf die Schnelle richtig gerechnet :stuck_out_tongue: )

Ich darf dabei natürlich nicht von einer bestimmten Anzahl von Wecktagen ausgehen, da die Variable weckzeit_wochentage individuell befüllt werden darf. Könnte also genausogut lauten

byte weckzeit_wochentage = B01111111;

oder

byte weckzeit_wochentage = B01010101;

Möglich ist genauso natürlich

byte weckzeit_wochentage = B00000000;

Was allerdings nicht heißt dass dieser Alarm abgeschalten ist, dafür gibts eine extra-Variable, z.B.

boolean weckzeit_ein = true;

Damit wäre der Alarm dann eingeschaltet.

So in etwa sieht das gerade bei mir aus.
Funktioniert auch alles super mit Weckzeiten einstellen und anzeigen usw. Wie dann der Alarm ausgelöst wird hab ich auch schon exakt im Kopf (nur noch nicht umgesetzt).

Dies würde sich lösen lassen wenn du beide Zeiten in das Unix Zeit Format umrechnest.
Also die Sekunden seit 1. Januar 1970.

Dann beide Werte subtrahieren, und schon hast du die Sekunden bis zum nächsten Alarm.

Wie gesagt in der Lib sind Macros für solche Sachen drin.

Hier ist ein Funktion dafür.

Dann müsste ich praktisch für jede Weckzeit und für jeden Wochentag eine LONG definieren, in der ich den jeweiligen Stempel für die Weckzeit pro Wochentag speichere. Für einen ausgeschalteten Wochentag würde dann halt 0 drinstehen.
Das wären dann bei 3 Weckzeiten 21 Variablen, die ich bei jedem Durchlauf (bzw in einem definiertem Intervall natürlich, aber sinnvollerweise mindestens minütlich) frisch berechnen müsste. Regelmäßig frisch berechnen weil wenn der Montag um ist brauch ich ja dann die Zeit für den nächsten Montag.

OK, das scheint mir am naheliegensten zu sein, aber ist das nicht ein bisschen aufwändig? Und Rechenzeit- und Speicherraubend?

Die Macros die ich in der Lib gefunden hab, sind ja im Prinzip auch nur simple Kalkulationen.
Meintest du die?

/* Useful Macros for getting elapsed time */
#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)  
#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) 
#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
#define dayOfWeek(_time_)  ((( _time_ / SECS_PER_DAY + 4)  % DAYS_PER_WEEK)+1) // 1 = Sunday
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY)  // this is number of days since Jan 1 1970
#define elapsedSecsToday(_time_)  (_time_ % SECS_PER_DAY)   // the number of seconds since last midnight 
// The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971
// Always set the correct time before settting alarms
#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY)  // time at the start of the given day
#define nextMidnight(_time_) ( previousMidnight(_time_)  + SECS_PER_DAY )   // time at the end of the given day 
#define elapsedSecsThisWeek(_time_)  (elapsedSecsToday(_time_) +  ((dayOfWeek(_time_)-1) * SECS_PER_DAY) )   // note that week starts on day 1
#define previousSunday(_time_)  (_time_ - elapsedSecsThisWeek(_time_))      // time at the start of the week for the given time
#define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK)          // time at the end of the week for the given time

Dann müsste ich praktisch für jede Weckzeit und für jeden Wochentag eine LONG definieren, in der ich den jeweiligen Stempel für die Weckzeit pro Wochentag speichere.

So würde ich das machen - funktioniert auch so, weil ich auch eine Uhr mit Weckfunktion auf diese Weise gebaut habe.

Ob es einen eleganteren Weg gibt, wird sich zeigen wenn Jurs seine Meinung dazu äußert :slight_smile:

OK, dann werd ich das morgen einfach mal so probieren :wink:

Ich bedank mich schonmal für die Anregungen, und bleibe weiterhin gespannt auf neuen Input! :slight_smile:

Die Macros die ich in der Lib gefunden hab, sind ja im Prinzip auch nur simple Kalkulationen.
OK, das scheint mir am naheliegensten zu sein, aber ist das nicht ein bisschen aufwändig? Und Rechenzeit- und Speicherraubend?

Wie du sagst, es sind einfache Operationen deshalb würde ich die Rechenzeit eher vernachlässigen.

Eine unsigned long verbraucht 4 Bytes* X denke das frisst den Sram bei 3 Weckzeiten nicht auf.

Hallo,

und wie löst Du das Problem mit dem Wochenüberlauf? Also wenn heute Sonntag ist und die nächste Weckzeit erst Dienstag in einer Woche?

Ich bin schon ein stück weiter, ich teile jetzt mal mein Hirnschmalz mit euch ^^

int minuten_bis_weckzeit()
  {
  int x = 0;
  int ergebnis = 0;
  int alle_weckzeiten[21];
  aktueller_zeitstempel = new_hour * 60 + new_min;      // Nur aktualisieren
  for(int welcher_wecker = 1; welcher_wecker <= 3; welcher_wecker++)
    {
    for(int welcher_wochentag = 0; welcher_wochentag <= 6; welcher_wochentag++)
      {
      alle_weckzeiten[x] = minuten_bis_weckzeit_pro_wochentag(welcher_wecker,welcher_wochentag);
      x++;
      }
    }
  

  return ergebnis;
  }
  
int minuten_bis_weckzeit_pro_wochentag(int welcher_wecker, int welcher_wochentag)
  {
// Gibt 0 zurück, wenn Wecker oder Wochentag abgeschaltet ist, ansonsten die Minuten bis zum Alarm am Wecktag
  int my_hour = 0;
  int my_min = 0;
  int my_on = 0;
  byte my_days = B00000000;
  
  if(welcher_wecker == 1){  my_hour = weckzeit1_hour;  my_min = weckzeit1_min;  my_days = weckzeit1_days;  my_on = weckzeit1_on;  }
  if(welcher_wecker == 2){  my_hour = weckzeit2_hour;  my_min = weckzeit2_min;  my_days = weckzeit2_days;  my_on = weckzeit2_on;  }
  if(welcher_wecker == 3){  my_hour = weckzeit3_hour;  my_min = weckzeit3_min;  my_days = weckzeit3_days;  my_on = weckzeit3_on;  }

//          minuten von 0 uhr bis weckzeit + minuten übersprungener tage                            - vergangene minuten vom tag * tag aktiv?                     * wecker aktiv?
  int ergebnis = (((my_hour * 60) + my_min + ((7 - new_dow + welcher_wochentag) * minuten_pro_tag)) - aktueller_zeitstempel) * bitRead(my_days,welcher_wochentag) * my_on;
// Wenn die Zeit bis zum nächsten Alarm größer als 1 Woche ist, ist der Alarm noch heute:
  if(ergebnis > minuten_pro_woche){ergebnis = ergebnis - minuten_pro_woche;}
  return ergebnis;
  }

Ja ich weiß dass meine Variablennamen komisch sind :wink:

die Funktion minuten_bis_weckzeit() soll jetzt die Minuten bis zum nächsten Alarm ausgeben, bisher hab ich Die Minuten bis zu jedem Alarm in dem Array alle_weckzeiten gespeichert. Hier muss ich jetzt nur noch den kleinsten Eintrag außer Null finden und in der INT ergebnis speichern. Daran knabbere ich gerade.

Die minuten_bis_weckzeit() ruft ihrerseits wiederum für jeden Wochentag von jeder Weckzeit die Funktion minuten_bis_weckzeit_pro_wochentag(int,int) auf, spart so eine menge platz :wink:

Ich denke das könnte so funktionieren.

Das Problem mit dem Wochenüberlauf ist dadurch gelöst dass ich als Ausgangspunkt immer den Tagesbeginn um 0 Uhr als Basis hernehme (das ganze in Minuten, somit reicht mir auch ein INT statt LONG), und zum schluss noch vergleiche ob die zeit bis zum nächsten alarm mehr als eine Woche ist, in dem moment weiß ich dass ein Wochenüberlauf stattgefunden hat und kann was dagegen tun, nämlich einfach eine Woche vom Ergebnis abziehen.
Wenn ich manuell nachrechne funktioniert das auch. Ein endergebnis hab ich aber noch nicht, weil ich erst noch den endgültigen Wert berechnen will.

die globalen variablen, die hier wichtig sind, sind folgende:

// Hierin sind die Weckzeiten gespeichert, 0 sind nur startwerte, aktuelle Werte werden aus dem EEPROM gelesen und per Benutzereingabe eingestellt:
    byte weckzeit1_on = 0;
    byte weckzeit1_hour = 0;
    byte weckzeit1_min = 0;
    byte weckzeit1_days = B00000000;
    byte weckzeit2_on = 0;
    byte weckzeit2_hour = 0;
    byte weckzeit2_min = 0;
    byte weckzeit2_days = B00000000;
    byte weckzeit3_on = 0;
    byte weckzeit3_hour = 0;
    byte weckzeit3_min = 0;
    byte weckzeit3_days = B00000000;

// Selbsterklärend:
    int minuten_pro_woche = 10080;
    int minuten_pro_tag = 1440;

// Die beiden werden von der RTC gefüttert:
    int new_hour;
    int new_min;

// Vergangene Minuten des Aktuellen Tages:
    int aktueller_zeitstempel = new_hour * 60 + new_min;

Einige der INT könnt ich noch zu BYTE machen, aber momentan gibts keine Platzprobleme :wink:

Ich denke die Weckzeiten könnt ich auch noch in Arrays speichern, das würde vielleicht ein bisschen Text und ein paar if-Abfragen sparen, aber es funktioniert eigentlich ganz gut soweit…

funktioniert:

// Den kleinsten Wert außer 0 von alle_weckzeiten[] herausfinden:
  int ergebnis = 0;
  for(int pos=0; pos < 21; pos++)
    {
    if(alle_weckzeiten[pos] > 0 && (alle_weckzeiten[pos] < ergebnis || ergebnis == 0))
      {ergebnis = alle_weckzeiten[pos];}
    }
  return ergebnis;

:grin:

Edit:
der vollständigkeit halber hier noch der Codeschnipsel der das dann sauber auf den Schirm bringt:

    int naechster_wecker = minuten_bis_weckzeit();
    int naechster_wecker_tage = 0;
    int naechster_wecker_stunden = 0;
    int naechster_wecker_minuten = 0;
    int naechster_wecker_rest_minuten = 0;
    if(naechster_wecker > 0)
      {
      tft.setTextSize(font_size_weck_ind);
      tft.setTextColor(color_weck_ind, color_background);
      tft.setCursor(pos_weck_next_x, pos_weck_next_y);
      tft.print("             ");
     
      tft.setTextColor(color_weck_ind, color_background);
      tft.setCursor(pos_weck_next_x, pos_weck_next_y);

      naechster_wecker_tage = naechster_wecker / minuten_pro_tag;
      naechster_wecker_rest_minuten = naechster_wecker % minuten_pro_tag;
      naechster_wecker_stunden = naechster_wecker_rest_minuten / 60;
      naechster_wecker_minuten = naechster_wecker_rest_minuten % 60;
      
      if(naechster_wecker_tage > 0)
        {
        tft.print(naechster_wecker_tage);tft.print("t");
        if(naechster_wecker_minuten > 0 || naechster_wecker_stunden > 0){tft.print(", ");}
        }
      if(naechster_wecker_stunden > 0)
        {
        tft.print(naechster_wecker_stunden);tft.print("s");
        if(naechster_wecker_minuten > 0){tft.print(", ");}
        }
     if(naechster_wecker_minuten > 0)
        {
        tft.print(naechster_wecker_minuten);tft.print("m");
        }
      }

Bisschen plump, aber tut was es soll :smiley:

Vielen dank für die ganzen Anregungen, habt mir sehr geholfen!! :slight_smile: