Software Timers period value question?

Hi,

I am trying to setup a software timer with the period as an unsigned long variable as below:

Timer sTimer(testTime, loadSTimer, true);

I want to confirm if this is doable or not since its not working for me. But if I used the below code, it would work:

 Timer sTimer(3000, loadSTimer, true);

I look at the document examples, the period value is define with milliseconds value not with define variable. Could variable be used in the period field with software timer? Other ways around this? Thanks

It works for me. Where are you declaring your variable testTime? It needs to be before the creation of your timer. However, I'm not sure you want to do this anyway, if you're using the variable to change the timer's period after it's already created. If you need to change the period of the timer, you should use the changePeriod() function, and you can pass a variable to it.

@Ric, look like I am declaring the testTime variable after the creation of the timer. Below is the flow for the timer. ChangePeriod() seem like a better solution then changing the period with a variable. I will change my test case and see how it goes. Thanks for the help

unsigned long testTime;
void setup()
{
...
}

Timer sTimer(testTime, loadSTimer, true);

void loadSTimer()
{
  Serial.println ("loadSTimer" );

}

void loop()
{
...
schedTimeEvent ();
...
}

void schedTimeEvent ()
{
testTime = 3000;
sTimer.start();
}

The periode of the timer is set when this line is executed

Timer sTimer(testTime, loadSTimer, true);

and not when the value of testTime changes.
Moreover, since constructors are not executed in the order they are placed in the source but in the order the linker decides to have them executed, you won’t even have setup() executed at the time your sTimer is constructed.

Hence, you should initialise testTime for the construction and when you want to change the periode call sTimer.changePeriod() as @Ric already said.

BTW, calling schedTimeEvent() in loop() doesn’t seem to be the way to go.
Software Timers are free running, so once started, you won’t have to call that function anymore.

@ScruffR, at first, I thought the timer will only start and get the period when sTimer.start(); is called. Now, I know its not the case as you mention above.

By using sTimer.changePeriod() as @Ric suggested, I was able to get the result that I wanted.

I am using a one shot timer in my case since I am trying to launch a schedule event once the timer is reached. I have 8 entries in the schedule that why I place it in the loop and need to be able to change the timer period base on the schedule time. I just need the timer to execute once per entries and different time period. Please correct me if I am wrong, from the document if I add true to the third field of the timer like Timer sTimer(testTime, loadSTimer, true); it become one shot and it only run once.

That is true. You need to be careful how you start or change the period of your timer. If you try to call start() in loop, you may just keep resetting the timer, and it will never fire. For instance, with this code below:

Timer t(2000, timerHandler, true);

void setup() {
  	Serial.begin(9600);
  	delay(3000);
	Serial.println("Starting");
}

void loop() {
	delay(1000);
	t.start();
}

void timerHandler() {
	Serial.print("Fired ");
}

This timer will never fire, because it has a 2 second period, but it is being reset (with the start() function) in loop every second. Calling changePeriod() also starts a stopped timer, so there's no need to call start() if you're using that function. It would be helpful if you could give us more information about what you're trying to accomplish. You say you have 8 entries, does that mean you want 8 timers? Once they all fire, is that it, to do you then change the periods, and fire them again? Are we talking about second type timeframes, minutes, hours?

@Ric, I want to use one timer with the ability to modify the period in milliseconds. The 8 entries is 15 min apart. I am taking the schedule time minus the current time as the period to launch the timer. Example: schedule time is 1:15pm and current time is 1:00pm. The difference is 15 minute, so the period for the timer is 15*60000 =900000 milliseconds. After triggering the timer at 900000 milliseconds, move on to next entries. etc… until 8 entries is done.

I would like to limit one timer instead of 8 timer flying around. This might cause confusion or problems. I am using one shot timer to have the timer fire once for each entry only. All the entries does the same thing, just at different time. I think a reusable timer will be a good fit in this case.

Will the changePeriod() function start a timer with the new period even if I never use timer.start() anywhere in my code? Right now, I am using changePeriod() then timer.start() after. It seem like its giving me the result that I want.

Yes, changePeriod() will start the timer without ever calling start(). I just tested that to be sure.

If each of your periodes is 15min then why not have one multi-shot timer that just stops after 8 events?

void timerCallback() {
  static int count = 0;
  // do whatever
  if (++count >= 8) {
    t.stop();
    count = 0; // for next 8-part run
  }
}

@ScruffR, I should said the 8 entries is over 24 hour, 15min apart so its could be 1:15am , 3:30pm, 5:00pm… etc. I was able to get it working by using the changePeriod() with a variable Period in a loop. Thanks all for the help.

Hi @ScruffR & @sheng

I tried to implement software timer but not working.

In my test code, I send the timer value like ‘5000’ through Particle.function() to turn the led D7 HIGH after 5 seconds but it doesn’t work.
If I direct assign the value=5000; then it works. Why?

int value;
int status(String command);

void setup()
{   
    pinMode(D7, OUTPUT);
    
 //   Particle.variable("value", value); to check value of value variable in console
    Particle.function("status", status);
    
}

int status(String command) //enter value like 2000 through console to set 2 sec timer
{   
    int value = command.toInt(); //convert string to int for timer
    Timer t(value, action);
    return 0;
}

    void action()
{
    digitalWrite(D7, HIGH);
}


void loop() {

}

Your timer object gets destroyed as soon you leave the function where it is created.
You need a global timer if you want to do it that way.

Oh!

I did some changes again and tested but not working even now.
Please edit to the code to make it workable.

int value;
Timer t(value, action);
int status(String command);

void setup()
{   
    pinMode(D7, OUTPUT);
    
 //   Particle.variable("value", value); to check value of value variable in console
    Particle.function("status", status);
    
}

int status(String command) //enter value like 2000 through console to set 2 sec timer
{   
    value = command.toInt(); //convert string to int for timer
    t.start();
    return 0;
}

    void action()
{
    digitalWrite(D7, HIGH);
}

If you want to change the previously set periode of the SoftwareTimer you should use the respective function :wink:
https://docs.particle.io/reference/device-os/firmware/argon/#changeperiod-

Uhm, no - we'd rather have people use the docs.

Explanation why this isn't working either:
When you create (instantiate) the global timer t, your global value variable is initialised to 0 and that value is taken for the timer periode, which in turn means that the timer is not supposed to ever run.
Changing the value of the variable that was used when you create the timer has no relevance for the timer anymore since value was only passed by value and not by reference.

I tried a lot and read documentation several times and change the code accordingly.
I spent whole day for this really but not working even now.

Is it correct way to convert “String command” to int by this line of code? And it should define as int or what?

int value = command.toInt();

I changed the code again

int value;
Timer t(value, action);
int status(String command);

void setup()
{   
    pinMode(D7, OUTPUT);
    
    Particle.variable("value", value); 
    Particle.function("status", status);
    
}

int status(String command) //enter value like 2000 through console to set 2 sec timer
{   
    value = command.toInt(); //convert string to int for timer
    t.changePeriod(value);
    return 0;
}

    void action()
{
    digitalWrite(D7, HIGH);
}

You almost got it, but while you now have the preparation of your timer correct, you never start it.

So try this.

int status(String command) //enter value like 2000 through console to set 2 sec timer
{   
    value = command.toInt(); //convert string to int for timer
    t.changePeriod(value);
    t.start();
    return value;  // <-- returning a useful value helps more that unconditionally return 0
}

:smile:
Yup

I changed what you suggest but not worked.

int value;
Timer t(value, action);
int status(String command);

void setup()
{   
    pinMode(D7, OUTPUT);
   // t.start();
    Particle.variable("value", value); 
    Particle.function("status", status);
    
}

int status(String command) //enter value like 2000 through console to set 2 sec timer
{   
    value = command.toInt(); //convert string to int for timer
    t.changePeriod(value);
    t.start();  //As you suggested and also tried with declare t.start() in setup() too
    return value;
}

    void action()
{
    digitalWrite(D7, HIGH);
}

I probably won’t get around trying it myself :wink:
Meanwhile you could try to initialise value like this before your timer declaration.

int value = 5000;

BTW, what device and device OS version are you targeting?

Now, it is working as per time I passed through console when I declared int value = 5000;
Why it is so?

I am executing on Photon and v0.7.0

Thank you so much to assist me.

Probably because the timer won't even be fully instantiated when the inital period is set to 0.
On the one hand that's not too surprising as a timer without periode makes no sense and before the introduction of setPeriod() there was no point in having such a timer at all.
But when setPeriod() was introduced later on that previous reasoning would have become invalid. However, as it seems, the implementation of the constructor was not adapted for that.

One other note. Since you only want to use the timer to switch the LED on once, you should rather use a one-shot timer

Timer t(value, action, true);
1 Like