#ifndef __CLOCK_H__
#define __CLOCK_H__
#include <RTClib.h>
#include "config.h"
enum AlarmState {
ALARM_DISABLED,
ALARM_OFF,
ALARM_ACTIVE,
ALARM_SNOOZED,
ALARM_STOPPED,
};
class Clock {
public:
Clock();
void begin();
DateTime now();
void incrementMinute();
void incrementHour();
bool alarmEnabled();
DateTime alarmTime();
bool alarmActive();
void toggleAlarm();
void snooze();
void stopAlarm();
void incrementAlarmHour();
void incrementAlarmMinute();
protected:
bool _isAlarmDueTime();
#if USE_RTC
RTC_DS1307 _rtc;
#else
RTC_Millis _rtc;
#endif
byte _alarm_hour;
byte _alarm_minute;
AlarmState _alarm_state;
unsigned long _alarm_snooze_time;
};
#endif
#include <Arduino.h>
#include "Clock.h"
#define MINUTE 60 * 1000L /* ms */
#define TIMESPAN_DAY TimeSpan(1, 0, 0, 0)
#define NVRAM_ADDR_ALARM_ENABLED 0
#define NVRAM_ADDR_ALARM_HOUR 1
#define NVRAM_ADDR_ALARM_MINUTE 2
Clock::Clock()
: _alarm_state(ALARM_OFF)
, _alarm_snooze_time(0)
, _alarm_hour(DEFAULT_ALARM_HOUR)
, _alarm_minute(0) {
}
void Clock::begin() {
# if USE_RTC
if (!_rtc.begin()) {
Serial.println("Couldn't find RTC");
abort();
}
_alarm_state = _rtc.readnvram(NVRAM_ADDR_ALARM_ENABLED) ? ALARM_OFF : ALARM_DISABLED;
_alarm_hour = _rtc.readnvram(NVRAM_ADDR_ALARM_HOUR) % 24;
_alarm_minute = _rtc.readnvram(NVRAM_ADDR_ALARM_MINUTE) % 60;
# else
DateTime zeroTime;
_rtc.begin(zeroTime);
# endif
}
DateTime Clock::now() {
return _rtc.now();
}
void Clock::incrementMinute() {
DateTime now = _rtc.now();
DateTime newTime = DateTime(now.year(), now.month(), now.day(), now.hour(),
(now.minute() + 1) % 60);
_rtc.adjust(newTime);
}
void Clock::incrementHour() {
DateTime now = _rtc.now();
DateTime newTime = DateTime(now.year(), now.month(), now.day(),
(now.hour() + 1) % 24, now.minute());
_rtc.adjust(newTime);
}
bool Clock::_isAlarmDueTime() {
auto currentTime = now();
auto alarm = alarmTime();
return ((currentTime.hour() == alarm.hour())
&& (currentTime.minute() == alarm.minute()));
}
bool Clock::alarmEnabled() {
return _alarm_state != ALARM_DISABLED;
}
bool Clock::alarmActive() {
switch (_alarm_state) {
case ALARM_DISABLED:
return false;
case ALARM_OFF:
if (_isAlarmDueTime()) {
_alarm_state = ALARM_ACTIVE;
return true;
}
return false;
case ALARM_ACTIVE:
return true;
case ALARM_SNOOZED:
if (millis() >= _alarm_snooze_time) {
_alarm_state = ALARM_ACTIVE;
return true;
}
return false;
case ALARM_STOPPED:
if (!_isAlarmDueTime()) {
_alarm_state = ALARM_OFF;
}
return false;
default:
return false;
}
}
void Clock::toggleAlarm() {
bool enabled = !alarmEnabled();
_alarm_state = enabled ? ALARM_OFF : ALARM_DISABLED;
# if USE_RTC
_rtc.writenvram(NVRAM_ADDR_ALARM_ENABLED, enabled);
# endif
}
DateTime Clock::alarmTime() {
DateTime now = _rtc.now();
DateTime alarm = DateTime(now.year(), now.month(), now.day(), _alarm_hour, _alarm_minute);
return alarm >= now ? alarm : alarm + TIMESPAN_DAY;
}
void Clock::snooze() {
_alarm_state = ALARM_SNOOZED;
_alarm_snooze_time = millis() + SNOOZE_TIME * MINUTE;
}
void Clock::stopAlarm() {
_alarm_state = ALARM_STOPPED;
}
void Clock::incrementAlarmHour() {
_alarm_hour = (_alarm_hour + 1) % 24;
_alarm_state = ALARM_OFF;
# if USE_RTC
_rtc.writenvram(NVRAM_ADDR_ALARM_HOUR, _alarm_hour);
# endif
}
void Clock::incrementAlarmMinute() {
_alarm_minute = (_alarm_minute + 1) % 60;
_alarm_state = ALARM_OFF;
# if USE_RTC
_rtc.writenvram(NVRAM_ADDR_ALARM_MINUTE, _alarm_minute);
# endif
}