Files
SyncHome/trunk/Arduino/libraries/ESP8266TimeAlarms/ESP8266TimeAlarms.h
2023-03-17 11:59:21 +00:00

242 lines
9.3 KiB
C++

// TimeAlarms.h - Arduino Time alarms header for use with Time library
#ifndef TimeAlarms_h
#define TimeAlarms_h
#include <Arduino.h>
#include <time.h>
#if !defined(dtNBR_ALARMS )
#if defined(__AVR__)
#define dtNBR_ALARMS 6 // max is 255
#elif defined(ESP8266)
#define dtNBR_ALARMS 20 // for esp8266 chip - max is 255
#else
#define dtNBR_ALARMS 12 // assume non-AVR has more memory
#endif
#endif
#define USE_SPECIALIST_METHODS // define this for testing
typedef enum {
dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday
} timeDayOfWeek_t;
#define SECS_PER_MIN ((time_t)(60UL))
#define SECS_PER_HOUR ((time_t)(3600UL))
#define SECS_PER_DAY ((time_t)(SECS_PER_HOUR * 24UL))
#define DAYS_PER_WEEK ((time_t)(7UL))
#define SECS_PER_WEEK ((time_t)(SECS_PER_DAY * DAYS_PER_WEEK))
#define SECS_PER_YEAR ((time_t)(SECS_PER_DAY * 365UL)) // TODO: ought to handle leap years
#define secs_per_year(y) ((time_t)(SECS_PER_DAY * year_lengths[isleap(y)]))
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
/* Useful Macros for getting elapsed time */
#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)
#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN)
#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
#define dayOfWeek(_time_) ((( _time_ / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY) // this is number of days since Jan 1 1970
#define elapsedSecsToday(_time_) (_time_ % SECS_PER_DAY) // the number of seconds since last midnight
// The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971
// Always set the correct time before settting alarms
#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY) // time at the start of the given day
#define nextMidnight(_time_) ( previousMidnight(_time_) + SECS_PER_DAY ) // time at the end of the given day
#define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY) ) // note that week starts on day 1
#define previousSunday(_time_) (_time_ - elapsedSecsThisWeek(_time_)) // time at the start of the week for the given time
#define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK) // time at the end of the week for the given time
static const int year_lengths[2] = {
365,
366
} ;
typedef enum {
dtMillisecond,
dtSecond,
dtMinute,
dtHour,
dtDay
} dtUnits_t;
typedef struct {
uint8_t alarmType :4 ; // enumeration of daily/weekly (in future:
// biweekly/semimonthly/monthly/annual)
// note that the current API only supports daily
// or weekly alarm periods
uint8_t isEnabled :1 ; // the timer is only actioned if isEnabled is true
uint8_t isOneShot :1 ; // the timer will be de-allocated after trigger is processed
} AlarmMode_t;
// new time based alarms should be added just before dtLastAlarmType
typedef enum {
dtNotAllocated,
dtTimer,
dtExplicitAlarm,
dtDailyAlarm,
dtWeeklyAlarm,
dtLastAlarmType
} dtAlarmPeriod_t ; // in future: dtBiweekly, dtMonthly, dtAnnual
// macro to return true if the given type is a time based alarm, false if timer or not allocated
#define dtIsAlarm(_type_) (_type_ >= dtExplicitAlarm && _type_ < dtLastAlarmType)
#define dtUseAbsoluteValue(_type_) (_type_ == dtTimer || _type_ == dtExplicitAlarm)
typedef uint8_t AlarmID_t;
typedef AlarmID_t AlarmId; // Arduino friendly name
#define dtINVALID_ALARM_ID 255
#define dtINVALID_TIME (time_t)(-1)
//#define AlarmHMS(_hr_, _min_, _sec_) (_hr_ * SECS_PER_HOUR + _min_ * SECS_PER_MIN + _sec_)
//Hack to get timezone and stuff working
#ifdef ARDUINO_ARCH_ESP8266
#include <functional>
typedef std::function<void()> OnTick_t;
#else
typedef void (*OnTick_t)(); // alarm callback function typedef
#endif
// class defining an alarm instance, only used by dtAlarmsClass
class AlarmClass
{
public:
AlarmClass();
OnTick_t onTickHandler;
void updateNextTrigger();
time_t value;
time_t nextTrigger;
AlarmMode_t Mode;
};
// class containing the collection of alarms
class TimeAlarmsClass
{
private:
AlarmClass Alarm[dtNBR_ALARMS];
void serviceAlarms();
uint8_t isServicing;
uint8_t servicedAlarmId; // the alarm currently being serviced
AlarmID_t create(time_t value, OnTick_t onTickHandler, uint8_t isOneShot, dtAlarmPeriod_t alarmType);
public:
TimeAlarmsClass();
//Translate Alarm time from localtime in to epochtime this will handle timezone things
int AlarmHMS(int H, int M, int S) {
time_t t1,t2;
struct tm tma;
time(&t1);
localtime_r(&t1, &tma);
tma.tm_hour = H;
tma.tm_min = M;
tma.tm_sec = S;
t2 = mktime(&tma);
return t2-previousMidnight(t1);
}
// functions to create alarms and timers
// trigger once at the given time in the future
AlarmID_t triggerOnce(time_t value, OnTick_t onTickHandler) {
if (value <= 0) return dtINVALID_ALARM_ID;
return create(value, onTickHandler, true, dtExplicitAlarm);
}
// trigger once at given time of day
AlarmID_t alarmOnce(time_t value, OnTick_t onTickHandler) {
if (value <= 0 || value > SECS_PER_DAY) return dtINVALID_ALARM_ID;
return create(value, onTickHandler, true, dtDailyAlarm);
}
AlarmID_t alarmOnce(const int H, const int M, const int S, OnTick_t onTickHandler) {
return alarmOnce(AlarmHMS(H,M,S), onTickHandler);
}
// trigger once on a given day and time
AlarmID_t alarmOnce(const timeDayOfWeek_t DOW, const int H, const int M, const int S, OnTick_t onTickHandler) {
time_t value = (DOW-1) * SECS_PER_DAY + AlarmHMS(H,M,S);
if (value <= 0) return dtINVALID_ALARM_ID;
return create(value, onTickHandler, true, dtWeeklyAlarm);
}
// trigger daily at given time of day
AlarmID_t alarmRepeat(time_t value, OnTick_t onTickHandler) {
if (value > SECS_PER_DAY) return dtINVALID_ALARM_ID;
return create(value, onTickHandler, false, dtDailyAlarm);
}
AlarmID_t alarmRepeat(const int H, const int M, const int S, OnTick_t onTickHandler) {
return alarmRepeat(AlarmHMS(H,M,S), onTickHandler);
}
// trigger weekly at a specific day and time
AlarmID_t alarmRepeat(const timeDayOfWeek_t DOW, const int H, const int M, const int S, OnTick_t onTickHandler) {
time_t value = (DOW-1) * SECS_PER_DAY + AlarmHMS(H,M,S);
if (value <= 0) return dtINVALID_ALARM_ID;
return create(value, onTickHandler, false, dtWeeklyAlarm);
}
// trigger once after the given number of seconds
AlarmID_t timerOnce(time_t value, OnTick_t onTickHandler) {
if (value <= 0) return dtINVALID_ALARM_ID;
return create(value, onTickHandler, true, dtTimer);
}
AlarmID_t timerOnce(const int H, const int M, const int S, OnTick_t onTickHandler) {
return timerOnce(AlarmHMS(H,M,S), onTickHandler);
}
// trigger at a regular interval
AlarmID_t timerRepeat(time_t value, OnTick_t onTickHandler) {
if (value <= 0) return dtINVALID_ALARM_ID;
return create(value, onTickHandler, false, dtTimer);
}
AlarmID_t timerRepeat(const int H, const int M, const int S, OnTick_t onTickHandler) {
return timerRepeat(AlarmHMS(H,M,S), onTickHandler);
}
void delay(unsigned long ms);
// utility methods
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);
// low level methods
void enable(AlarmID_t ID); // enable the alarm to trigger
void disable(AlarmID_t ID); // prevent the alarm from triggering
AlarmID_t getTriggeredAlarmId(); // returns the currently triggered alarm id
bool getIsServicing(); // returns isServicing
void write(AlarmID_t ID, time_t value); // write the value (and enable) the alarm with the given ID
time_t read(AlarmID_t ID); // return the value for the given timer
dtAlarmPeriod_t readType(AlarmID_t ID); // return the alarm type for the given alarm ID
void free(AlarmID_t ID); // free the id to allow its reuse
#ifndef USE_SPECIALIST_METHODS
private: // the following methods are for testing and are not documented as part of the standard library
#endif
uint8_t count(); // returns the number of allocated timers
time_t getNextTrigger(); // returns the time of the next scheduled alarm
bool isAllocated(AlarmID_t ID); // returns true if this id is allocated
bool isAlarm(AlarmID_t ID); // returns true if id is for a time based alarm, false if its a timer or not allocated
};
extern TimeAlarmsClass Alarm; // 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)
#endif /* TimeAlarms_h */