ropg / ezTime

ezTime — pronounced "Easy Time" — is a very easy to use Arduino time and date library that provides NTP network time lookups, extensive timezone support, formatted time and date strings, user events, millisecond precision and more.
MIT License
327 stars 92 forks source link

Daily Alarm? #9

Open chinswain opened 5 years ago

chinswain commented 5 years ago

Would it be possible to set a daily repeating alarm without specifying the day, month and year?

I'm using Paul's time and timeAlarm libraries plus some hacky time zone adjustments. (https://github.com/PaulStoffregen/TimeAlarms)

 Alarm.alarmRepeat(8,30,0, MorningAlarm);  // 8:30am every day
 Alarm.alarmRepeat(17,45,0,EveningAlarm);  // 5:45pm every day 

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

void EveningAlarm(){
  Serial.println("Alarm: - turn lights on");           
}

I'm currently trying with this - which triggers continuously when the time arrives. How should I be setting up a daily repeating alarm?

#include <ezTime.h>
#include <WiFi.h>
Timezone myTZ;
void setup() {

  Serial.begin(115200);

  WiFi.begin("", "");

  waitForSync();
  Serial.println();
  Serial.println("UTC:             " + UTC.dateTime());

  myTZ.setLocation(F("gb"));
  Serial.print(F("United Kingdom:         "));
  Serial.println(myTZ.dateTime());
  myTZ.setDefault();

  // Set the event to trigger for the first time
  setEvent( mistOneStart, pumpOn() );
  setEvent( mistOneStop, pumpOff() );
}

void loop() {

  events();

}

void mistOneStart() {
  Serial.print(F("Start! "));
  Serial.println(myTZ.dateTime());
  setEvent( mistOneStart, pumpOn() );
}

time_t pumpOn() {
  int8_t d = myTZ.day() ;
  int8_t m = myTZ.month();
  int16_t y = myTZ.year();

  time_t t = 0;
  t = makeTime(1, 04, 0, d, m, y);
  return t;

}

void mistOneStop() {
  Serial.print(F("Stop! "));
  Serial.println(myTZ.dateTime());
  setEvent( mistOneStop, pumpOff() );
}

time_t pumpOff() {
  int8_t d = myTZ.day() ;
  int8_t m = myTZ.month();
  int16_t y = myTZ.year();

  time_t t = 0;
  t = makeTime(1, 04, 30, d, m, y);
  return t;

}
ropg commented 5 years ago

How about:

#include <ezTime.h>
#include <WiFi.h>
Timezone myTZ;

void setup() {
  WiFi.begin("SSID", "PW");
  waitForSync();
  myTZ.setLocation(F("gb"));
  myTZ.setDefault();
  time_t one_oh_four_am = makeTime(1,4,0, day(), month(), year());
  if (now() >= one_oh_four_am) one_oh_four_am += 24*3600;
  setEvent(mistOneStart, one_oh_four_am);
  setEvent(mistOneStop, one_oh_four_am + 30);
  ...
}

void loop() {
  events();
}

void mistOneStart() {
  setEvent(mistOneStart, now() + 24 * 3600);
  ...
}

void mistOneStop() {
  setEvent(mistOneStop, now() + 24 * 3600);
  ...
}

Each function, when called, sets itself to run again 24 hrs later and all should be good. I didn't test, let me know if this works for you...

chinswain commented 5 years ago

Thanks for the reply Rop,

The Alarm library has a repeat function that allows a weekly or daily trigger and ignores the day, month and year so by creating the event it will always trigger as expected (i.e as soon as that time is reached daily).

I think I simplified my example too much - neglected to include the fact that the times can be adjusted on the fly.

I have a touch screen that updates 8 sets of integers (HH, MM & SS) which are then stored to EEPROM. As these can be updated before or after the event has occurred for the day I think think adding a day will work. Here's a more detailed (non working attempt) example:

#include <ezTime.h>
#include <WiFi.h>
Timezone myTZ;

struct config_settings {
  uint8_t  mist1StartHour;
  uint8_t  mist1StartMinute;
  uint8_t  mist1StartSecond;

  uint8_t  mist1StopHour;
  uint8_t  mist1StopMinute;
  uint8_t  mist1StopSecond;
} MySettings;

void setup() {

  Serial.begin(115200);

  WiFi.begin("", "");

  waitForSync();
  Serial.println();

  myTZ.setLocation(F("gb"));
  Serial.print(F("United Kingdom:         "));
  Serial.println(myTZ.dateTime());
  myTZ.setDefault();

  Serial.println("UTC:             " +  myTZ.dateTime());

  // Set the event to trigger for the first time
  setEvents();
}

void loop() {

  events();

  if (User_Updates_Values) {
    deleteEvents();
    setEvents();
  }
}

void setEvents() {
  setEvent( mistOneStart, pumpOn() );
  setEvent( mistOneStop, pumpOff() );
}

void deleteEvents() {
  deleteEvent(mistOneStart());
  deleteEvent(mistOneStop());
}

void mistOneStart() {
  Serial.print(F("Start! "));
  Serial.println(myTZ.dateTime());
  setEvent( mistOneStart, pumpOn() );
}

time_t pumpOn() {
  int8_t d = myTZ.day() ;
  int8_t m = myTZ.month();
  int16_t y = myTZ.year();

  time_t t = 0;
  t = makeTime(MySettings.mist1StartHour, MySettings.mist1StartMinute, MySettings.mist1StartSecond, d, m, y);
  return t;
}

void mistOneStop() {
  Serial.print(F("Stop! "));
  Serial.println(myTZ.dateTime());
  setEvent( mistOneStop, pumpOff() );
}
time_t pumpOff() {
  int8_t d = myTZ.day() ;
  int8_t m = myTZ.month();
  int16_t y = myTZ.year();

  time_t t = 0;
  t = makeTime(MySettings.mist1StopHour, MySettings.mist1StopMinute, MySettings.mist1StopSecond, d, m, y);
  return t;
}

For the Alarm library, I do the following:

 // create the alarms on boot
  mist1AlarmID = Alarm.alarmRepeat(MySettings.mist1StartHour, MySettings.mist1StartMinute, MySettings.mist1StartSecond, mistOnAlarm); // ON
  mist2AlarmID = Alarm.alarmRepeat(MySettings.mist1StopHour, MySettings.mist1StopMinute, MySettings.mist1StopSecond, mistOffAlarm); // OFF

//Update if any alarm times changed by user
  time_t mist1StartTime = AlarmHMS(MySettings.mist1StartHour, MySettings.mist1StartMinute, MySettings.mist1StartSecond) ; // ON
  time_t mist1StopTime = AlarmHMS(MySettings.mist1StopHour, MySettings.mist1StopMinute, MySettings.mist1StopSecond) ; // OFF
  Alarm.write(mist1AlarmID, mist1StartTime);
  Alarm.write(mist2AlarmID, mist1StopTime);
ropg commented 5 years ago

I could maybe at some point build a more generic periodic alarm trigger thing. But knowing me, I would then also want to be able to set an alarm for "Monthly, every second Wednesday at 10:00" and such.

But as for your thing: I'm not sure I understand what the exact remaining problem is: You now have a way of setting an event for a given time and a way for repeating the alarm 24 hrs later. If you want to change the time, simply deleteEvent the event and start over. Everything else is just the plumbing in between. Right?

chinswain commented 5 years ago

It would certainly be appreciated - I'm sure quite a few people will be using this for alarms\triggers. Something else I was thinking about, is it possible to prevent an event running if it's been missed? I see from your notes past missed events are still triggered?

Would you mind creating an example showing how to trigger an event at the same time each day (Taking the time zone into account) that will not trip up if the trigger time is changed on the fly? With the above, if I remove and recreate the trigger an hour or so before it's scheduled it won't fire on that day.

I have a water pump that needs to come on for xx seconds (user configurable) each day at the same time. I currently perform a DST check and adjust the hour as required.

ropg commented 5 years ago

Allright: I'll add recurring events sometime soon. I'll think how to make it clean, simple and maximally flexible.

As for an example: I'm not sure I understand: if I check if a time has already happened today and set it for tomorrow instead if it has (like I did above with if (now() >= one_oh_four_am) one_oh_four_am += 24*3600;) that covers it, or not?

khawajamechatronics commented 4 years ago

Hi,

I came here after having issues with TimeAlarm Library. I am trying to understand how library works. Is there examples available for daily alarms?

Thanks

khawajamechatronics commented 4 years ago

@ropg Can I use setEvent like this setEvent(patternProcess(patternCount), one_oh_four_am);

I want to setup multiple daily alarms. I want them to process some variables when initiate the alarm. Ideally it can be variable passed like patterncount above.

Also will it setup different alarm based on value of patterncount?

When I setup like this it seems there is compilation error.

Is something possible like this ?

Thanks for your help.

ropg commented 4 years ago

setEvent only takes a function and a time as arguments. The function is essentially just the address of that function in memory. Without raising MAX_EVENTS in ezTime.h, you have 8 events. You could make 8 event functions, I guess, and then store any values you like to have available for when they trigger separately. I guess I could make it such that it could pass its 8-bit alarm handle back out to the function, that way you know which alarm is being trigered.

I'll leave this issue open, so I'll remember when I get to a bigger update.

robeastwood commented 3 years ago

Any update on adding functionality for repeating daily events, like for alarms? I appreciate there are ways of doing, but they seem a bit clunky and manual in comparison to the nice clean functions of the rest of the library. One thing to consider, it's made more complicated a by the timezone changes, for example just setting an event to run in +24 hours will go off at the wrong time when the clocks go backwards/forwards.

robeastwood commented 3 years ago

For anyone else looking for a solution, this is basically what I ended up doing. It wakes up twice per day, once at the actual alarm time, and again at midnight to determine the right alarm time for that day.

void loop() {
  // run events to handle alarms
  events();
}
void handleAlarm(){

  // determine today's alarm time
  time_t alarm = makeTime(alarm_hour,alarm_min,alarm_sec, day(), month(), year());

  // is it time to do something?
  if(now()>=alarm) {

    // do the alarm thing!

    // then wake up again tomorrow
    alarm = makeTime(0,0,0, day(), month(), year())+86400;
  }

  // set next time to wakeup
  wakeUp = setEvent(handleAlarm, alarm);

}
Spyker2000 commented 8 months ago

I saw a post from "Chinswain" in October 2018 with regards to setting up daily repeating alarms. He had issues with the timers looping continuously. I was doing a similar timer concept and was also struggling to get the code working. After lots of experimenting, I managed to get the code working. I thought I would post my code to help anyone else who is having issues.

I read the ezTime manual many, many times but could not get it right. I then used Cinswain's code and went from there.

Understanding how the setEvent() function works was the main issue. In my code, I used two setEvents as follows:-

myTZ.setEvent(PumpTimerStart, pumpON() ); myTZ.setEvent(PumpTimerStop, pumpOFF() );

When the above setEvent code is executed for the first time, it executes the pumpON() function and sets up the trigger time and returns this as a time to the second part of the setEvent function. i.e. pumpON in effect becomes 18, 17, 0, d, m, y. This is then done with the second setEvent but using the PumpOFF() function. PumpOFF in effect becomes 18, 18, 0, d, m, y. The setEvent function's now wait and wait. When pumpON() reaches 18hrs 17mins and 0 seconds, the setEvent function executes the PumpTimerStart() function. This turns on the pump (LED in my case) and the then creates The myTZ.setEvent( PumpTimerStop, pumpOFF()); The same process now starts again using the second set event.

I have just got this working a few hours ago and and will fiddle around a bit more i.e. I am not sure if I need the second setEvent(PumpTimerStop....... as the last line of pumpTimerStart function sets it up again.

I now need to setup the program to run qty x 48 on/off timers at specific times during a 24 hour cycle.

`

include

include

Timezone myTZ;

define LED 2

void setup() { Serial.begin(115200); pinMode (LED, OUTPUT);

while (!Serial) { ; // wait for Serial port to connect. Needed for native USB port only }

WiFi.begin("xxxxx", "yyyyyy"); //setDebug(INFO);

waitForSync();

myTZ.setLocation(F("Africa/Johannesburg")); delay(5000); Serial.print("The JHB time is: "); Serial.println(myTZ.dateTime()); myTZ.setDefault();

myTZ.setEvent(PumpTimerStart, pumpON() ); myTZ.setEvent(PumpTimerStop, pumpOFF() ); }

void loop() { events(); }

void PumpTimerStart() { Serial.print(F("You are in the PumpTimerStart() function at: ")); Serial.println(myTZ.dateTime()); digitalWrite(LED, HIGH); Serial.println(F("Led Light turned on")); myTZ.setEvent( PumpTimerStop, pumpOFF()); }

void PumpTimerStop() { Serial.print(F("You are in the PumpTimerStop() function at: ")); Serial.println(myTZ.dateTime()); digitalWrite(LED, LOW); Serial.println(F("Led Light turned off")); }

time_t pumpON() { int8_t d = myTZ.day(); int8_t m = myTZ.month(); int8_t y = myTZ.year(); time_t t = 0; t = makeTime(18, 17, 0, d, m, y); Serial.println("You are in the pump ON function"); return t ; }

time_t pumpOFF() { int8_t d = myTZ.day(); int8_t m = myTZ.month(); int8_t y = myTZ.year(); time_t t = 0; t = makeTime(18, 18, 0, d, m, y); Serial.println("You are in the pump OFF function"); return t; } `