Code Monkey home page Code Monkey logo

arduino-timer's People

Contributors

contrem avatar philj404 avatar skrobinson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

arduino-timer's Issues

Bug: Behavior Inconsistency when canceling a timer

As the title says, there is a bug / inconsistency when canceling a timer.
Let's say we have a timer: Timer<10> MainTimer; // Timer with 10 task slots .

If we assign a Task ID to a certain task, e.g. Timer<>::Task DummyTask; and set a task for it, e.g. DummyTask = MainTimer.every(1000, DummyFunction);, the library behaves differently when canceling the timer from the inside (by returning false into the DummyFunction), compared to canceling it from the outside (by calling the MainTimer.cancel(DummyTask) method ).

When canceling the timer using the .cancel() method, DummyTask gets back ID (value) = 0, as it should.
But when canceling the timer from the inside (by returning false in DummyFunction), DummyTask retains its original ID, without reverting back to 0 as it should.

I created two demo examples here:
This one demonstrates the good behavior (using the cancel() method): https://wokwi.com/projects/355520237400512513
And the other one, demonstrates the wrong behavior (by retaining its task ID whenever using return false): https://wokwi.com/projects/355520290434348033 .

I'm not an experienced C++ developer, but I suppose the code misses a task reference when removing it, here.
(I'm wondering if this could also explain the fact that I found situations when the timer cancels itself, as per my other opened issue, #76 ).

Thanks,
A.

Support regression tests on push to confirm safe code changes

An "enhancement" request.

Sometimes when I make changes in code I accidentally break some other part of the system I didn't understand.

It can be difficult to recognize that kind of issue until after the code is released and breaks other user's builds.

It would be nice to have some regression tests which can confirm your code changes will not break other required behaviors. Regression test results should be trivial to interpret (for example return PASS or FAIL). There should be no question if your change broke something important. Even if you don't understand what the problem is, you should be able to learn there is a problem.

Timer overflow

Current implementation doesn’t account for the possibility of timer overflow, which should be a concern specially when using microsecond resolution (will happen every ~1h 12min).

Reuse timer

How can I reuse the same timer after it's over? The timer may remain in memory and the space cannot be freed.

#include <arduino-timer.h>

Timer<1, millis, void*> timer;
Timer<>::Task task;
uint32_t n = 0;

void setup() {
  SerialUSB.begin(115200);
  while (!SerialUSB){}
  
  task = timer.in(1000, test);
}

void loop() {
  timer.tick();
}

bool test(void *)
{
  SerialUSB.print("n: ");
  SerialUSB.println(n);

  n++;

  timer.cancel(task);
  task = timer.in(1000, test);
  
  return true;
}

Timer executes only after a reboot

Hi,

A brief description of what I am trying to achieve, anytime an IF condition is true a timer should activate and set a GPIO pin HIGH/LOW at end of timer. The hardware I am using is an ESP8266 with WIFI enabled. I am aware that the workflow of ESP MCU is different compared to Arduino MCU.

I read through the documentation and issues of arduino-timer, but I am not able to figure out the problem. I tried using both timer.in(delay, func) and timer.at(millis()+delay, func).

I have setup two timers, timerON - 60s delay and timerOFF - 30s delay. After a reboot, once WIFI is connected, timerON will execute properly and set GPIO pin LOW. However, when WIFI disconnects timerOFF does not execute, GPIO pin is instantaneously set HIGH. Subsequently if WIFI is connected again, nothing happens GPIO pin is kept HIGH.

I would be grateful if you could point out my mistake and looking forward for suggestions to workaround delay().

Please find below my code.

#include <ESP8266WiFi.h>
#include <arduino-timer.h>
 
const char* ssid = "*********";
const char* password = "*********";
byte bssid[] = {***, ***, ***, ***, ***, ***};

WiFiEventHandler gotIpEventHandler, disconnectedEventHandler;

const int DCON = 12; // Red LED
const int CON = 14; // Green LED
const int SSR = 4; // Solid State Relay

auto timerON = timer_create_default();
auto timerOFF = timer_create_default();

bool ON(void *)
{
  Serial.println("Switch ON");
  digitalWrite(SSR, LOW);
  return false;
}

bool OFF(void *)
{
  Serial.println("Switch OFF");
  digitalWrite(SSR, HIGH);
  return false;  
}
 
void setup() 
{
  pinMode(SSR, OUTPUT);   
  pinMode(CON, OUTPUT);   
  pinMode(DCON, OUTPUT);  

  digitalWrite(SSR, HIGH);
  
  Serial.begin(115200);
  Serial.println();

  WiFi.mode(WIFI_STA);

  gotIpEventHandler = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP& event)
  {
    Serial.print("Station connected, IP: ");
    Serial.println(WiFi.localIP());
    digitalWrite(CON, HIGH);
    digitalWrite(DCON, LOW);
  });

  disconnectedEventHandler = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected& event)
  {
    Serial.println("Station disconnected");
    WiFi.begin(ssid, password, 0, bssid);
    digitalWrite(DCON, HIGH);
    digitalWrite(CON, LOW);
  });

  Serial.printf("Connecting to %s ...\n", ssid);
  WiFi.begin(ssid, password, 0, bssid);
  
  Serial.println();
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());

  timerON.in(60000, ON);
  timerOFF.in(30000, OFF);
}

void loop() 
{
  if (WiFi.status() == WL_CONNECTED) 
  {
    timerON.tick();
  }    
  else 
  {    
    timerOFF.tick();
  }
}

"return" outside of interrupt routine

Is it possible to designate a given timer to return false or return true outside of their respective interrupt routines?

For example, create two timers:

auto timer1 = timer_create_default();
timer1.every(500, Do_Something1);

auto timer2 = timer_create_default();
timer2.every(25, Do_Something2);

void loop()
{
blah, blah,blah
if (x<10)
timer1.return false;
if (y>0.1)
timer2.return true;
blah,blah,blah
}

how do i close a timer?

i made a timer with:
auto delay_timer = timer_create_default();

and cannot terminate it using:
delay_timer.cancel();

timer is started with:
delay_timer.every(1000, bomb_armed);

seems he need the "task", but cannot find a way to define this task ...

Accessing timers from other files

Hi. This is probably mode of a programming issue than an issue with the library but I'm struggling to find the appropriate syntax.

I create some timers in the main setup function and update them in the loop as expected, but I need to access and control the timer object from a function within another file.

I've tried.. both without success.

extern Timer<> timer0 ;
extern auto timer0;

What would be the correct format to use for this as I'm not familiar using externs with templates.

Changelog?

Hello. What are the changes between the release tags?

How to create a static timer but not have it running?

If I want to create several global timers but not have all of them running, how would one do that? E.g,

static bool heartbeatTimerCallback (void *argument __attribute__ ((unused)));
static bool sixHourTimerCallback (void *argument __attribute__ ((unused)));
static bool motionTimerCallback (void *argument __attribute__ ((unused)));

static auto timer = timer_create_default ();
static auto heartbeatTimer = timer.every (HEARTBEAT_TIMEBASE, heartbeatTimerCallback);
static auto sixHourTimer = timer.in (0, sixHourTimerCallback);
static auto motionTimer = timer.in (0, motionTimerCallback);

You can't actually create a timer with an expiration of 0 milliseconds, so what I've done is set it for a ridiculous amount of time, then cancel it in the setup() function. The heartbeatTimer is one I do want running when I create it.

Disclaimer: Not a C++ guy, but been writing C for 40 years.

Weird Serial.println bug

Hello,

I have a strange bug with this code.
Minuterie.ino.txt

When line 126 ( Serial.println("RING 2"); ) is commented, "Ring 1" is sent to serial monitor at each call.
When line 126 is uncommented, "Ring 1" is displayed only at the first call, and then nothing.

No problem with the call to display_data() in both case.

I don't kown how to debug this case.

bug: timer.cancel(anId) always returns true

I tried creating an automated regression test to verify good behavior for timer.cancel() and timer ID collisions.
... and I think I found a regression for the returned value from timer.cancel().

    bool
    cancel(Task &task)
    {
        struct task * const t = static_cast<struct task * const>(task);

        if (t) {
            remove(t);
            task = static_cast<Task>(NULL);
            return true;
        }

        return false;
    }

Here's the regression: Now that a timer ID is just a pointer to the timer's slot in the array, it will never be 0... and so timer.cancel(anId) always returns true.

See #79 for the regression test and a fix.

Function return value interpretation

Hi Michael,

This is one of the better timer libraries for Arduino I've seen so far. 👍 It does without dynamic allocation and makes use of const. I hope you've tested the timer overflow. 😉

Only a personal nitpicking: I would change the meaning of the function_to_call() return value. false is negative, intuitively meaning "I don't want no more." I would have expected it to not be called again when returning a false.

Just my 2¢,
Flössie

Timer cancels itself?

Is there any circumstance or technical possibility that could lead to a timer canceling itself?

I have few .every() timers and sometimes (very rarely indeed), with no possible explanation a random timer simply stops.
I just can't figure out why.

Their function always return true; , there's plenty of RAM available, I had set plenty of timers (Timer<48> MainTimer;), I run the loop with MainTimer.tick<void>(); // to avoid ticks() calculation , I count them and I have a total of 15... but one of them, very rarely, just suddenly stops.

Can't figure out why.

Board is ATMega4808 if it helps.

Timer library affects String behaviour.

Hi,

first let me thank you for this library it makes scheduling easier.

I am using Version 2.3.1

I use the timer library to schedule sensor requests on a 328P board. (Software like it behaves as an arduino UNO).

There a stange error happens: If I set the number of timers higher than 8 - the concaternation of two strings stops working:

	lcd.print("Temperature:  "+String(SHT2X_data.Temperature));

produces no output on the display. It does not help to call:

	lcd.print("Temperature:  "+String(24));

I thinned out the whole program it might not be compilable

If you need the complete program I can send it to you.

Regards

Alexander

-------------------------- cut here --------------------------------------

#include "Arduino.h"
#include "Wire.h"
#include "TCA9548A.h"
#include "LiquidCrystal_I2C.h"
#include "SHT2x.h"
#include <arduino-timer.h>

uint8_t NewSensorData=1; // True if new Sensor data to display
auto timer = timer_create_default(); // create a timer with default settings

Timer<> default_timer; // save as above
// create a timer that holds 16 tasks, with millisecond resolution,
// and a custom handler type of 'const char *
Timer<8, millis, const char *> My_timer; // ####################### If you set here more than 8 timers ############

//########## Timed Tasks ########
bool toggle_led(void *) {
digitalWrite(Debug_LED, !digitalRead(Debug_LED)); // toggle the LED
return true; // repeat? true
}

void DisplayPage (uint8_t PageToDisplay) {
digitalWrite(Debug_LED2, !digitalRead(Debug_LED2)); // Humidity Sensor
I2CMux.openChannel(AddrDisplay);
lcd.clear();
// Line 0
lcd.print("SHT21");
// Line 1
lcd.setCursor(0,1);
lcd.print("Temperature: "+String(SHT2X_data.Temperature)); // ####################### This String command fails ############
}

void setup() {
// initialize digital pin as an output.
pinMode(Debug_LED, OUTPUT);
pinMode(Debug_LED2, OUTPUT);
lcd.init();
// turn on the backlight
lcd.backlight();
lcd.clear();

My_timer.every(500, toggle_led);
My_timer.every(3000, HumidSensorUpdate);

}

void loop() {

  My_timer.tick();
  
  	if (NewSensorData==1) {
	DisplayPage(PageToDisplay);
    NewSensorData=0;

}
--------------------------- cute here ------------------------

warning: invalid conversion from 'void (*)()'

I am using this (brilliant) library to run several tasks repeatedly using an Arduino Uno.

I have got 3 or 4 tasks running at different intervals using:

scheduler_timer.every(500, update_sensors)

and similar calls, as per the example provided in the docs.

This works. My code runs as expected and things are fine, except that there is a warning during compilation.

`Test_Rig.ino: In function 'bool send_config_data(void*)':'

'Test_Rig.ino:299:70: warning: invalid conversion from 'void ()()' to 'Timer<4, millis>::handler_t {aka bool ()(void*)}' [-fpermissive]
scheduler_timer.in(250, scheduler_timer.every(500, update_sensors));'
^
In file included from Test_Rig.ino:15:0:
\Arduino\libraries\arduino-timer\src/arduino-timer.h:85:5: note: initializing argument 2 of 'Timer<max_tasks, time_func, T>::Task Timer<max_tasks, time_func, T>::every(long unsigned int, Timer<max_tasks, time_func, T>::handler_t, T) [with unsigned int max_tasks = 4; long unsigned int (* time_func)() = millis; T = void*; Timer<max_tasks, time_func, T>::Task = unsigned int; Timer<max_tasks, time_func, T>::handler_t = bool ()(void)]'
every(unsigned long interval, handler_t h, T opaque = T())
^~~~~
Test_Rig.ino:299:50: warning: invalid conversion from 'Timer<4, millis>::Task {aka unsigned int}' to 'Timer<4, millis>::handler_t {aka bool ()(void)}' [-fpermissive]
scheduler_timer.in(250, scheduler_timer.every(500, update_sensors));
~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
In file included from Test_Rig.ino:15:0:
\Arduino\libraries\arduino-timer\src/arduino-timer.h:70:5: note: initializing argument 2 of 'Timer<max_tasks, time_func, T>::Task Timer<max_tasks, time_func, T>::in(long unsigned int, Timer<max_tasks, time_func, T>::handler_t, T) [with unsigned int max_tasks = 4; long unsigned int (* time_func)() = millis; T = void*; Timer<max_tasks, time_func, T>::Task = unsigned int; Timer<max_tasks, time_func, T>::handler_t = bool ()(void)]'
in(unsigned long delay, handler_t h, T opaque = T())
^~
Sketch uses 14514 bytes (44%) of program storage space. Maximum is 32256 bytes.
Global variables use 1067 bytes (52%) of dynamic memory, leaving 981 bytes for local variables. Maximum is 2048 bytes.
`

I dont know what to do (if anything) to fix this issue. I cannot copy too much of my code here (confidentiality and all), but I can maybe make a minimal demonstration of the problem if necessary.

Thanks

Greg

Mixing high frequency tasks with long running tasks disable the high-frequency ones. (in the return value of tick)

This is a very nice compact library! Just what I needed.

I ended up splitting my code into 2 timers, because I observed that if I register an task to run every 10ms, and one a slow one to run every 1s, but the slow one might take 200ms, the high frequency task would not get triggered.

At least not when respecting the return value of the tick() function. If I ran the tick() twice after each other, it would go fine. Since the slow one wouldn't trigger, but the fast one would, and it would now return a much smaller delay time.

rough code:

void setup() {
   Serial.begin();
   timer.every(1000, [](void*) -> bool{ delay(200); return true);
   timer.every(10, [](void*) -> bool { blinkLed(); return true});
}

void loop() {
   unsigned long sleepTime = timer.tick();
    if (sleepTime > 10) {
       Serial.print("Sleeping longer than high frequency max: ");
       Serial.println(sleepTime);
    }
   delay(sleepTime);
}

Version: 2.1.0

timer.h header collides with Arduino BLE lib file (mbed os)

Arduino IDE version 1.8.9, for Windows 10
arduino-timer library version 2.0.1.
Boards Manager support for "Arduino nRF528x Boards (Mbed OS)", version 1.1.4.

  • Select "examples/arduino-timer/blink" sketch.

  • Select "Arduino Leonardo" board and build.
    Build works as expected. This is a really nice, simple and useful library.

  • Select "Arduino Nano 33 BLE" board and build.

NOTE: Build fails with:

blink:10:14: error: 'timer_create_default' was not declared in this scope

 auto timer = timer_create_default(); // create a timer with default settings

However if I rename the arduino-timer library file "timer.h" to "renamedAduinoTimer.h"

#include <timer.h>
#include <renamedAduinoTimer.h>

the code compiles and the timer works correctly.

I think the conflict is with the file named https://github.com/arduino/ArduinoCore-nRF528x-mbedos/blob/master/cores/arduino/mbed/drivers/Timer.h.
While Windows ignores case in filenames, with these two file names so similar there WILL be confusion for developers anyway.

Could we rename <timer.h> to <Arduino_timer.h> perhaps?
Pros:

  • it's easy to tell which kind of timer we are talking about
  • in the long run it's highly likely others will use the name "timer.h", too.
  • it's unlikely MBED OS developers will rename Timer.h (fix this on their side) because one client wants to use the name too.

Cons:

  • changing the filename breaks all legacy code.
  • The Arduino folks are picky about what they attach their name to. If they don't want to pick up this timer library, maybe we could rename it to "contrem_Timer.h" or something.

Another alternative would be to just mark this library as incompatible with Nano 33 BLE boards. Which would be a shame because this is really a nice timer library.

Thanks for creating this library!

Stops executing after some executions

Hey guys,

my timer stops executing after some executions. Short breakdown of my code:

Timer<2, micros> timer;

void setup()
{
  timer.every(20000, updateLight1);
  timer.every(20500, updateLight2);
}

void loop()
{
  auto ticks = timer.tick();
  Serial.println(ticks);
}

// updateLight1 and updateLight2 reads and writes GPIOs

If I add Serial.println() to the updateLight1/updateLight2 function after the gpio doing it works as expected and executes forever. If not it stops after some executions and the serial ticks output shows 0.

ability to distinguish between an empty timer and a timer ready to run

At this time ticks() returns 0 when the task queue is empty. (Bug?)

This makes it difficult to know how long to sleep before checking the queue again. Is it time to run tick() again right now? Is it safe to go to sleep?

I think ticks() should return (unsigned long) (-1L) to hint that the queue is idle (no tasks to run). Then the caller can decide how how much sleep is appropriate when there are no tasks.

To see this behavior there is a unit test which confirms this in extras/tests/timerTest/timerTest.ino, test(timer_every).

Is It possible to get time remaining until the end?

Hi,

I appreciate your work and I would know if is possible to show the time remaining until the end.

For example, I have a countdown with a specific time using the function in(), but I want to show the time remaining in a display.

I tried use the function every() but in the looping the time is desynchronized because I have some delays.

An exemple of code to illustrate that.

`#include <arduino-timer.h>

auto t = timer_create_default();

int timerToShow = 60000;
int timerToShowArduino = 60000;

long previousMillis = 0;
const int maxTime = 1000;

bool counting = false;

bool updateTimer(void *)
{
timerToShow -= 1000;
return true;
}

void updateTimerByArduino() {
timerToShowArduino -= 1000;
}

bool finishTimer(void *) {
Serial.println("finish");
counting = false;
t.cancel();
return true;
}

void setup()
{
Serial.begin(9600);
Serial.println("start");
counting = true;
t.in(timerToShow, finishTimer);
t.every(1000, updateTimer);
}

void loop()
{
if(millis() - previousMillis >= maxTime) {
previousMillis = millis();

    t.tick();

    updateTimerByArduino();

    if(counting) {
      Serial.println(String(t.ticks()) + "|" + String(timerToShow) + "|" + String(timerToShowArduino));
    }

    delay(1500);

}
}`

In this case when the call finishTimer() in the correct time, the counters are on 20000 yet.

Can you help me?

Regards

Suggestion: Option to trigger repeating timer immediately

It would be useful to have a signature that specified whether a timer should start already expired so that it fires on the first timer tick via loop() rather than only after the specified interval. In this way an initial action can be performed without the need to explicitly trigger the callbacks.

cannot generate timer events at "minutes"

Hi, i have to handle events that will occour in minutes (not milliseconds), but i noticed that using this code:

// in the global
auto play_time_timer = timer_create_default();

// in the setup
play_time_timer.in(play_time*1000, CT_win);

// in the loop
play_time_timer.tick();

// and the called procedure
bool CT_win(void *argument){
Serial.print("timer complete!");
return false;
}

works only if play_time if less than 33 seconds (so global delay less than 33000 milliseconds).

I suppose have to deal with the INT limit, but how i can make a minute counting timer?

Confirm a code change is compatible across platforms

In the past I have run into problems where my code was not compatible with a new (unknown to me) platform.

Code changes were necessary to get it to work in new environments which I did not have access to.

It would be nice to be able to check that code changes are compatible with many Arduino variants without having to confirm by manually reconfiguring my local system for each check.

Use timerId and callbacks without argument etc.

  1. Why do you use Timer<>::Task timerTask instead of int timerId like in JS? Id more understandable and you don't need pick in mind or looking for docs about Timer<>::Task instead of auto if variable is not defined.
  2. Why you can't use just void function_to_call() without error or warnings? It is common way to return void and use no arguments in the callback. Boolean returning and arguments should be optional.
  3. Create plz setInterval and setTimeout aliases for better compatibility with common standards.

Cannot use the timer template as a class member

The Timer template is treated by the gcc compiler as belonging to the anonymous namespace. This causes a linker error.. I have made a simple test program available at (https://github.com/rogerjames99/heapstat.git).

This program has two versions of the Timer template class member one produces a compiler warning the other does not. Neither define the symbol.

I cannot understand why the template is in the anonymous namespace.

All help gratefully received! Apologies in advance if I have made a stupid error.

Roger

is there a more efficient way to cancel a timer in the future?

In my code, alarm() is called once, which activates the previously defined Task blinkAlarm, calling the function alarmSub() every blinkDelay. I want this alarmSub() function to run for a limited time, duration, and then end. currently, I set up another function, alarmEnd(), which is called using timer.in(duration, alarmEnd), but this seems unnecessarily complicated to me. My setup might be more of an edge-case though..

Here's my full code

Timer<>::Task blinkAlarm;

bool alarmSub(void *)
{
  alarmState *= -1;
  if(alarmState == 1) {
    on();
  } else {
    off();
  }
  return true;
}
bool alarmEnd(void *) {
  timer.cancel(blinkAlarm);
  return true;
}
void alarm()
{
  blinkAlarm = timer.every(blinkDelay, alarmSub);
  timer.in(duration, alarmEnd);
}

Problems with repeating tasks

In the example below, the timers do not repeat unless the default for add_task is changed to
add_task(unsigned long start, unsigned long expires,
handler_t h, T opaque, bool repeat = 1)

#include <arduino-timer.h>

// create a timer that holds 16 tasks, with millisecond resolution,
Timer<16, millis, const char *> t_timer1;
Timer<16, millis, int> t_timer2;

bool print_message(const char *m) {
Serial.print("print_message: ");
Serial.println(m);
return true; // repeat? true
}

bool pmsg(int m) {
Serial.print("pmsg: ");
Serial.println(m);
return true; // repeat? true
}

void setup() {
Serial.begin(9600);

t_timer1.in(3000, print_message, "3");
t_timer2.in(3000, pmsg, 3);
t_timer2.in(1000, pmsg, 1);
}

void loop() {
t_timer1.tick();
t_timer2.tick();
//Serial.print(".");
}

delay value bigger than 32 768 doesn't work

Hi! Thank you for a great library!

When I try to use a delay grater than 32768 with the timer.in method i get weird results.
For example: timer.in(32768, function_to_call);

Is this some known limit of the library or some of the dependent libraries?
(Sorry if this is obvious from the code, but I´m still a beginner in c++ and don´t fully understand how this library work)
If so, is there a simple way to get around this limit?

(I´m using this on a uno where I have changed the prescalers on the internal timers to get a higher frequency PWM-output, and therefore have to multiply all time values accordingly to compensate for the timer moving faster while using functions such as millis etc. Thats why I easily get values that are quite large.
With a compensation factor of 64, the maximum time I can have is 512ms.)

Thanks! /Nbasse

PWM generation on multiple pins

Hello,
I am using the timer library to generate 100 Hz PWM on four pins, which can have different duty cycles. In my current implementation, the timer misses one cycle every 500ms or so, where it just skips the timer.in delay and proceeds with the delay function immediately i.e. switching the pin high and then low immediately. Could you please share your thoughts on the probable cause for this ?

`#include <arduino-timer.h>

Timer<10> timer;
float frequency; // Frequency in Herz
double period;
double onFor[]={0,0,0,0};
long previous;

// Output pins for solenoid valves
#define Valve1 2 // Valve 1
#define Valve2 3 // Valve 2
#define Valve3 4 // Valve 3
#define Valve4 5 // Valve 4

bool switch_high(void *) {
//Serial.println(*onFor);

previous = millis();
// PWM1
if (onFor[0] > 0) {
digitalWrite(Valve1, 1); // Switch to high
}
if (onFor[0] < period) {
timer.in(onFor[0], switch_low1);
}

// PWM2
if (onFor[1] > 0) {
digitalWrite(Valve2, 1); // Switch to high
}
if (onFor[1] < period) {
timer.in(onFor[1], switch_low2);
}

// PWM3
if (onFor[2] > 0) {
digitalWrite(Valve3, 1); // Switch to high
}
if (onFor[2] < period) {
timer.in(onFor[2], switch_low3);
}

// PWM4
if (onFor[3] > 0) {
digitalWrite(Valve4, 1); // Switch to high
}
if (onFor[3] < period) {
timer.in(onFor[3], switch_low4);
}

// Serial.print(onFor[0]); Serial.print(";");
// Serial.print(onFor[1]); Serial.print(";");
// Serial.print(onFor[2]); Serial.print(";");
// Serial.print(onFor[3]); Serial.println();;
//Serial.print(period); Serial.println();;
return true;
}

bool switch_low1(void *) {
// Serial.print(millis()-previous);Serial.print(";");
digitalWrite(Valve1, 0); // Switch to low
return false;
}

bool switch_low2(void *) {
// Serial.print(millis()-previous);Serial.print(";");
digitalWrite(Valve2, 0); // Switch to low
return false;
}

bool switch_low3(void *) {
// Serial.print(millis()-previous);Serial.print(";");
digitalWrite(Valve3, 0); // Switch to low
return false;
}

bool switch_low4(void *) {
Serial.print(onFor[3]);Serial.print(";");
Serial.print(millis()-previous);Serial.println();
digitalWrite(Valve4, 0); // Switch to low
return false;
}

void setup() {
pinMode(Valve1, OUTPUT); // set Valve1 pin to OUTPUT
pinMode(Valve2, OUTPUT); // set Valve2 pin to OUTPUT
pinMode(Valve3, OUTPUT); // set Valve3 pin to OUTPUT
pinMode(Valve4, OUTPUT); // set Valve4 pin to OUTPUT

digitalWrite(Valve1, 0); // Switch to low
digitalWrite(Valve2, 0); // Switch to low
digitalWrite(Valve3, 0); // Switch to low
digitalWrite(Valve4, 0); // Switch to low

frequency = 100;
period = 1000 / frequency;
Serial.begin(9600);

onFor[0] = 0;
onFor[1] = 0;
onFor[2] = 0;
onFor[3] = 0;

timer.every(period, switch_high);
}

void loop()
{
//int sensorValue = analogRead(A5); // Range is 0 to 1023
//float dutyCycle = sensorValue*100.0 / 1023.0;
float dutyCycle[] = { 98, 0, 0, 98 };
onFor[0] = period * (dutyCycle[0] / 100.0);
onFor[1] = period * (dutyCycle[1] / 100.0);
onFor[2] = period * (dutyCycle[2] / 100.0);
onFor[3] = period * (dutyCycle[3] / 100.0);
timer.tick(); // tick the timer
}`

image

Every don't work if return non true

OMG! I had a couple of hours troubles with this code until understand that every works one time if return nothing. Really wired logic.

arduinoTimer.every(500, readSerial);

bool readSerial(void *argument) {
  // Do something
}

Improvement: Replace func handler by std::function

Hello,

With the current definition of the func handler:

typedef bool (*handler_t)(T opaque); /* task handler func signature */

It's impossible to use std::bind when you want the timer to run a callback that is a member of the class.

by changing the definition to use std::function like

typedef std::function<bool(T opaque)> handler_t;

It should let the user use any type of supported callback and shouldn't break the current working of the library.

Multiple Arguments to callback function

I was trying to handover multiple arguments to the callback function, but it did not work. However as descibed in the readme, one argument is working fine.

Therefore is it maybe possible to cast
timer.every(10, callbackFunc_timer, color, screenBufferOld, screenBufferNew);

to

bool callbackFunc_timer(void *, uint16_t screenBufferOld[], uint16_t screenBufferNew[], uint8_t color) {
// do something
}

canceling a task while it is running can delete an added task

I would like to use timer tasks for my finite state machines, but I have been having trouble with them.
Basically I want to ensure that at most ONE state machine task is in the timer at any stage. This helps ensure that the number of tasks does not increase uncontrollably if I queue up a new task (for example for an emergency stop).
So, what I was planning on doing is cancel the pending task, then add the new task. This works ok EXCEPT when the two timer.cancel()...timer.in() calls are made from within the running task.

Here is my example (built for Arduino Nano -- processor should not matter):

#include <arduino-timer.h>

auto timer = timer_create_default();

static const int WAIT_POLO = 2022;
static const int WAIT_MARCO = 4000;

Timer<>::Task activeMarcoPoloTask = 0;  // NULL

///////////////////////////////////////////////////////////////////////////////
// reschedule a new task in place of the old one
// at most ONE task is scheduled, regardless of where it happens
//
void rescheduleTask(int nextWait, Timer<>::handler_t nextAction) {

#define SHOW_CANCEL_BUG
#ifdef SHOW_CANCEL_BUG
  timer.cancel(activeMarcoPoloTask);
#endif
  activeMarcoPoloTask = timer.in(nextWait, nextAction);
}

///////////////////////////////////////////////////////////////////////////////
bool doMarco(void *) {
  Serial.print("Marco ");
  rescheduleTask(WAIT_POLO, doPolo);
  return false;
}

///////////////////////////////////////////////////////////////////////////////
bool doPolo(void *) {
  Serial.println("Polo");
  rescheduleTask(WAIT_MARCO, doMarco);
  return false;
}

///////////////////////////////////////////////////////////////////////////////
void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println(F("Running " __FILE__ ",\nBuilt " __DATE__));

  activeMarcoPoloTask = timer.in(1000, doMarco);
}

///////////////////////////////////////////////////////////////////////////////
void loop() {
  timer.tick();
}

The phrase "Marco Polo" does not repeat when SHOW_CANCEL_BUG is defined -- output freezes as there are no tasks in the timer.

I THINK the problem is that canceling the active task (while it is running) frees up its slot in the timer but the timer does not realize the free slot can now refer to a new task. When a new task uses that slot, the timer cleanup code from the previous task deletes it,

Here's the code for tick():

    template <typename R> void
    tick()
    {
        timer_foreach_task(task) {
            if (task->handler) {
                const unsigned long t = time_func();
                const unsigned long duration = t - task->start;

                if (duration >= task->expires) {
                    task->repeat = task->handler(task->opaque) && task->repeat; // ! THIS MAY BE REPLACED WITH A NEW TASK!

                    if (task->repeat) task->start = t;
                    else remove(task);
                }
            }
        }
    }

So... is this a bug?

Should I submit a pull request with a bugfix and new regression test to confirm it works?

amount of timers

First I would like to thank you for this really easy to use and very versatile timer.

I have got a question to the amount of timers I can program. Have I got it right and the functions are limited to 16 timers? If so, is there any chance to extend this in any way, let's say 24 or 32?
As the project is growing, so is the need to control different things with the timer functions and slowly I've used all 16 ;-)

Thanks

Better to have multiple timers or multiple tasks?

Dear Michael,

Is it better to have multiple timers with one task apiece, or one timer with multiple tasks? I ask because I can't reliably set multiple tasks and cancel them individually. The code below fires phasers and photon torpedoes on a model starship. I want to "randomly" alternate firing phasers and torpedoes with each press of a button, for a total of four weapons fire: one phaser, three torpedoes (port and starboard); one torpedo, one phaser, two torpedoes; etc.

I'm using the LedFlasher library from Nick Gammon to fire the phasers, but I use your timer to tell it when to stop. (I've also tried disabling LedFlasher.) I also use your timer to repetitively increase the intensity of the torpedo effect, and again to ramp down the effect. I've tried it with one timer and assigning a task without obtaining an identifier, and just doing a blanket cancel when I want to end the task. And I've also created multiple tasks and using systemTimer.cancel(phasTask) and systemTimer.cancel(torpTask).

Whatever method I try, it only seems to work for two calls to the randomFire function. After that, the fireTorpedo function or firePhaser functions act unreliably. The randomFire function, for instance, just keeps firing torpedoes endlessly.

I've also tried instantiating the timer with Timer<2> systemTimer, but that causes a different problem — the phaser fires endlessly.

I'd appreciate any suggestions. Tomorrow I'll try creating multiple timers with one task apiece. Ultimately I have four tasks for this sketch, to control spotlights, thrusters, phasers and torpedoes. But for now, I'd just be happy to handle two tasks.

Thanks,

Jennifer Petkus

`#include <arduino-timer.h>
#include <LedFlasher.h>

const int CS_PIN = 10;
// Audio out = pin 9, declared in setup

const int WEAP_BTN = 8;
const int TORP1_PIN = 6;
const int TORP2_PIN = 5;
const int PHAS_PIN = 4;

auto systemTimer = timer_create_default();

/* Next value is a very loose approximation of the time
for a torpedo launch burst. Torpedo brightness increases
until it reaches threshold value, goes to 255, and then fades */
const int TORP_DUR = 1500/255;
const int TORP_THRESH = 64; // when torp launcher switches to full bright
int torpInt = 0; // current brightness of torp launcher
int torpCnt = 0; // port or starboard launcher
int fireCnt = 0; // how many times weapons fired
int fireLmt = 4; // max number weapons fire
bool weapFlag = 0; // prevents overlapping fire
bool warpFlag = 0; // prevents overlapping impulse/warp triggers
bool phasFlag = 0;
const int PHAS_LOW = 250;
const int PHAS_HIGH = 1500;

LedFlasher phasers(PHAS_PIN, 50, 100); // phaser flicker effect

void setup() {
pinMode(TORP1_PIN, OUTPUT);
pinMode(TORP2_PIN, OUTPUT);
pinMode(WEAP_BTN, INPUT);

phasers.begin();
}

void loop() {
systemTimer.tick();
if (digitalRead(WEAP_BTN) == HIGH && !weapFlag) { // prevents overlapping fire and firing in spacedock
weapFlag = 1;
randomFire();
}
if (phasFlag) {
digitalWrite(PHAS_PIN, HIGH);
phasers.update();
}
}

void randomFire() {
if (fireCnt < fireLmt) {
if (random(0,2) == 1) {
phasFlag = 1;
firePhaser("fire");
} else {
systemTimer.every(TORP_DUR, fireTorpedo, "ramp");
}
fireCnt++;
} else {
fireCnt = 0;
weapFlag = 0;
}
}

void fireTorpedo(const char *which) {
if (which == "ramp") { // slowly lights LED until …
if (torpInt > TORP_THRESH) { // … flash after threshold
systemTimer.cancel();
torpInt = 255;
systemTimer.every(TORP_DUR/2, fireTorpedo, "fade");
} else {
torpInt++;
}
} else if (which == "fade") {
if (torpInt <= 0) {
if (torpCnt == 0) {
systemTimer.cancel();
systemTimer.every(TORP_DUR, fireTorpedo, "ramp");
torpCnt++;
torpInt = 0;
} else if (torpCnt == 1) {
torpCnt = 0;
randomFire();
}
} else {
torpInt--;
}
}
int cur_pin = 0;
if (torpCnt == 0) { // switches between port / stbd launcher
cur_pin = TORP1_PIN;
} else if (torpCnt == 1) {
cur_pin = TORP2_PIN;
}
analogWrite(cur_pin, torpInt);
}

void firePhaser(const char *which) {
if (which == "fire") {
systemTimer.in(500, firePhaser, "stop");
} else if (which == "stop") {
systemTimer.cancel();
digitalWrite(PHAS_PIN, LOW);
phasFlag = 0;
randomFire();
}
}`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.