Arduino timer scheduler library

Here is the first cut of the timerAlarm library. I will post it here for people to play with and comments while I finish testing and writing up.

The library is the suggested subset of scheduling functionality mentioned in this thread http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1217833080

The library provides for co-operative scheduling of time based tasks with periods from one second to many years. Alarms can be created that repeat at a given interval or that trigger only once. Alarms once created can be enabled, disabled or rescheduled as required.

The number of Alarms concurrent alarms is fixed at library compile time, the default is 6 alarms. The number can be changed by altering a define in the header file, each alarm takes 12 bytes of RAM.

Alarms are created by calling one of the following two methods:
AlarmId1 = dtAlarms.createRepeating( PeriodInSeconds ); // fires periodically
AlarmId2 = dtAlarms.createOneshot( PeriodInSeconds ); // fires once

There are convenience macros to set periods of minutes, hours, days and weeks

The scheduling algorithm is a simple round robin, priority is given to alarms based on the order the create methods are called, first created alarms have the highest priority. Any timers that mature while a callback is executing while wait for the callback to finish.

The library requires the timing mechanism from the DateTime library which is available here:
http://www.arduino.cc/playground/Code/DateTime

I intend to post the finished code for the Alarm library in the playground along with a write-up but welcome a discussion on the functionality here.

example sketch

#include <DateTime.h>
#include <TimerAlarms.h>

AlarmID_t AlarmA, AlarmB, AlarmC, AlarmD;

void onAlarm(AlarmID_t Sender){
// alarm callback function
 Serial.print("cume time ");  
 Serial.print(DateTime.now());  // print elapsed seconds since sketch started runnin 
 
 Serial.print(": ");
  if( Sender == AlarmA) {
      Serial.println("15 seconds Alarm");         
  }
  else  if( Sender == AlarmB){
      Serial.println("90 second Alarm");    
  }
  else  if( Sender == AlarmC){
      Serial.print("One Shot alarm, elapsed period was ");    
      time_t alarmValue  = dtAlarms.getValue(Sender);
      Serial.println(alarmValue,DEC); 
  }
  else  if( Sender == AlarmD){
      Serial.print("re-trigged One Shot alarm, elapsed period was ");    
      time_t alarmValue  = dtAlarms.getValue(Sender);
      Serial.println(alarmValue,DEC); 
      dtAlarms.setValue(Sender, alarmValue + 2); //re-enable with a new value two seconds longer
    }
}

void setup(){
   pinMode(13,OUTPUT);
   Serial.begin(19200);

   AlarmA = dtAlarms.createRepeating( 15 );  // alarm every 15 seconds
   AlarmB = dtAlarms.createRepeating( AlarmHMS(0,1,30) );  // alarm every 1 minute 30 seconds
   AlarmC = dtAlarms.createOneshot( 10 );  // one shot alarm in 10 seconds
   AlarmD = dtAlarms.createOneshot( 12 );  // one shot alarm that will be manually retriggered

   Serial.println("started");
 }

void loop(){
  dtAlarms.waitUntilThisSecond(0); //  this code blocks waiting for start of the next minute, background alarms are still serviced 
  Serial.println("turning LED on");
  digitalWrite(13, HIGH);
  dtAlarms.delay(2000);  // note we call the alarm delay to service the background alarms 
  Serial.println("turning LED off");
  digitalWrite(13,LOW);  

}

Library Header file, save as

TimerAlarms.h

/*
  TimerAlarms.h - Arduino Timer Alarm library

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

*/

#ifndef TimerAlarms_h
#define TimerAlarms_h

#include <inttypes.h>
#include <wiring.h> // for boolean

#include "DateTime.h" 

typedef enum {
     dtMillisecond, dtSecond, dtMinute, dtHour, dtDay //,clkMonth,clkYear
 } dtUnits_t;

typedef struct  {
      uint8_t isEnabled              :1 ; // alarm will only fire if true
      uint8_t isOneShot              :1 ; // alarm will fire once only if true
      uint8_t isAlarm                :1 ; // alarm based on absolute time, not used this version
   }
     AlarmMode_t   ;

typedef uint8_t AlarmID_t;
#define INVALID_ALARM_ID 255
#define NBR_ALARMS 6 // this sets the number of alarms, each alarm uses 12 bytes of RAM

class AlarmClass;  // forward reference
typedef void (*OnTick_t)(AlarmID_t);  // alarm callback function typedef 
extern void onAlarm(AlarmID_t);  // User supplied callback for all alarms

// class defining an alarm instance, only used by dtAlarmsClass
class AlarmClass
{      
private:
      friend class dtAlarmsClass;
      void updateNextTrigger();
      time_t value;
      time_t nextTrigger;
      AlarmID_t ID;           // unique instance id 
      AlarmMode_t Mode;
      AlarmClass();
public:
};

// class containing the collection of alarms
class dtAlarmsClass
{
private:
   AlarmClass Alarm[NBR_ALARMS];
   boolean isServicing;
   AlarmID_t nextID;
   AlarmID_t create( time_t value, boolean isOneShot, boolean isEnabled );
   void serviceAlarms();

public:
      dtAlarmsClass();
      void delay(unsigned long ms);
      uint8_t getDigitsNow( dtUnits_t Units);  // returns the current digit value for the given time unit
      void waitForDigits( uint8_t Digits, dtUnits_t Units);
      void waitForRollover(dtUnits_t Units);
    AlarmID_t createOneshot( time_t value, boolean isEnabled = true);
    AlarmID_t createRepeating( time_t value, boolean isEnabled = true);
      void setValue(AlarmID_t ID, time_t value);
      time_t getValue(AlarmID_t ID);
      void enable(AlarmID_t ID);
      void disable(AlarmID_t ID);

};

extern dtAlarmsClass dtAlarms;  // make an instance for the user

/*==============================================================================
 * MACROS
 *============================================================================*/

/* public */
#define waitUntilThisSecond(_val_) waitForDigits( _val_, dtSecond)
#define waitUntilThisMinute(_val_) waitForDigits( _val_, dtMinute)
#define waitUntilThisHour(_val_)   waitForDigits( _val_, dtHour)
#define waitUntilThisDay(_val_)    waitForDigits( _val_, dtDay)
#define waitMinuteRollover() waitForRollover(dtSecond)
#define waitHourRollover()   waitForRollover(dtMinute)
#define waitDayRollover()    waitForRollover(dtHour)

#define AlarmHMS(_hr_, _min_, _sec_) (_hr_ * SECS_PER_HOUR + _min_ * SECS_PER_MIN + _sec_)

#endif

Library source file, save as

TimerAlarms.cpp

/*
  TimerAlarms.cpp - Arduino Timer Alarm library
  Copyright (c) Michael Margolis.  All right reserved.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
*/

extern "C" {
#include <string.h> // for memset
}

#include "DateTime.h"
#include "TimerAlarms.h"

//**************************************************************
//* Alarm Class Constructor

AlarmClass::AlarmClass(){
  Mode.isAlarm =  Mode.isEnabled = Mode.isOneShot = 0;
  value = nextTrigger = 0;  
}

//**************************************************************
//* Private Methods

void AlarmClass::updateNextTrigger(){
  if( (value != 0) && (Mode.isEnabled != 0) ){
    time_t time = DateTime.now();
    nextTrigger = time + value;  // add the value to previous time (this ensures delay always at least Value seconds)    
  }
  else {
    Mode.isEnabled = 0;  // Disable if the value is 0
  }
}

//**************************************************************
//* Date Time Alarms Public Methods

dtAlarmsClass::dtAlarmsClass(){
  nextID = 0;
  isServicing = false;
}

AlarmID_t dtAlarmsClass::createOneshot( time_t value, boolean isEnabled){
  return create( value, true, isEnabled );
}

AlarmID_t dtAlarmsClass::createRepeating( time_t value, boolean isEnabled){
   return create( value, false, isEnabled );
}

void dtAlarmsClass::enable(AlarmID_t ID){
  if(ID < NBR_ALARMS){
    Alarm[ID].Mode.isEnabled = (Alarm[ID].value != 0) ;  // only enable if value is non zero
    Alarm[ID].updateNextTrigger(); // trigger is updated whenever  this is called, even if already enabled       
  }
}

void dtAlarmsClass::disable(AlarmID_t ID)
{
  if(ID < NBR_ALARMS)
    Alarm[ID].Mode.isEnabled = false;
}

void dtAlarmsClass::setValue(AlarmID_t ID, time_t value){
  if(ID < NBR_ALARMS){
    Alarm[ID].value = value;
    enable(ID);
  }
}

time_t dtAlarmsClass::getValue(AlarmID_t ID){
  if(ID < NBR_ALARMS)
    return Alarm[ID].value;
  else 
    return 0;
}

// following functions are not Alarm ID specific.
void dtAlarmsClass::delay(unsigned long ms)
{
  unsigned long endtime = millis() + ms;
  boolean Overflow = endtime < millis();
  if( Overflow){
    while( millis() > endtime)
      serviceAlarms();
  }
  while( millis() < endtime)
    serviceAlarms();
}

void dtAlarmsClass::waitForDigits( uint8_t Digits, dtUnits_t Units){
  while(Digits != getDigitsNow(Units) )  {
    serviceAlarms();
  }
}

void dtAlarmsClass::waitForRollover( dtUnits_t Units){
  while(getDigitsNow(Units) == 0  ) // if its just rolled over than wait for another rollover                                  
    serviceAlarms();
  waitForDigits(0, Units);
}

uint8_t dtAlarmsClass::getDigitsNow( dtUnits_t Units){
  time_t time = DateTime.now();
  if(Units == dtSecond) return numberOfSeconds(time);
  if(Units == dtMinute) return numberOfMinutes(time); 
  if(Units == dtHour) return numberOfHours(time);
  if(Units == dtDay) return dayOfWeek(time);
  return 255;  // This should never happen 
}

//***********************************************************
//* Private Methods

void dtAlarmsClass::serviceAlarms(){
  if(! isServicing)
  {
    isServicing = true;
    for(uint8_t i = 0; i < NBR_ALARMS; i++)
    {
      if( Alarm[i].Mode.isEnabled && (DateTime.now() >= Alarm[i].nextTrigger)  )
      {
              if( Alarm[i].Mode.isOneShot == 1 ){
                 Alarm[i].Mode.isEnabled = 0;  // this prevents the oneshot from re-triggering unless its explicitly re-armed
              } 
        Alarm[i].updateNextTrigger();
        if( onAlarm != NULL) {                        
                onAlarm(i);
        }
      }
    }
    isServicing = false;
  }
}

AlarmID_t dtAlarmsClass::create( time_t value, boolean isOneShot, boolean isEnabled ){  // returns true if has been registerd ok
  AlarmID_t id = INVALID_ALARM_ID;
  if( nextID < NBR_ALARMS){
      id = nextID;
    Alarm[id].Mode.isOneShot = isOneShot;
      Alarm[id].value = value;
      isEnabled ?  enable(id) : disable(id);   
    nextID++;   
  }
  return id;
}

// make one instance for the user to use
dtAlarmsClass dtAlarms = dtAlarmsClass() ;

Hmm, why does the Alarm class store its own ID? I do not see it being used anywhere, perhaps it should be eliminated to save space? It seems like this class is obfuscated from the users so they would not need to look it up either, right?


A Naming question, why "alarms"? Just curious, is this standard terminology for this type of thing? I think of Alarms as something that gets triggered when something bad happens. Would "Events" or 'DtEvents" not be better? :slight_smile:


It appears that you have gone through great pains to save space, very nice. :slight_smile: I assume that is one reason why you use indexes instead of a more "traditional" object oriented design? I.E. you pass an index to your onAlarm() function instead of a pointer to the Alarm object, this saves a byte. As a user, I would much prefer to have a more object oriented approach, so that instead of doing something like:

dtAlarms.getValue(Sender);

I could do something like:

alarm->getValue();

But this just leads me to the next "user interface" request, which would be to simply have a separate onAlarm() function for each Alarm. This now trades 1 byte of temporary (stack) RAM, your index, for 2 bytes of permanent RAM per alarm for a function pointer (6*2 =12 bytes). I can see why you probably did not take this approach. I wonder however if there aren't some clever tricks with constants that could be played to still get the separate function per Alarm while not using an extra 12 bytes of RAM. Is this something that would be desirable?

What about allowing the user to create a lookup table of function pointers that could be declared constant? Would these perhaps get optimized out of RAM by the compiler? If so, would this be cleaner from a user interface standpoint?

Martin,

Great feedback, here is a quick response with a more detailed post to follow tomorrow when I have some more time.

This alarm code is a very much simplified version of a more sophisticated library I developed some time ago, and some and the names (and it seems a redundant variable or two ) reflect the more complex version.

The original library allowed the user to instantiate alarm (or event) instances in the sketch as required, the ID was used to register the creation of an instance with the alarm collection class. This allowed access to the public methods and properties of the individual class but did require the use of the indirection syntax which did not seem a common notation in popular arduino code.

The current functionality has been pared down to the minimum in order to keep it the usage as simple as possible.

In the earlier library I actually used the terms 'timers' for event triggers that are based on a expiration of a given time period (this is the functionality supported in the posted version) and 'alarms' for events triggered at a specific time of day (like an alarm clock). I am certainly open to suggestions on the most intuitive term for the duration based triggers in the current code.

The library did allow each alarm to have its own callback or share callbacks, you can see examples of both in the code I posted in another thread a few days ago. But [u]mellis's comment[/u] reinforced my view that simple is best so I removed that functionality.

Perhaps I should post both versions.

In the mean time I would certainly want to hear suggestions for naming of any or all of the variables and methods in the code and suggestions an finding the right balance between simplicity and functionality in the version above.

I could imagine an even simpler API. Something along the lines of:

void setup()
{
Alarm.once(500, foo); // call foo() in half a second
Alarm.repeat(1000, blink); // call blink() every second
}

void loop()
{
Alarm.delay(1000);
}

void foo() {}
void blink() {
}

That seems like it would cover most needs. Or am I missing some common use cases?

  1. Well, how do you change alarms? For repeating alarms it is likely that it will need to be turned on/off at various times or the rate may need to be changed.

  2. Do you envision that users might want to call each of those functions more than once? If so, then either some default amount of memory needs to be assigned (evil), or malloc() needs to be used, or the user needs to pass in some Alarm object that they have allocated themselves. Do you agree? Passing in an Alarm object solves issue number 1 also.

how do you change alarms?

Well, since I'm not writing the code:

Alarm.disable(foo);
Alarm.enable(foo);
Alarm.delete(foo);

Of course, the first two could be accomplished by just using the delete and once/repeat calls, but adding enable and disable seems neater to me.

As for the table (we're setting up a "timer" signal vector, analogous to the hardware's interrupt vector), I don't have a glib response for that one, but it shouldn't be too difficult. Worst case, use some default number and allow the user to override it at compile time.

-j

Alarm.disable(foo);
Alarm.enable(foo);
Alarm.delete(foo);

Ahh, but what happens when the user tries this:

Alarm.once(500, foo); // call foo() in half a second
Alarm.repeat(1000, foo); // call the same foo() every second

If you let me register a function, is it not reasonable to assume that I might want to register that function with more than one alarm? Now which one am I enabling/disabling?

As for the table (we're setting up a "timer" signal vector, analogous to the hardware's interrupt vector), I don't have a glib response for that one, but it shouldn't be too difficult. Worst case, use some default number and allow the user to override it at compile time.

Unfortunately, you have to do a lot more than setup a table which would only be two bytes per Alarm. You also have to store information about the date/time of when the alarm should be triggered. If you look at the implementation provided at the beginning of this thread, it works out to about ~12 bytes per Alarm. It is probably not reasonable with only 1024 bytes of RAM to preallocate enough space for several of these in advance. Making this a compile time option eliminates it from ever being becoming a core library right? The remaining alternatives I believe are:

  1. making the user allocate them (more complicated for users, but more efficient)
    or
  2. using malloc (easier for users, but uses more RAM).

If you let me register a function, is it not reasonable to assume that I might want to register that function with more than one alarm?

Yes.

But, if the library doesn't allow for it, I could easily write code that looks like

setup()
{
   Alarm.once(500,foo);
   Alarm.repeat(1000, bar);
}

void bar()
{
   foo();
}
...

Now which one am I enabling/disabling?

If you allow the function to be used with multiple alarms, this introduces the need for an alarm identifier beyond using the function's address.

Unfortunately, you have to do a lot more than setup a table which would only be two bytes per Alarm. You also have to store information about the date/time of when the alarm should be triggered.

That's just a bigger table.

Making this a compile time option eliminates it from ever being becoming a core library right?

Why?

#define MAX_ALARMS 17
#include <Alarm.h>
...

I'm not necessarily against making the user allocate them, but the target Arduino audience does not consist exclusively of your typical microcontroller hacker, and that is a huge part of the success of the project, IMO.

-j

One thing that does worry me is the apparent reentrant nature of the monolithic signal handler (onAlarm())in the first post in this thread. If it is reentrant, you've got to be careful (e.g. static variables) in the way you write the code. I'm not sure that reentrant code is appropriate for the Arduino, just be cause of the complexity of the concept and the target audience of the Arduino.

-j

This handler is never called in a reentrant fashion since it is only triggered by the main program, not interrupt routines. It requires a voluntary call to dtAlarm.delay() or other functions which check it for Alarm triggers. Care for static variables should be no different than with any other Arduino function used.

Thanks. I wasn't clear if the core trigger was a timer ISR or something else, and am currently too busy/lazy to read the code and find out.

-j

setup()
{
Alarm.once(500,foo);
Alarm.repeat(1000, bar);
}

void bar()
{
foo();
}

Uhh, don't be so sure that the compiler would not optimize away bar() making the pointer to &bar() == &foo(). Now you still can't distinguish between the two. :frowning:

That's just a bigger table.

In this case, a much bigger table! :slight_smile: It is worth spending some time analyzing mem's code. While I may not have made the same design decisions as him, it did allow me to form the opinion that while on the surface this task/problem seems simple/easy to solve, to do it in a way that will actually be useful probably means making things a little more complicated than first desired and making certain tradeoffs.

#define MAX_ALARMS 17
#include <Alarm.h>
...

I would assume that the Alarm library code would need to be compiled with this MAX_ALARMS macro set to the user's custom value (this is not just setting a variable somewhere). If so, how could that be delivered as a precompiled library in the core then?

how could that be delivered as a precompiled library in the core then?

It's my understanding that the libraries are not pre-compiled, but that the source gets copied to the applet subdirectory of your sketch directory, and gets compiled as part of your sketch. I could be wrong about that, too, though...

-j

Regarding re-entrance, as Martin says, the service routine is only entered by foreground code calling another alarm library function (typically it will be the library's delay or wait function). It is blocked from processing if its called from an alarm handler. There is flag in the service routine that ensures that a new alarm will never be triggered while processing an existing alarm. This minimizes stack usage and reduces the likelihood that shared global variables in the users foreground sketch will be abused.

The comments are stimulating my thinking around a very simple API along the lines mellis suggested with the option to create additional and more sophisticated alarm instances for those that need them. This gives basic scheduling that is highly intuitive out of the box but the richer functionality can be added by people that understand how to instantiate a class. I don't think it adds any complexity to code I have (although it will be more complex than the code posted above) but I do want to see if it adds much in the way of resource consumption.

Here is slightly modified version of the DateTimeAlarm code that compiles in Arduino version 0012:

/*
  DateTimeAlarms.h - Arduino Date and Time alrms library

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

*/

#ifndef DateTimeAlarms_h
#define DateTimeAlarms_h

#include <inttypes.h>
//#include <wiring.h> // moved into cpp file for Arduino ver 0012

#include "dateTime.h"

typedef enum {
     dtMillisecond, dtSecond, dtMinute, dtHour, dtDay //,clkMonth,clkYear
 } dtUnits_t;

typedef struct  {
      uint8_t isEnabled              :1 ;
      uint8_t isOneShot              :1 ; 
      uint8_t isAlarm                :1 ;    
   }
     AlarmMode_t   ;

typedef uint8_t AlarmID_t;
#define dtINVALID_ALARM_ID 255
#define dtNBR_ALARMS 6

class AlarmClass;  // forward reference
typedef void (*OnTick_t)(uint8_t);  // alarm callback function typedef 

// class defining an alarm instance, only used by dtAlarmsClass
class AlarmClass
{      
private:
public:
      AlarmClass();
      OnTick_t onTickHandler;      
      void updateNextTrigger();

      time_t value;
      time_t nextTrigger;
      AlarmID_t ID;           // unique instance id (only used for debugging
      AlarmMode_t Mode;
};


// class containing the collection of alarms
class dtAlarmsClass
{
   friend class TimerClass;
private:
   AlarmClass Alarm[dtNBR_ALARMS];
   void serviceAlarms();
   boolean isServicing;
   AlarmID_t nextID;
   AlarmID_t create( time_t value, OnTick_t onTickHandler,boolean isAlarm, boolean isEnabled );
public:
      dtAlarmsClass();
      void delay(unsigned long ms);
      uint8_t getDigitsNow( dtUnits_t Units);  // returns the current digit value for the given time unit
      void waitForDigits( uint8_t Digits, dtUnits_t Units);
      void waitForRollover(dtUnits_t Units);
      
      // functions for specific alarms identifed by alarm ID
      AlarmID_t createAlarm( time_t value, OnTick_t onTickHandler, boolean isEnabled = true );
    AlarmID_t createTimer( time_t value, OnTick_t onTickHandler, boolean isEnabled = true );
      void setValue(AlarmID_t ID, time_t value);
      void enable(AlarmID_t ID);
      void disable(AlarmID_t ID);

};

extern dtAlarmsClass dtAlarms;  // make an instance for the user

/*==============================================================================
 * MACROS
 *============================================================================*/

/* public */
#define waitUntilThisSecond(_val_) waitForDigits( _val_, dtSecond)
#define waitUntilThisMinute(_val_) waitForDigits( _val_, dtMinute)
#define waitUntilThisHour(_val_)   waitForDigits( _val_, dtHour)
#define waitUntilThisDay(_val_)    waitForDigits( _val_, dtDay)
#define waitMinuteRollover() waitForRollover(dtSecond)
#define waitHourRollover()   waitForRollover(dtMinute)
#define waitDayRollover()    waitForRollover(dtHour)

#define AlarmHMS(_hr_, _min_, _sec_) (_hr_ * SECS_PER_HOUR + _min_ * SECS_PER_MIN + _sec_)

#endif /* Clock_h */

here is the DateTimeAlarms.cpp file moded for 0012

/*
 DateTimeAlarms.cpp - cooperative scheduler for arduino

 */

extern "C" {
#include <string.h> // for memset
}

#include <wiring.h> // moved from header to here for Arduino 0012
#include "DateTimeAlarms.h"
#include "DateTime.h"

//**************************************************************
//* Alarm Class Constructor

AlarmClass::AlarmClass()
{
  Mode.isAlarm =  Mode.isEnabled = Mode.isOneShot = 0;
  value = nextTrigger = 0;
  onTickHandler = NULL;  // prevent a callback until this pointer is explicitly set 
}

//**************************************************************
//* Private Methods

void AlarmClass::updateNextTrigger()
{
  if( (value != 0) && (Mode.isEnabled != 0) )
  {
    time_t time = DateTime.now();
    if(Mode.isAlarm && nextTrigger <= time )   // update alarm if next trigger is not yet in the future
    {
      if( value >= time ) { // is the value a specific data and time in the future
        nextTrigger = value;  // yes, trigger on this value      
      }
      else if ( value <= SECS_PER_DAY) {
        if( value + previousMidnight(DateTime.now()) < time)
          nextTrigger = value + nextMidnight(time); // if time has passed then set for tomorrow 
        else
          nextTrigger = value + previousMidnight(time);  // set the date to today and add the time given in value      
      }
      else if ( value <= SECS_PER_WEEK) {
        nextTrigger = value + previousMidnight(time); // set the date to today and add the time given in value
      }
      else {
        Mode.isEnabled = 0; // values more than a year but less than today have expired so the alarm is disabled 
      }
    }
    if(Mode.isAlarm == false){
      // its a timer
      nextTrigger = time + value;  // add the value to previous time (this ensures delay always at least Value seconds)
    }
  }
  else {
    Mode.isEnabled = 0;  // Disable if the value is 0
  }
}

//**************************************************************
//* Date Time Alarms Public Methods

dtAlarmsClass::dtAlarmsClass()
{
  nextID = 0;
  isServicing = false;
}

AlarmID_t dtAlarmsClass::createAlarm(time_t value, OnTick_t onTickHandler, boolean isEnabled ){  // returns true if has been registerd ok
  return create( value, onTickHandler, true, isEnabled );
}

AlarmID_t dtAlarmsClass::createTimer( time_t value, OnTick_t onTickHandler, boolean isEnabled ){  // returns true if has been registerd ok
  return create( value, onTickHandler, false, isEnabled );
}

void dtAlarmsClass::enable(AlarmID_t ID)
{
  if(ID < dtNBR_ALARMS){
    Alarm[ID].Mode.isEnabled = (Alarm[ID].value != 0) && (Alarm[ID].onTickHandler != 0) ;  // only enable if value is non zero and a tick handler has been set
    Alarm[ID].updateNextTrigger(); // trigger is updated whenever  this is called, even if already enabled       
  }
}

void dtAlarmsClass::disable(AlarmID_t ID)
{
  if(ID < dtNBR_ALARMS)
    Alarm[ID].Mode.isEnabled = false;
}

void dtAlarmsClass::setValue(AlarmID_t ID, time_t value){
  if(ID < dtNBR_ALARMS){
    Alarm[ID].value = value;
    enable(ID);
  }
}

// following functions are not Alarm ID specific.
void dtAlarmsClass::delay(unsigned long ms)
{
  unsigned long endtime = millis() + ms;
  boolean Overflow = endtime < millis();
  if( Overflow){
    while( millis() > endtime)
      serviceAlarms();
  }
  while( millis() < endtime)
    serviceAlarms();
}

void dtAlarmsClass::waitForDigits( uint8_t Digits, dtUnits_t Units)
{
  while(Digits != getDigitsNow(Units) )
  {
    serviceAlarms();
  }
}

void dtAlarmsClass::waitForRollover( dtUnits_t Units)
{
  while(getDigitsNow(Units) == 0  ) // if its just rolled over than wait for another rollover                                  
    serviceAlarms();
  waitForDigits(0, Units);
}

uint8_t dtAlarmsClass::getDigitsNow( dtUnits_t Units)
{
  time_t time = DateTime.now();
  if(Units == dtSecond) return numberOfSeconds(time);
  if(Units == dtMinute) return numberOfMinutes(time); 
  if(Units == dtHour) return numberOfHours(time);
  if(Units == dtDay) return dayOfWeek(time);
  return 255;  // This should never happen 
}

//***********************************************************
//* Private Methods

void dtAlarmsClass::serviceAlarms()
{
  if(! isServicing)
  {
    isServicing = true;
    for(uint8_t i = 0; i < dtNBR_ALARMS; i++)
    {
      if( Alarm[i].Mode.isEnabled && (DateTime.now() >= Alarm[i].nextTrigger)  )
      {
        OnTick_t TickHandler = Alarm[i].onTickHandler;
        Alarm[i].updateNextTrigger();
        if( TickHandler != NULL) {                        
          (*TickHandler)(i);     // call the handler
        }
      }
    }
    isServicing = false;
  }
}

AlarmID_t dtAlarmsClass::create( time_t value, OnTick_t onTickHandler,boolean isAlarm, boolean isEnabled ){  // returns true if has been registerd ok
  AlarmID_t id = dtINVALID_ALARM_ID;
  if( nextID < dtNBR_ALARMS){
      id = nextID;
    Alarm[id].onTickHandler = onTickHandler;
    Alarm[id].Mode.isAlarm = isAlarm;
      Alarm[id].value = value;
      isEnabled ?  enable(id) : disable(id);   
    nextID++;   
  }
  return id;
}

// make one instance for the user to use
dtAlarmsClass dtAlarms = dtAlarmsClass() ;

Hi,

I'm trying to use DateTimeAlarms in Arduino 0012.
I need to have a periodic alarm with period PERIOD, but that feature seems to have disappeared... I managed to do it by doing, at the end of the callback function,

  dtAlarms.setValue(alarmID, DateTime.now() + PERIOD);

Is there any other option?

Moreover, I have a question: is dtAlarms.delay() blocking? If I understand well, I need to include it in the loop() so that alarms get serviced. Because I need sparse alarms (daily or so), this an important thing...

Best regards

If you use the createTimer method instead if createAlarm, the argument to setValue will be the number of seconds from the time now. I think that is what you want.

Timer1 = dtAlarms.createTimer( PERIOD, &OnTimer); // call OnTimer function every PERIOD seconds

//the following is optional if the period needs to be changed on the fly:
dtAlarms.setValue(Timer1, NEW_PERIOD); // change the period for Timer1

dtAlarms.delay() is blocking to the foreground sketch code, it works like delay() – with the difference that while waiting in dtAlarms.delay(), the scheduler is checking and servicing the alarms and timers, so the alarm code will be run even though the foreground sketch is blocked.