Would the TimeAlarms be the best fit?

Hello all,

I hope this post finds you all well and in good spirits. I am new to the world of Particle and have decided to integrate the Photon into a few projects of mine, as well as a project for a client, so I bought three of them to play around with. I have successfully gotten them all connected and have made a few different demonstrations with them and have had some fun with LED bar graphs, and buttons, and buzzers, and the like to familiarize myself with the workings of the Photon. Next up I am taking on a project that requires scheduling.

Ultimately I am looking to create a program that waits for a high signal from a momentary button that begins the timers instead of them beginning with initialization of the Photon. These timers will control three individual systems:

System 1 - Will utilize a digital pin to switch on an SSR for 1 minute to power an external load. And then again 2 weeks after the initial button press, 4 weeks after the initial button press, and then each week thereafter.

System 2 - Will utilize a digital pin to switch on an SSR for a period of 16 hours daily to power an external load.

System 3 - Will utilize two digital pins to switch on two Mosfets one week after the initial button press for 5 minutes. And then thereafter switch the Mosfets on every 12 hours for 5 minutes.

I have been playing around with the example code from the TimeAlarms library via the Web IDE and in Atom and it seems pretty powerful. I just do not know the best way to go about this project, and if this library is suitable for bi-weekly scheduling. I have not been able to locate any examples of such.

Thoughts?

Hi @Rex.iLL

I would say that Time Alarms is a reasonable fit for the things you have mentioned. Your requirements are simple enough that you could just work with the built-in Time object which uses time from the cloud in seconds since a start date. For your relative timing, you would record the start time from Time.now() and then compare it to that time plus some number of seconds, so for instance one week is 7x24x60x60 = 604800 seconds.

Thank you @bko

I am assuming that I would place such function in void setup() and have the call run upon the button press?

I am working on the structure of my code, I am far rustier than I had believed. Ha.

Hi @Rex.iLL

No, not in setup() but in loop(). You need to think of it like a state-machine: you start off in the idle (do nothing) state and when the button is pressed you enter the active state and capture the current time as the starting time. Then for every time around loop() you check if you current time is after the next event time (starting time plus whatever offset you need to get your day, week, etc.) and do that action. You will likely want to make some boolean variables for the actions so that you don’t do them more than once.

3 Likes

Thank you for that clarification @bko! I am going to get some code written up here and I will report back.

1 Like

Ok, So I have been playing around with this concept for a few hours now and can not seem to get this to work as I expected. With my code, the State change of D0 is detected just fine and the LED on D7 illuminates and switches with each button press and release respectively. Watching from the console, the Particle.publish functions successfully publish “System Started” and “Waiting.” I set the states of the button within the individual If statements inside of void loop() and they work as they should, this all leads to me believe that the loop and if statements are working? But I cannot seem to get the variable startTime to store the output from Time.now() and publish it to the Cloud. What am I doing wrong?

const int btn = D0;                      // button is connected to D0
const int LED = D7;                      // LED is connected to D1

int btnState = 0;                         // variable to store the read value
long startTime = 0;

void setup()
{
  pinMode(LED, OUTPUT);            // sets pin as output
  pinMode(btn, INPUT_PULLDOWN);    // sets pin as input
  Particle.publish("System Started", PRIVATE);
}

void loop()
{
  btnState = digitalRead(btn);          // read the input pin
   
    if (btnState == HIGH) {
        digitalWrite(LED, btnState);    // sets the LED to the button's value
        startTime = Time.now();
        Particle.publish("Cycle began at:", String(startTime), PRIVATE);
    } 
    
    if (btnState == LOW) {
        digitalWrite(LED, btnState);    // sets the LED to the button's value
        Particle.publish("Waiting", PRIVATE);
    }
  delay(50);                       // protect against bounce
}
//END 

What do you mean?
What do you expect and what do you get?

You also need to adhere the rate limit of max 1 publish per second.
Read this https://docs.particle.io/reference/firmware/photon/#particle-publish-

Since after each publish you wait max. 50ms your publishes will end up muted after four events.

This suggestion would have helped prevent this situation

Since you didn't guard against that your code produces an event every 50ms irrespective of change in state.

Thank you @ScruffR

I do believe this is what I am experiencing. After the system comes online I get exactly 4 events before I stop seeing any additional output irregardless of the state of btn.

As for the Boolean Variables, I hadn’t realized that it should be utilized here as well. I’m still knocking the cobwebs off. I will give these additions a go and trek on.

Hey there guys,

As I work on this project I have been playing around with a few different approaches. In my loop section as it is written, I have a call to functionWpm() within the if statement. This function was not being called by this statement when it was placed on any line below the digitalWrite command. What rule was I breaking here? What do I need to keep in mind to prevent such in the future?

void loop()
{
  btnState = digitalRead(btn); // read the input pin
   
    if (btnState == HIGH) { 
        delay(50); // protect against bounce
        functionWpm();
        digitalWrite(LED, btnState); // sets the LED to the button's value
        startTime = Time.now();
        Particle.publish("Cycle began at:", String(startTime), PRIVATE);
    } 
        else {
            Particle.publish("Waiting", PRIVATE);
        }

  delay(1000); // publish limit
}

By what we are seeing there should not by any difference whether you call that function before or after digitalWrite(LED, btnState);.
There might be other things a play tho’ - but since we don’t see the implementation of functionWpm() we can only guess
e.g.

  • does functionWpm() do anything to btnState?
  • does it depend on the state of LED?
  • …

BTW, the unconditional delay(1000) will impact responsivness to button presses - worst case a button press will take 1 second before it’s recognised. The proposed flag to only act on change-of-state would help address this too.

Thank you @ScruffR

I have since made some edits to the code and this is no longer an issue. I burnt a few hours trying to figure this one out to no avail. The call to functionWpm() worked just fine when placed in setup() but once moved back into the if statement the call seemingly was no longer made. I am testing this on a breadboard with a momentary button and a LED bar graph hooked up to the various outputs of the Photon that are being utilized.

But to answer your question, functionWpm() was written to turn the LED for one minute as such:

void functionWpm()
{
    digitalWrite(wpm, HIGH);
    delay(60000);
    digitalWrite(wpm, LOW);
}

I have rewritten the code like this (LED has been renamed to btnIndicate):

void loop()
{
  btnState = digitalRead(btn); // read the input pin
   
    if ((btnState == true) && (state != 0)) { 
        delay(50); // protect against bounce
        startTime = Time.now();
        digitalWrite(btnIndicate, btnState); // sets D7 LED to high for visual confirmation of cycle start
        state = 0;
        functionWpm(); // begins 
    } 
    
    if ((btnState == false) && (state != 1)) {
        delay(50);
        Particle.publish("Waiting", PRIVATE);
        state = 1;
    }
}

void functionWpm()
{
    Particle.publish("WPM cycle began at:", String(startTime), PRIVATE);
    digitalWrite(wpm, HIGH);
    delay(60000);
    digitalWrite(wpm, LOW);
}

These excessive delays are no good practice.
If you want a LED to go on and stay on for a longer period you’d rather switch it on in one function and then set up a timer that does the switching off after the correct amount of time.
You may want to have a look at Software Timers for that.

An alternative approach for your state flag could be this

Timer offTimer(60000, switchOff, true);

void loop() {
  static int prevBtn = -1;                // this local variable will keep its value between iterations
  int currBtn = digitalRead(btn);
  if (currBtn == prevBtn) return;         // no change in state, don't care for the rest of loop()
  prevBtn = currBtn;
  // now you know the state has changed  

  if (currBtn) {
    startTime = Time.now();
    Particle.publish("WPM cycle began at:", String(startTime), PRIVATE);
    digitalWrite(wpm, HIGH);
    offTimer.start();
  }
  else {
    Particle.publish("Waiting", PRIVATE);
  }

  digitalWrite(btnIndicate, currState); // sets D7 LED to reflect button state   
  delay(1000);                          // since both cases will publish, common delay
}

void swtichOff() {
  digitalWrite(wpm, LOW);
}
2 Likes

I will keep this in mind moving forward. I ONLY did it here because I was having issues with calling this function and wanted a quick and dirty test. (I was being lazy.) Now that I have this working sufficiently I will be doing better at these kinds of things.

Thank you for that alternative @ScruffR that helps me understand the timer function much better. I appreciate that insight.

So here is the final code, all timers work the way they should on the breadboard. I used shorter interval timers just to test functionality over the past twelve hours or so and have been periodically checking the particle console to monitor the time stamps and ensure there has not been any dwell. Any suggestions or improvements that you would recommend? Anything that I may have over looked?

Thanks.

const int btn = D0; // Button is connected to D0
const int glp = D2; // GLP system connected to D2
const int wpm = D4; // WPM system connected to D4
const int fan = D6; // FAN system connected to D6
const int btnIndicate = D7; // LED is connected to D1

int state; // variable to store the state of btn
int counterWpm = 0; // variable to store WPM counter
long startTime = 0; // variable to store time of intial button press
bool btnState = false; // variable to store the state of the button
  
Timer offWpm (60000, wpmFunction, true);  // sets period that the WPM system is active
Timer onWpm (60000, wpmOn, true);  // sets period that the WPM system is inactive 
Timer offGlp (90000, glpOff, true); // sets period that the GLP system is active
Timer onGlp (120000, glpOn, true); // sets period that the GLP system is inactive
Timer offFan (60000, fanOff, true); //sets period that the FAN system is active
Timer onFan (120000, fanOn, true); // sets period that the FAN system is inactive
Timer delayFan (180000, fanOn, true); // sets period that the FAN system waits before starting intial cycle


void setup()
{
  pinMode(wpm, OUTPUT); // control pin for WPM system
  pinMode(glp,OUTPUT); // control pin for GLP system
  pinMode(fan, OUTPUT); // control pin for FAN system
  pinMode(btnIndicate, OUTPUT); // sets D7 led as indicator
  pinMode(btn, INPUT_PULLDOWN); // sets pin as input for btn
  Particle.publish("SYSTEM STARTED", PRIVATE);
}

void loop()
{
  btnState = digitalRead(btn); // read the state of btn
   
    if ((btnState == true) && (state != 0)) { 
        delay(50); // protect against bounce
        digitalWrite(btnIndicate, btnState); // turns D7 led on for visual confirmation
        startTime = Time.now(); // sets startTime to time of intial button press
        intializeTimers(); // start all system timers
        Particle.publish("BUTTON PRESSED", PRIVATE);
        state = 0; // sets state
        return; // if statement is true, ignore rest of function
    } 
    
    if ((btnState == false) && (state != 1)) {
        delay(50); // protect against bounce
        Particle.publish("WAITING", PRIVATE);
        state = 1; // sets state
        return; // if statement is true, ignore rest of function
    }
}

void intializeTimers()
{
    digitalWrite(wpm, HIGH); // begins WPM ON cycle
    offWpm.start(); // intialize timer
    Particle.publish("WPM ON: Cycle will end in 1 minute.", PRIVATE);
    
    digitalWrite(glp, HIGH); // begins intial GLP ON cycle
    offGlp.start(); // intialize timer
    Particle.publish("GLP ON: Cycle will end in 1.5 minutes.", PRIVATE);
    
    digitalWrite(fan, LOW); // begins FAN delay period
    delayFan.start(); // intialize timer
    Particle.publish("FAN: Cycle will begin in 3 minutes.", PRIVATE);
}

void wpmOn()
{ 
    digitalWrite(wpm, HIGH); // turns WPM system on
    offWpm.start(); // intialize time
    Particle.publish("WPM ON: Cycle begin.", PRIVATE);
}

void wpmFunction()
{
    if (counterWpm == 0) {
        counterWpm++; // increments counterWpm by 1
        digitalWrite(wpm, LOW); // turns WPM system off
        onWpm.changePeriod(120000); // changes period until WPM sytem turns on
        onWpm.reset(); // resets timer onWpm
        Particle.publish("WPM OFF: Cycle (1) will begin in 2 minutes.", PRIVATE);
        return; // if statement is true, ignore the rest of the function
    }
    
    if (counterWpm == 1) {
        counterWpm++; // increments counterWpm by 1
        digitalWrite(wpm, LOW); // turns WPM system off
        onWpm.changePeriod(120000); // changes period until WPM sytem turns on
        onWpm.reset(); // resets timer onWpm
        Particle.publish("WPM OFF: Cycle (2) will begin in 2 minutes.", PRIVATE);
        return; // if statement is true, ignore the rest of the function
    }
    
    if (counterWpm == 2) {
        digitalWrite(wpm, LOW); // turns WPM system off
        onWpm.changePeriod(240000); // changes period until WPM sytem turns on
        onWpm.reset(); // rests timer onWpm
        Particle.publish("WPM OFF: Cycle will begin in 4 minutes.", String(startTime), PRIVATE);
        return; // if statement is true, ignore the rest of the function
    }
}

void glpOn()
{
    digitalWrite(glp, HIGH); // turns GLP system on
    offGlp.start(); // intialize timer
    Particle.publish("GLP ON: Cycle will end in 2 minutes.", PRIVATE);
}

void glpOff()
{
    digitalWrite(glp, LOW); // turns GLP system off
    onGlp.start(); // intialize timer onGLP
    Particle.publish("GLP OFF: Cycle will begin in 1.5 minutes.", PRIVATE);
}

void fanOn()
{
    digitalWrite(fan, HIGH); // turns FAN system on
    offFan.start(); // intialize timer
    Particle.publish("FAN ON: Cycle will end in 1 minute.", PRIVATE);
}

void fanOff()
{
    digitalWrite(fan, LOW); // turns FAN system off
    onFan.start(); // intialize timer
    Particle.publish("FAN OFF: Cycle will begin in 2 minutes.", PRIVATE);
}
//END

With all these “unguarded” Particle.publish() calls you may run the risk of hitting the rate limit.
Also since your timers are running at multiples of half a minute, you could set up a single 30000ms timer with
one FSM per target to cut down on overhead.

@ScruffR

How does one usually go about guarding such calls?

These timers in the final product are gonna span days/ weeks. But for my own personal knowledge how would you structure such a Timer - FSM system? How would you setup the count for multiples of the timer system? I figure I could implement such with a 12 hour timer?

If that is the case, then the use of a millis() timer (i.e. Timer class) would be subject to considerable drift relative to real time.

You may want to (as recommended before) use the RTC (real Time Clock) functions built in to your device. That will provide one second precision as to the start time of your event sequence.

Note that using the RTC, you will need to make a call to Particle.syncTime() at least daily, but you can do it several times a day, even hourly with little overhead on a Photon.

Trigger your series of events using the Real Time, and then if your precision is less important vis Ă  vis real time 1) you can use your Timer driven event sequence based on millis() or 2) have a Timer check the RTC, or 3) write a class that has settable callback times and events based on Real Time or millis().

In either case, as @ScruffR points out, developing a State Machine paradigm will enable you to programmatically see the sequence of events, rather than your overlapping timer approach in your last posted code.

So in summary, try a two step approach, a timing mechanism which uses Real Time as the event trigger (even taking steps to store your next event time in EEPROM in case of some power interruption) coupled with an event sequence that would be controlled with an overarching State Machine approach using either the Timer class or your own class.

2 Likes

So I had tried a similar approach to this as well, and at the end of the day, my understanding of the functions of time within the Photon isn’t up to par just yet. I was under the, now understood to be erroneous, impression that the timers worked with the RTC? I am not trying to shrug off any particular recommendations, I’m just making sense of all of this.

The functionality of the system isn’t dependent on time, as in these events don’t have to happen at certain times of the day or night, but rather at specified intervals from “start.” So I am thinking that an equation can be set up as the condition of an if statement that can check currentTime against startTime or rather subtract the times from one another and if the difference is greater than the period of the interval, execute that function. And then store currentTime into a prevTime variable to be checked against on the next loop. And utilize the timers to control the time that each system actually remains in the “on” state.

I was having issues getting certain Time functions to work, such as the timeStr() function; it came back as an invalid function? But that’s neither here nor there. The Time.now() is a function of the RTC correct? And returns the UNIX time; I now understand what @bko was trying to explain to me in regards to this, upon the initial recommendation I couldn’t see it.

If I am correct in this train of thought then I now know how I need to execute this program with the exception of the Finite State Machine. I have looked into these and have not been able to find good examples that break down the logic in a way that coincides with how I learn. But I am getting there.

I can place Particle.syncTime() within one of the GLP functions as this is a daily system.

Yeah?

1 Like

If you can show how you tried to use it and what the error message was, we may be able to provide the missing puzzle pieces.

One thing that hasn't been mentioned/asked yet: Does your device need to actually run during the "idle" time (between actions)? If not, you could also send the device to Stop Mode Sleep for the desired time and only wake when the next action is due.

1 Like

Hey @ScruffR,

I am not at my computer at the moment, but when I return I will attempt to reproduce the error message for you guys. I thank you for that assistance.

As for the latter, the device needs to keep the GLP system on for a period of 16 hours via a Mosfet connected. If this can be done with the system in Stop Sleep Mode, then the system can sleep until the next action needs to take place.