SimpleTimer Library problem

I’ve been trying to build some code that makes good use of the Simple Timer Library. Things seem to work as expceted with all the timer functions until I try to use the .setTimeout one. I’ve been calling
int numTimers = timer.getNumTimers(); at the bottom and the more i run .setTimeout timers, the more timer counts I get. Isn’t .setTimeout suppose to run once and than be deleted? So I went digging through the BlynkLibrary I’m currently using and I find this down on lines 181-188:

int SimpleTimer::setTimeout(unsigned long d, timer_callback f) {
    return setupTimer(d, (void *)f, NULL, false, RUN_ONCE);
}

int SimpleTimer::setTimeout(unsigned long d, timer_callback_p f, void* p) {
  return setupTimer(d, (void *)f, p, true, RUN_ONCE);
}

Ok… Where am I missing it here. where does the RUN_ONCE magic happen here in the run loop??


void SimpleTimer::run() {
    int i;
    unsigned long current_millis;

    // get current time
    current_millis = elapsed();

    for (i = 0; i < MAX_TIMERS; i++) {

        timer[i].toBeCalled = DEFCALL_DONTRUN;

        // no callback == no timer, i.e. jump over empty slots
        if (timer[i].callback != NULL) {

            // is it time to process this timer ?
            // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592

            if ((current_millis - timer[i].prev_millis) >= timer[i].delay) {

                unsigned long skipTimes = (current_millis - timer[i].prev_millis) / timer[i].delay;
                // update time
                timer[i].prev_millis += timer[i].delay * skipTimes;

                // check if the timer callback has to be executed
                if (timer[i].enabled) {

                    // "run forever" timers must always be executed
                    if (timer[i].maxNumRuns == RUN_FOREVER) {
                        timer[i].toBeCalled = DEFCALL_RUNONLY;
                    }
                    // other timers get executed the specified number of times
                    else if (timer[i].numRuns < timer[i].maxNumRuns) {
                        timer[i].toBeCalled = DEFCALL_RUNONLY;
                        timer[i].numRuns++;

                        // after the last run, delete the timer
                        if (timer[i].numRuns >= timer[i].maxNumRuns) {
                            timer[i].toBeCalled = DEFCALL_RUNANDDEL;
                        }
                    }
                }
            }
        }
    }

    for (i = 0; i < MAX_TIMERS; i++) {
        if (timer[i].toBeCalled == DEFCALL_DONTRUN)
            continue;

        if (timer[i].hasParam)
            (*(timer_callback_p)timer[i].callback)(timer[i].param);
        else
            (*(timer_callback)timer[i].callback)();

        if (timer[i].toBeCalled == DEFCALL_RUNANDDEL)
            deleteTimer(i);
    }
}

So it looks like there’s 3 types of timers: setTimer(Run_n times), setInterval(Run_Forever) and SetTimeout(Run_Once). Now I can see how the run forever and the run n times one’s work, but can someone show me how the Run_Once ones are handled and why they don’t seem to be deleting in my code after they’ve ran?

@Gunner 's helpful post and the snippet of code I’ve been puzzling over for awhile already:

 // "Sacrificial" Timer - Seems to be needed when deleting timers as that will kill first timer created...
 redblueLEDtimer = timer.setTimeout(10L, []() {
   // Do nothing
 });  // END sacrificial Function
}

You seem to be mixing-up SimpleTimer and BlynkTimer.
Although BlynkTimer is based on SimpleTimer there are some differences.

I think that what you’re seeing here is that the next timer that is created is being given the next timer ID, leaving unused timer ID slots, that are then reused later.

There is a bug in SimoleTimer. I think it’s to do with the timer IDs being zero based and this not being handled correctly in some situations.
@Gunner’s sacrificial timer takes care of this by creating a timer with ID 0 (it has to be done before anything else timer related). This timer is never used, and uses-up one of the available slots, but then allows the subsequently created timers to be enabled/disabled/created correctly.
This sacrificial timer isn’t needed if all you’re doing is running multiple times and never need to refer to them by their IDs.

I thought that I’d done some tests and convinced myself that this issue had been solved in BlynkTimer, but @Gunner and others have since disagreed with this and I’ve not bothered re-testing this since (as I haven’t had the need).

BlynkTimer allow more timer slots (16) and has some additional code which prevents server flooding.

Pete.

I’ve been about ready to give up on BlynkTimer a couple times already and just go back to the good ol millis() checking for my timers, but thanks to @PeteKnight post I went ahead and kept on digging. For starters, I’ve figure out this:

with this:

    // setTimer() constants
    const static int RUN_FOREVER = 0;
    const static int RUN_ONCE = 1;

So I’ve built this to try and figure out whats going on here. You see my end goal is to be able to put a timer within a function and the difficulty I’m having is this function could be called again (if user decides to turn the dial some more) before the timer has run, and I don’t want the timer function to start doubling and tripling up.

// This #include statement was automatically added by the Particle IDE.
#include <blynk.h>
char auth[] = "oxqsxd9OWMutgQ0QlUBn_pt1H5n-cUw4"; //
WidgetTerminal terminal(V10);
BlynkTimer timer;
bool ledon;
int LED = D7;
int button1;
int button2;

//if timers are created as setInterval in setup() they will hang onto there timerID forever
int t0;
int t1;
int t2;

//creating timers in the loop() is a whole differant story
int loopT3;
int loopT4;
int loopT5;


/////////************* **********/////////
BLYNK_WRITE(V1)
{
    button1 = param.asInt(); //this can come back higher than 100-add in if to fix -- fixed by setting Widget to limit at 100
    if (button1) {
        //timer.restartTimer(t2); //with> t2 = timer.setTimeout(10000L, loop4); in setup() after 10 sec .timer will have lost track of function loop4()
        loopT3 = timer.setTimeout(12000L, LEDToggle); //test 1
        //timer.restartTimer(t2);
            //timer.disable(t1);  //test2
    }
}
BLYNK_WRITE(V2)
{
    button2 = param.asInt(); //this can come back higher than 100-add in if to fix -- fixed by setting Widget to limit at 100
    if (button2) {
        //loopT4 = timer.setTimeout(8000L, loop4); //test 1
          timer.deleteTimer(loopT3);            // test 1A
            //timer.enable(t1); //test2
        
    }
}

void setup() {
    Time.zone(-6);
    Blynk.begin(auth);
    pinMode(LED, OUTPUT);
    digitalWrite(LED, HIGH);
    ledon = 1;
    
    t0 = timer.setInterval(10000L, WiFifunction);//this is for testing timer errors >>to be deleted<<
    t1 = timer.setInterval(5000L, sendinfo);
    t2 = timer.setTimeout(10000L, loop4); 

}

void loop() {
    Blynk.run();
    timer.run();
}

void sendinfo() {
    Blynk.virtualWrite(V0, Time.format("%r - %a %D"));
    int numTi = timer.getNumTimers();
    int t0ON = timer.isEnabled(t0);
    int t1ON = timer.isEnabled(t1);
    int t2ON = timer.isEnabled(t2);
    int t3ON = timer.isEnabled(loopT3);
    int t4ON = timer.isEnabled(loopT4);
    int t5ON = timer.isEnabled(loopT5);
    terminal.print(Time.format("%r - # Timers: "));
    terminal.println(numTi);
    terminal.print("t0 - EN?: ");
    terminal.print(t0ON);
    terminal.print(" t0timerID: ");
    terminal.println(t0);
    terminal.print("t1 - EN?: ");
    terminal.print(t1ON);
    terminal.print(" t1timerID: ");
    terminal.println(t1);
    terminal.print("t2 - EN?: ");
    terminal.print(t2ON);
    terminal.print(" t2timerID: ");
    terminal.println(t2);
    terminal.print("loopT3: ");
    terminal.print(t3ON);
    terminal.println(loopT3);
    terminal.print("loopT4: ");
    terminal.print(t4ON);
    terminal.println(loopT4);
    terminal.print("loopT5: ");
    terminal.print(t5ON);
    terminal.println(loopT5);
    terminal.flush();
}

void WiFifunction() {
    int wifi = WiFi.RSSI().getStrength();
    //Blynk.virtualWrite(V105, wifi);
    terminal.print(Time.format("%r - "));
    terminal.print(wifi);
    terminal.println("% WiFi Strength");
    terminal.flush();
}
void LEDToggle() {
    if(!ledon){
        digitalWrite(LED, HIGH);
        ledon = 1;
    }
    else if (ledon) {
        digitalWrite(LED, LOW);
        ledon = 0;
    }
    terminal.print(Time.format("%r - InToggle State: "));
    terminal.println(ledon);
    terminal.flush();
}

void loop4() {
    terminal.println(Time.format("%r - inLoop4: "));
}

With what I’m getting back in the terminal there seems to be a disconnect between the timerID’s in the code and the timerID’s within the library. So I’m thinking about doing something like this:

    int loopT4 = timer.setTimeout(5000L, []() {    //don't set this to slow or high, if Mode OFF select between delay, FWDrelay will stay on
        digitalWrite(FWDrelay, LOW); //turn on FWD
        loopT4 = -1;
    });

I’m not sure if setting the timer ID back out like that will fix my problem. Going to test this out, in the meantime I’d sure take any advise!!

I’m getting this in the terminal right after the device starts:

12:03:44 PM - # Timers: 3
t0 - EN?: 1 t0timerID: 0
t1 - EN?: 1 t1timerID: 1
t2 - EN?: 1 t2timerID: 2
loopT3: 10
loopT4: 10
loopT5: 10

and this after timer t2 has expired:

12:04:44 PM - # Timers: 2
t0 - EN?: 1 t0timerID: 0
t1 - EN?: 1 t1timerID: 1
t2 - EN?: 0 t2timerID: 2
loopT3: 10
loopT4: 10
loopT5: 10

I’m afraid I’m not seeing that disconnect. Can you elaborate?

Pete.

I can try… I really need to do some more testing here as I not sure if I’m right here yet or not. Looking through the library, all the timers types get built in the SimpleTimer::setupTimer after a timer is built you get a timerID number back IF you ask for it. Now if you just go building timers and letting them run until they expire you’ll never have any trouble. But as soon as you go messing with it before its expired you’ll get into trouble. 1 timer?? no trouble, more than one?? big trouble. because all the timer types don’t report back to the main code that a timers been deleted and change the pointer to it to something like -1.
So you setup 3 timers in setup:

    t0 = timer.setTimeout(10000L, WiFifunction);
    t1 = timer.setInterval(5000L, sendinfo);
    t2 = timer.setInterval(2000L, loop4); 

directly after boot var are:
t0 = 0
t1=1
t2=2
11 seconds later t1 and t2 are still the same, but t0 has expired and your next timer thats created gets stuck into timerID 0.
So a minute later in loop a buttons pressed and t3 is created in timer slot 0.
there you have it, now both t0 and t3 = 0. so doing timer.toggle(t3) messes with t0 as well. Doesn’t look to bad here but fire up my test code and run a bunch of timers and before long you’ll have more things happening than you know what to do with!! Here’s what I’ve got so far, but I haven’t found a solution yet that disables a timer from being created if its already running:

/////////************* **********/////////
//          Blynk Timer Rules           //
/////////************* **********/////////
/*
#1 Never start another timer in the loop if it is already running if you do, you'll create 2 timers and the first timers timerID is easily lost. (Both timers will run and you'll never be able to dig up the first timer and kill it)
    I was hoping  >>if (!timer.isEnabled(loopT5)) {loopT5 = timer.setInterval(1000L, LEDToggle);}<< would fix this but for some reason all timers (??1-16??)are enabled at setup()
#2 When using setTimer and setTimeout (the ones that will expire) in the loop be sure to set their ID back to ??-1?? once finished with them if you don't that expired timerID will come back to haunt you
*/

#include <blynk.h>
char auth[] = "oxqsxd9OWMutgQ0QlUBn_pt1H5n-cUw4"; //
WidgetTerminal terminal(V10);
BlynkTimer timer;
bool ledon;
int LED = D7;
int button1;
int button2;
int button3;

//if timers are created as setInterval in setup() they will hang onto there timerID forever
int t0;
int t1;
int t2;

//creating timers in the loop() is a whole differant story
int loopT3;
int loopT4;
int loopT5;

int t15;


/////////************* **********/////////
BLYNK_WRITE(V1)
{
    button1 = param.asInt(); //this can come back higher than 100-add in if to fix -- fixed by setting Widget to limit at 100
    if (button1) {
        //timer.restartTimer(t2); //with> t2 = timer.setTimeout(10000L, loop4); in setup() after 10 sec .timer will have lost track of function loop4()
        loopT3 = timer.setInterval(4000L, loop4); //test 1
        //loopT5 = timer.setInterval(1000L, LEDToggle);
        //timer.restartTimer(t2);
            //timer.disable(t1);  //test2
    }
}
BLYNK_WRITE(V2) {//if timer is already running, don't turn start it again
    button2 = param.asInt();
    if (button2) {
            if (!timer.isEnabled(loopT5)) {
            loopT5 = timer.setInterval(4000L, LEDToggle);
            }
    }
}
BLYNK_WRITE(V3)
{
    button3 = param.asInt(); //this can come back higher than 100-add in if to fix -- fixed by setting Widget to limit at 100
    if (button3) {
        //timer.deleteTimer()
            timer.toggle(t0);
    }
}

void setup() {
    Time.zone(-6);
    Blynk.begin(auth);
    pinMode(LED, OUTPUT);
    digitalWrite(LED, HIGH);
    ledon = 1;
    
    t0 = timer.setInterval(10000L, WiFifunction);//this is for testing timer errors >>to be deleted<<
    t1 = timer.setInterval(5000L, sendinfo);
    t2 = timer.setTimeout(2000L, loop4); 

}

void loop() {
    Blynk.run();
    timer.run();
}

void sendinfo() {
    Blynk.virtualWrite(V0, Time.format("%r - %a %D"));
    int numTi = timer.getNumTimers();
    int t0ON = timer.isEnabled(t0);
    int t1ON = timer.isEnabled(t1);
    int t2ON = timer.isEnabled(t2);
    int t3ON = timer.isEnabled(loopT3);
    int t4ON = timer.isEnabled(loopT4);
    int t5ON = timer.isEnabled(loopT5);
    
    int t15ON = timer.isEnabled(15);
    terminal.print("t15 - EN?: ");
    terminal.print(t15ON);
    terminal.print(" t15timerID: ");
    terminal.println(t15);
    
    terminal.print(Time.format("%r - # Timers: "));
    terminal.println(numTi);
    terminal.print("t0 - EN?: ");
    terminal.print(t0ON);
    terminal.print(" t0timerID: ");
    terminal.println(t0);
    terminal.print("t1 - EN?: ");
    terminal.print(t1ON);
    terminal.print(" t1timerID: ");
    terminal.println(t1);
    terminal.print("t2 - EN?: ");
    terminal.print(t2ON);
    terminal.print(" t2timerID: ");
    terminal.println(t2);
    terminal.print("loopT3: ");
    terminal.print(t3ON);
    terminal.println(loopT3);
    terminal.print("loopT4: ");
    terminal.print(t4ON);
    terminal.println(loopT4);
    terminal.print("loopT5: ");
    terminal.print(t5ON);
    terminal.println(loopT5);
    terminal.flush();
}

void WiFifunction() {
    int wifi = WiFi.RSSI().getStrength();
    //Blynk.virtualWrite(V105, wifi);
    terminal.print(Time.format("%r - "));
    terminal.print(wifi);
    terminal.println("% WiFi Strength");
    terminal.flush();
}
void LEDToggle() {
    if(!ledon){
        digitalWrite(LED, HIGH);
        ledon = 1;
    }
    else if (ledon) {
        digitalWrite(LED, LOW);
        ledon = 0;
    }
    terminal.print(Time.format("%r - InToggle State: "));
    terminal.println(ledon);
    terminal.flush();
}

void loop4() {
    terminal.println(Time.format("%r - inLoop4: "));
}

work in progress…

And what happens if you permanently tie-up slot 0 with a sacrificial timer?

Pete.

Edit: Yes that helps, but that only seems to b a bandaid. In my large project with a lot of timers I eventually wind up with a timer in ID 0 after awhile. So I’ve elected to go the route of keeping track of the timers with int myTimer =99; above setup. Doing this has solved most of my troubles with timers doubling up.

I ran accross @Gunner examples where he did that, but than he was only using 1 timer in his loop. The trouble I’ve been running into is I want to use a lot of timers in my loop and build up the code in such a way that allows the code to “come back around” on top of a timer before it’s time has ran out.
From all my test so far with this code, you can’t permanently time up slot 0 with a sacrificial timer unless that sacrificial timer is a timer.setInterval. If you don’t use timer.setInterval and your first line of the loop() is timer.Timeout(my first looptimer); you’ve just stuck a timer in timerID slot 0. Also the reason timer.delete(loopTimer); deletes the first timer is because above setup() we usually do: int loopTimer; instead of int loopTimer = 99;

Still hunting through the library to try and determine the best number for unsed timers.
Any idea??

The test code from above is now in a GitHub File
At first I though to use -1 for this as I see it here in places in the library. Instead I’ve been thinking about using a number higher than MAX_TIMERS. Wouldn’t that make the first if here true? And than does that make is so the rest of that code doesn’t run due to the return under the 1st if?

void SimpleTimer::deleteTimer(unsigned timerId) {
    if (timerId >= MAX_TIMERS) {
        return;
    }

    // nothing to delete if no timers are in use
    if (numTimers == 0) {
        return;
    }

    // don't decrease the number of timers if the
    // specified slot is already empty
    if (timer[timerId].callback != NULL) {
        memset(&timer[timerId], 0, sizeof (timer_t));
        timer[timerId].prev_millis = elapsed();

        // update number of timers
        numTimers--;
    }
}

I’ve documented my findings in this README file If someone finds any errors or problems with my approach to using a bunch (nearly all 16) of timers please let me know! As long as all you do create timers and than let them run out you shouldn’t have any problems, but as soon as you start doing timer.deleteTimer(); with more than 1 timer in your code you’ll be in for all kinds of surprises!