TimeAlarms.cpp file (no comments)
/*
TimeAlarms.cpp
testing version
*/
extern "C" {
#include <string.h> // for memset
}
#include <WProgram.h>
#include "TimeAlarms.h"
#include "Time.h"
#define IS_ONESHOT true
#define IS_REPEAT false
#define IS_ALARM true
#define IS_TIMER false
AlarmClass::AlarmClass()
{
Mode.isAlarm = Mode.isEnabled = Mode.isOneShot = 0;
value = nextTrigger = 0;
onTickHandler = NULL; // prevent a callback until this pointer is explicitly set
}
void AlarmClass::updateNextTrigger()
{
if( (value != 0) && Mode.isEnabled )
{
time_t time = now();
if(Mode.isAlarm && nextTrigger <= time )
{
if(Mode.alarmType == dtExplicitAlarm )
{
nextTrigger = value; // yes, trigger on this value
}
else if(Mode.alarmType == dtDailyAlarm)
{
if( value + previousMidnight(now()) <= time)
{
nextTrigger = value + nextMidnight(time);
}
else
{
nextTrigger = value + previousMidnight(time);
}
}
else if(Mode.alarmType == dtWeeklyAlarm)
{
if( (value + previousSunday(now())) <= time)
{
nextTrigger = value + nextSunday(time);
}
else
{
nextTrigger = value + previousSunday(time);
}
}
else // its not a recognized alarm type - this should not happen
{
Mode.isEnabled = 0; // Disable the alarm
}
}
if(Mode.isAlarm == false)
{
// its a timer
nextTrigger = time + value;
}
}
else
{
Mode.isEnabled = 0;
}
}
TimeAlarmsClass::TimeAlarmsClass()
{
isServicing = false;
for(uint8_t id = 0; id < dtNBR_ALARMS; id++)
free(id);
}
// this method will now return an error if the value is greater than one day - use DOW methods for weekly alarms
AlarmID_t TimeAlarmsClass::alarmOnce(time_t value, OnTick_t onTickHandler){
if( value <= SECS_PER_DAY)
return create( value, onTickHandler, IS_ALARM, IS_ONESHOT, dtDailyAlarm );
else
return dtINVALID_ALARM_ID; // dont't allocate if the time is greater than one day
}
AlarmID_t TimeAlarmsClass::alarmOnce(const int H, const int M, const int S,OnTick_t onTickHandler){
return create( AlarmHMS(H,M,S), onTickHandler, IS_ALARM, IS_ONESHOT, dtDailyAlarm );
}
AlarmID_t TimeAlarmsClass::alarmOnce(const timeDayOfWeek_t DOW, const int H, const int M, const int S, OnTick_t onTickHandler){
unsigned long secondsToGivenDay = (DOW-1) * SECS_PER_DAY;
return create( secondsToGivenDay + AlarmHMS(H,M,S), onTickHandler, IS_ALARM, IS_ONESHOT, dtWeeklyAlarm );
}
// this method will now return an error if the value is greater than one day - use DOW methods for weekly alarms
AlarmID_t TimeAlarmsClass::alarmRepeat(time_t value, OnTick_t onTickHandler){ // trigger daily at the given time
if( value <= SECS_PER_DAY)
return create( value, onTickHandler, IS_ALARM, IS_REPEAT, dtDailyAlarm );
else
return dtINVALID_ALARM_ID; // dont't allocate if the time is greater than one day
}
AlarmID_t TimeAlarmsClass::alarmRepeat(const int H, const int M, const int S, OnTick_t onTickHandler){ // as above with HMS arguments
return create( AlarmHMS(H,M,S), onTickHandler, IS_ALARM, IS_REPEAT, dtDailyAlarm );
}
AlarmID_t TimeAlarmsClass::alarmRepeat(const timeDayOfWeek_t DOW, const int H, const int M, const int S, OnTick_t onTickHandler){
unsigned long secondsToGivenDay = (DOW-1) * SECS_PER_DAY;
return create( secondsToGivenDay + AlarmHMS(H,M,S), onTickHandler, IS_ALARM, IS_REPEAT, dtWeeklyAlarm );
}
AlarmID_t TimeAlarmsClass::timerOnce(time_t value, OnTick_t onTickHandler){
return create( value, onTickHandler, IS_TIMER, IS_ONESHOT, dtTimer );
}
AlarmID_t TimeAlarmsClass::timerOnce(const int H, const int M, const int S, OnTick_t onTickHandler){
return create( AlarmHMS(H,M,S), onTickHandler, IS_TIMER, IS_ONESHOT, dtTimer );
}
AlarmID_t TimeAlarmsClass::timerRepeat(time_t value, OnTick_t onTickHandler){
return create( value, onTickHandler, IS_TIMER, IS_REPEAT, dtTimer);
}
AlarmID_t TimeAlarmsClass::timerRepeat(const int H, const int M, const int S, OnTick_t onTickHandler){
return create( AlarmHMS(H,M,S), onTickHandler, IS_TIMER, IS_REPEAT, dtTimer);
}
void TimeAlarmsClass::enable(AlarmID_t ID)
{
if(ID < dtNBR_ALARMS && Alarm[ID].Mode.isAllocated){
Alarm[ID].Mode.isEnabled = (Alarm[ID].value != 0) && (Alarm[ID].onTickHandler != 0) ;
Alarm[ID].updateNextTrigger();
}
}
void TimeAlarmsClass::disable(AlarmID_t ID)
{
if(ID < dtNBR_ALARMS && Alarm[ID].Mode.isAllocated)
Alarm[ID].Mode.isEnabled = false;
}
void TimeAlarmsClass::free(AlarmID_t ID)
{
if(ID < dtNBR_ALARMS && Alarm[ID].Mode.isAllocated)
{
Alarm[ID].Mode.isEnabled = Alarm[ID].Mode.isAllocated = false;
Alarm[ID].Mode.alarmType = dtNotAlarm;
Alarm[ID].onTickHandler = 0;
Alarm[ID].value = 0;
Alarm[ID].nextTrigger = 0;
}
}
// write the given value to the given alarm
void TimeAlarmsClass::write(AlarmID_t ID, time_t value)
{
if(ID < dtNBR_ALARMS && Alarm[ID].Mode.isAllocated){
Alarm[ID].value = value;
enable(ID);
}
}
// return the value for the given alarm ID
time_t TimeAlarmsClass::read(AlarmID_t ID)
{
if(ID < dtNBR_ALARMS && Alarm[ID].Mode.isAllocated)
return Alarm[ID].value ;
else
return 0l;
}
// return the alarm type for the given alarm ID
dtAlarmPeriod_t TimeAlarmsClass::readType(AlarmID_t ID)
{
if(ID < dtNBR_ALARMS && Alarm[ID].Mode.isAllocated)
return (dtAlarmPeriod_t)Alarm[ID].Mode.alarmType ;
else
return dtNotAlarm;
}
// returns the number of allocated timers
uint8_t TimeAlarmsClass::count()
{
uint8_t c = 0;
for(uint8_t id = 0; id < dtNBR_ALARMS; id++)
{
if(Alarm[id].Mode.isAllocated)
c++;
}
return c;
}
AlarmID_t TimeAlarmsClass::getTriggeredAlarmId() //returns the currently triggered alarm id
// returns dtINVALID_ALARM_ID if not invoked from within an alarm handler
{
if(isServicing)
return servicedAlarmId; // new private data member used instead of local loop variable i in serviceAlarms();
else
return dtINVALID_ALARM_ID; // valid ids only available when servicing a callback
}
void TimeAlarmsClass::delay(unsigned long ms)
{
unsigned long start = millis();
while( millis() - start <= ms)
serviceAlarms();
}
void TimeAlarmsClass::waitForDigits( uint8_t Digits, dtUnits_t Units)
{
while(Digits != getDigitsNow(Units) )
{
serviceAlarms();
}
}
void TimeAlarmsClass::waitForRollover( dtUnits_t Units)
{
while(getDigitsNow(Units) == 0 )
serviceAlarms();
waitForDigits(0, Units);
}
uint8_t TimeAlarmsClass::getDigitsNow( dtUnits_t Units)
{
time_t time = 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
}
void TimeAlarmsClass::serviceAlarms()
{
if(! isServicing)
{
isServicing = true;
for( servicedAlarmId = 0; servicedAlarmId < dtNBR_ALARMS; servicedAlarmId++)
{
if( Alarm[servicedAlarmId].Mode.isEnabled && (now() >= Alarm[servicedAlarmId].nextTrigger) )
{
OnTick_t TickHandler = Alarm[servicedAlarmId].onTickHandler;
if(Alarm[servicedAlarmId].Mode.isOneShot)
Alarm[servicedAlarmId].Mode.isEnabled = Alarm[servicedAlarmId].Mode.isAllocated = false;
else
Alarm[servicedAlarmId].updateNextTrigger();
if( TickHandler != NULL) {
(*TickHandler)(); // call the handler
}
}
}
isServicing = false;
}
}
// returns the absolute time of the next scheduled alarm, or 0 if none
time_t TimeAlarmsClass::getNextTrigger()
{
time_t nextTrigger = 0xffffffff; // the max time value
for(uint8_t id = 0; id < dtNBR_ALARMS; id++)
{
if(Alarm[id].Mode.isAllocated )
{
if(Alarm[id].nextTrigger < nextTrigger)
nextTrigger = Alarm[id].nextTrigger;
}
}
return nextTrigger == 0xffffffff ? 0 : nextTrigger;
}
// attempt to create an alarm and return true if successful
AlarmID_t TimeAlarmsClass::create( time_t value, OnTick_t onTickHandler, uint8_t isAlarm, uint8_t isOneShot, dtAlarmPeriod_t alarmType, uint8_t isEnabled)
{
if( ! (isAlarm && now() < SECS_PER_YEAR)) // only create alarm ids if the time is at least Jan 1 1971
{
for(uint8_t id = 0; id < dtNBR_ALARMS; id++)
{
if(Alarm[id].Mode.isAllocated == false)
{
// here if there is an Alarm id is available
Alarm[id].Mode.isAllocated = true;
Alarm[id].onTickHandler = onTickHandler;
Alarm[id].Mode.isAlarm = isAlarm;
Alarm[id].Mode.isOneShot = isOneShot;
Alarm[id].Mode.alarmType = alarmType;
Alarm[id].value = value;
isEnabled ? enable(id) : disable(id);
return id; // alarm created ok
}
}
}
return dtINVALID_ALARM_ID;
}
TimeAlarmsClass Alarm = TimeAlarmsClass() ;