[SOLVED] TimeAlarm.h - Alarm. triggerOnce(time_t value, explicitAlarm);

I want to turn on a pump at 8 AM every Saturday, but have the pump on for a different amount of time each week.

For example:

Week 1:
8 AM - Saturday.
Pump 1 > On for 5 seconds, Off.

Week 2:
8 AM - Saturday.
Pump 1 > On for 10 seconds > Off.

Week 3:
8 AM - Saturday.
Pump 1 > On for 15 seconds > Off.

I’ve read the readMe.txt file in the TimeAlarm.h library TimeAlarms/readme.txt at master · PaulStoffregen/TimeAlarms · GitHub and have tried using all the various timer/alarm combos but nothing works.
I have a feeling my answer lies within the ExplicitAlarm() example in the library, but there are no examples I can find of this anywhere.

My problem is that all 3 of my alarms trigger at the same time (See attachment for a screenshot of my serial monitor)

Has anyone else gotten this to work successfully?

V. 1.8.5 - Compiles okay

// Libraries:
#include <TimeLib.h>
#include <TimeAlarms.h>

// Defines:
AlarmId Week1;
AlarmId Week2;
AlarmId Week3;

// Constants:
const int LED = 13; //On-board Arduino LED; Simulates running a pump.

//----------SETUP----------//
void setup()
{  
  Serial.begin(9600);
  setTime(8,29,0,1,1,11); // set time to Saturday 8:29:00am Jan 1 2011
  Week1 = Alarm.alarmOnce(dowSaturday,8,29,03, Liquid_Pump_Week_1);  //Starts the liquid pump function at 8:29:03 every Saturday.
  Week2 = Alarm.alarmOnce(dowSaturday,8,29,03, Liquid_Pump_Week_2);  //Starts the liquid pump function at 8:29:03 every Saturday.
  Week3 = Alarm.alarmOnce(dowSaturday,8,29,03, Liquid_Pump_Week_3);  //Starts the liquid pump function at 8:29:03 every Saturday.
}

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

void Liquid_Pump_Week_1()
{
  digitalWrite(LED, HIGH);
  Serial.println("Week 1: Simulated pump is ON");
}

void Liquid_Pump_Week_2()
{
  digitalWrite(LED, HIGH);
  Serial.println("Week 2: Simulated pump is ON");
}

void Liquid_Pump_Week_3()
{
  digitalWrite(LED, HIGH);
  Serial.println("Week 3: Simulated pump is ON");
}

void digitalClockDisplay() 
{
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println();
}

void printDigits(int digits) 
{
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

My problem is that all 3 of my alarms trigger at the same time (See attachment for a screenshot of my serial monitor)

If that is a problem, why did you define them to all trigger at the same time?

The time library knows that the current time/date is a Saturday, or not. It hasn't a clue whether it is the first Saturday, the second Saturday, etc. Why do you imagine that it should?

What do you expect to do on the 4th Saturday? The 5th Saturday?

@PaulS
Are you the author of the TimeAlarm.h btw?

I was defining all 3 to trigger at the same time because I was under the impression that by having the different alarm Id's, the library might be able to differentiate between each, but I guess that's not how it works.

And I also was thinking that by setting the time in the sketch to:

  • setTime(8,29,0,1,1,11); // set time to Saturday 8:29:00am Jan 1 2011
    That the library would know that the current day was the first Saturday of the month (1, 1)

As for what it's supposed to do on the other Saturdays (4, 5, 6, etc.); not do anything.

How is this thing supposed to keep track of time over the long term?
Are you going to add an RTC module, such as for example the DS3231 ?
What happens if the power goes out?

How do you intend to set the time? Will you change your code every time you reset the time?

If you don’t intend to use an external RTC, and you are willing to hand-modify the code every time you wish to reset the time, then why do you even need TimeAlarms?

I would use something like this. (WARNING: untested code)
Actually, I would make it simpler than this: most of the complexity is from debug statements and such, which I put in only because you appear to want reassurance that everything is working properly.

Notice that I changed the pin number for the output. You can, of course, change it as you please.

const unsigned long ONE_SECOND =      1000UL;
const unsigned long ONE_MINUTE =     60000UL;
const unsigned long ONE_HOUR   =   3600000UL;
const unsigned long ONE_DAY    =  86400000UL;
const unsigned long ONE_WEEK   = 604800000UL;

// Next is the pin number for your output.
// You can change it if you wish.
const int myOutputPin = 12; 

// Next line is where you decide how long to wait before starting.
unsigned long startTime = 2 * ONE_MINUTE;
// I said wait 2 minutes, but you can modify this.

void setup() {
  // set up Serial for debug messages
  Serial.begin(9600);
  
  // Remember to declare your output pin as an output!
  pinMode(myOutputPin, OUTPUT);
  
  // wait until it's time to start
  unsigned long millisCheck = millis();
  while (millisCheck < startTime) {
    Serial.print("Been waiting ");
    prettyPrintTime(millisCheck);
    Serial.println("");
    delay(150); // so as not to scroll messages too fast
    millisCheck = millis();
  }
  Serial.println("Waited long enough!!");
  Serial.println("Time to start our routine!!");
}

void loop() {
  unsigned long routineClock = millis() - startTime;
  Serial.print("T = ");
  prettyPrintTime(routineClock);
  Serial.println(", ");
  
  if (routineClock < (5 * ONE_SECOND)) {
    digitalWrite(myOutputPin, HIGH);
    Serial.println("*** pump ON (Week 1) ***");
  }
  else if (routineClock < (1 * ONE_WEEK)) {
    digitalWrite(myOutputPin, LOW);
    Serial.println("pump OFF");    
  }
  else if (routineClock < ((1 * ONE_WEEK) + (10 * ONE_SECOND))) {
    digitalWrite(myOutputPin, HIGH);
    Serial.println("*** pump ON (Week 2) ***");
  }
  else if (routineClock < (2 * ONE_WEEK)) {
    digitalWrite(myOutputPin, LOW);
    Serial.println("pump OFF");    
  }
  else if (routineClock < ((2 * ONE_WEEK) + (15 * ONE_SECOND))) {
    digitalWrite(myOutputPin, HIGH);
    Serial.println("*** pump ON (Week 3) ***");
  }
  else if (routineClock < (3 * ONE_WEEK)) {
    digitalWrite(myOutputPin, LOW);
    Serial.println("pump OFF");    
  }
  else {
    startTime += (3 * ONE_WEEK);
    Serial.println("restarting routine");
  }
  delay(150); // so as not to scroll messages too fast
}

void prettyPrintTime(unsigned long t) {
  unsigned long leftover = t;
  Serial.print((leftover / ONE_DAY), DEC);
  Serial.print("d ");
  leftover %= ONE_DAY;
  if (leftover < (10 * ONE_HOUR)) {
    Serial.print(" ");
  }
  Serial.print((leftover / ONE_HOUR), DEC);
  Serial.print("h ");
  leftover %= ONE_HOUR;
  if (leftover < (10 * ONE_MINUTE)) {
    Serial.print(" ");
  }
  Serial.print((leftover / ONE_MINUTE), DEC);
  Serial.print("m ");
  leftover %= ONE_MINUTE;
  if (leftover < (10 * ONE_SECOND)) {
    Serial.print(" ");
  }
  Serial.print((leftover / ONE_SECOND), DEC);
  leftover %= ONE_SECOND;
  Serial.print(".");
  if (leftover < 10) {
    Serial.print("00");
    Serial.print(leftover);
  }
  else if (leftover < 100) {
    Serial.print("0");
    Serial.print(leftover);
  }
  else {
    Serial.print(leftover);
  }
  Serial.print("s ");  
}

@PaulS
Are you the author of the TimeAlarm.h btw?

No.

I was defining all 3 to trigger at the same time because I was under the impression that by having the different alarm Id's, the library might be able to differentiate between each,

The library knows which alarm is triggered. But, since all three trigger at the same time, and call different functions, I don't see what knowing which one triggered the function will do you.

And I also was thinking that by setting the time in the sketch to:

  • setTime(8,29,0,1,1,11); // set time to Saturday 8:29:00am Jan 1 2011
    That the library would know that the current day was the first Saturday of the month (1, 1)

Assuming that January 1st, 2011 was a Saturday, the library knows to trigger the functions on a Saturday. It does NOT know whether that Saturday is the first one in the month.

YOU need to do some thinking, instead of trying to rely on libraries to do all the thinking for you. YOU will need to figure out, if today is Saturday, how many times this month that has been true, and act accordingly.

@odometer
I do plan on adding a RTC into the fray once I can figure out the triggering issue. Good catch though, thank you.
Also, +1 Karma for your code, sir! But that was total overkill because I'm not here to have you guys do everything for me, I'm simply trying to learn and absorb what I can. But damn, that's a great sketch to learn from!!
I loved what you did with the millis and the if/else's. I didn't think of trying that direction. But it makes total sense.
One question; what's the purpose of line 85; the leftover = t;? Is that just dealing with the leftover milliseconds?
(lol @ prettyPrintTime btw :D)

@PaulS
Oh, okay. I just saw that the author of the library was PaulStoffregen. Just adding 2 and 2.
And thank you very much for the direction. I understand what you're saying about me needing to figure out how to tell the library which Saturday of the month it is, but it looks like the author already thought of this and wrote it into the library. No need to do double work, right?

In the TimeAlarm.h readme.txt it says:
"If you want to trigger once at a specified date and time you can use the trigger Once() method:
Alarm. triggerOnce(time_t value, explicitAlarm); // value specifies a date and time. (See the makeTime() method in the Time library to convert dates and times into time_t)"

@odometer - I believe this is what you were doing with your code by converting time units into unsigned long (UL) numbers.

but it looks like the author already thought of this and wrote it into the library.

I don't see that. You can set an alarm to fire on Saturday. When it goes off, you know that it is Saturday, and you know what time/date it is. If the day is 7 or less, you know that it is the first Saturday of the month. If the day is between 8 and 14, it is the second Saturday of the month. If the day is between 15 and 21, it is the third Saturday of the month. If the day is between 22 and 28, it is the fourth Saturday of the month. If the day is above 28, it is the 5th Saturday of the month.

So, ((day-1) / 7) + 1 will tell you which Saturday of the month today is, when today is Saturday.

@PaulS & @odometer

We did it!!! +1 Karma all around :slight_smile:
You guys both helped me find the solution and it actually works now! I can trigger an event on the exact day and time I want using the TimeAlarms.h library found here (GitHub - PaulStoffregen/TimeAlarms: Time library add-on, schedule alarms to occur at specific dates/times)
I used an Epoch Unix Time Stamp Converter - whatever that is. I’m going to research more into that - found here (Unix Time Stamp - Epoch Converter) to enter in the day and time I wanted to trigger my specific event. The converter changed the actual date/time into one big number, I put that into the library and BOOM. Magically it worked!

Here’s my code so anyone having the same issue can figure out the solution as well!
V. 1.8.5

// Libraries:
#include <TimeLib.h>
#include <TimeAlarms.h>

// Defines:
AlarmId Week1;
AlarmId Week2;
AlarmId Week3;

// Constants:
const int LED = 13; //On-board Arduino LED; Simulates running a pump.

//----------SETUP----------//
void setup()
{  
  Serial.begin(9600);
  setTime(8,29,0,1,1,11); // set time to Saturday 8:29:00am Jan 1 2011
  Alarm.triggerOnce(1293870543, Liquid_Pump_Week_1);  //Starts the liquid pump function at 8:29:03, Saturday, 1,1, 2011.
  Alarm.triggerOnce(1293870549, Liquid_Pump_Week_2);  //Starts the liquid pump function at 8:29:09, Saturday, 1,1, 2011.
//  Week3 = Alarm.triggerOnce(dowSaturday,8,29,03, Liquid_Pump_Week_3);  //Starts the liquid pump function at 8:29:03 every Saturday.
}

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

void Liquid_Pump_Week_1()
{
  digitalWrite(LED, HIGH);
  Serial.println("Week 1: Simulated pump is ON");
}

void Liquid_Pump_Week_2()
{
  digitalWrite(LED, HIGH);
  Serial.println("Week 2: Simulated pump is ON");
}

void Liquid_Pump_Week_3()
{
  digitalWrite(LED, HIGH);
  Serial.println("Week 3: Simulated pump is ON");
}

void digitalClockDisplay() 
{
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println();
}

void printDigits(int digits) 
{
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

Btw, @PaulS, I never got the chance to try your last post’s equation. I understand where you were going with it, but didn’t actually try it. I will though. Always up for more learning! :slight_smile:

DR2727:
@PaulS & @odometer

I used an Epoch Unix Time Stamp Converter - whatever that is. I'm going to research more into that - found here (Unix Time Stamp - Epoch Converter) to enter in the day and time I wanted to trigger my specific event. The converter changed the actual date/time into one big number, I put that into the library and BOOM. Magically it worked!

It converts a date/time into the number of elapsed seconds since Jan 1, 1970.
It does the same thing as the makeTime() function in your time library.

@blh64
Thank you sir.
I was actually reading about that. Just out of curiosity, any idea why they choose to begin counting from 1,1,1970 and not something more recent?

DR2727:
@blh64
Thank you sir.
I was actually reading about that. Just out of curiosity, any idea why they choose to begin counting from 1,1,1970 and not something more recent?

It's call a Unix time. When was Unix invented?