I need a timer to make sure a garage door goes completely open or closed within a specified travel time. There are two limit switches, one activated when the door is closed and one activated when the door is open. Both are Normally Open. When the door starts going up, the bottom limit switch will go open (upper switch is already open). Both are open during door travel. When the door is completely open, the bottom limit switch will be open and the upper switch will be closed.
Timer Requirements: I want to set up a timer such that, if the door doesnāt go completely open or closed within X seconds after starting, I will get an alert. If the door goes completely open or closed in less than X seconds, the timer expires and doesnāt trigger anything. The timer should be reused on the next door opening or closing. Or the timer could be destroyed and a new timer created for the next door opening or closing.
I donāt need entire code (Iāve got a Garage Door State Machine running) - just need help with the BlynkTimer functions to be able to start, expire/stop, and restart a timer.
Function setTimeout(unsigned long d, timer_callback f) looked promising, but āafter the callback f has been called, the interval is deleted, therefore the value timerId is no longer valid.ā (per the Arduino SimpleTimer Library Docs.
I tested setTimeout below and Timer2 is no longer enabled after f has been called.
Do you have any ideas how to code this using BlynkTimer? Should I delete a timer and create a new one?
Thanks for any help on this!
#include <WiFiNINA.h>
#include <BlynkSimpleWiFiNINA.h>
int timer2;
BlynkTimer timer;
void OnceOnlyTask() {
Serial.println("This timer only triggers once");
// Debug print lines:
Serial.print("Timer number: ");
Serial.println(timer2);
Serial.print("Timer is enabled: ");
Serial.println(timer.isEnabled(timer2));
Serial.println("Restarting timer");
timer.restartTimer(timer2);
}
void setup() {
Serial.begin(9600);
Serial.println("SimpleTimer Example");
Serial.println("Timer2 is set to trigger only once after 1 second");
Serial.println();
timer2 = timer.setTimeout(1000, OnceOnlyTask);
}
void loop() {
timer.run();
// Is timer2 enabled after f executes?
if ((millis() % 3000) == 0){
Serial.print("Timer is enabled: ");
Serial.println(timer.isEnabled(timer2));
}
}
Replying to my own question aboveā¦
After a bit of reflection, I realized that this is a very basic timer function that should probably be performed without the library. I took this inspiration from Robin2ās suggestion in Simple timer function for arduino; Reply #1 where he wrote āHave a look at the demo several things at a time which illustrates how to manage timing with millis(). It is quite straightforward. You could wrap that up in a library if you want to but it will just make the code longer and more complex for the same end result. Libraries are great when they work but a real PITA when they donāt. I prefer to spend my time fixing my problems rather than trying to figure out what a library is doing that it should not be doing (or vice versa).ā
A few basic lines of code solved it (excerpts below from my finite state machine).
#1: In state DOOR_OPEN, when door starts to close, calculate the maximum permissible end timeā¦
if (limitSwitchOpenState == HIGH) { // Door starting to close
timeDoorTravelEnd = millis() + maxTravelTime; // Time door has to be closed by
branchToState(DOOR_LOWERING);
}
#2: then, in state DOOR_LOWERING, watch for timer expiration or door going closed, whichever happens firstā¦
if (millis() > timeDoorTravelEnd) { // Timer expired
branchToState(DOOR_FAULT); // Move to Door Fault state
}
if (limitSwitchClosedState == LOW) { // Door fully closed
branchToState(DOOR_CLOSED); // Move to Door Closed state
}
In your case you are using that type of timer in the wrong place.
Timers do NOT need to be started in the setup() it is just that that is the best place to start interval timers that run from then on.
But as you discovered, a setTimeout() timer is a one shot deal. If you want to run it again, your simply use the same command again, wherever it is most useful in your code.
However, as far as possible bugs go in both SimpleTmer and BlynkTimer, I have noticed that when using Timer IDās and associated ID based commands (most notably deleteTimer() in my experiments) that it will completely stop the first timer called in your codeā¦ apparently never to work again?? To āsolveā this, I simply create a first sacrificial timeout timer that does nothingā¦
Just WOW! This is terrible. No wonder I was pulling my hair out trying to use BlynkTimer. I donāt know how much longer I would have played around before stumbling onto that bug.
Since I have to include the Blynk library anyway, is there any memory overhead penalty for using BlynkTimer compared to the two lines of millis() and a defined interval (such as I did above)?
Set timer expiration timeā¦
timeDoorTravelEnd = millis() + maxTravelTime;
then, have we passed the expiration-time?
if (millis() > timeDoorTravelEnd) { // Timer expired
branchToState(DOOR_FAULT); // Move to Door Fault state
}
Iām sure the Blynk people are extremely busy, but a bug like this should have been squashed long ago (speaking as a noob to Blynk).
It is a SimpleTimer bug (I have tested it with non-Blynk code), so it wasnāt really Blynkās initial issue. And probably not a common use case (with IDs and special commands) so not as prominent an issueā¦
Heck, took me over a year of fiddling with various fancy timer based routines to finally realise there even was an issue!! (and not just from my strange experiments), let alone what it was or how to solve it. But hopefully it can be figured out eventually.