From 6b1c04e1de8a47e882bbce82895c173215b10574 Mon Sep 17 00:00:00 2001 From: Lewis Jackson <> Date: Wed, 31 May 2023 04:04:25 +0300 Subject: [PATCH 1/5] Experimental timer-based RTC updates --- src/WatchFace.cpp | 2 +- src/Watchy.cpp | 15 ++++-- src/WatchyRTC.cpp | 128 +++++++++++++++++++--------------------------- src/WatchyRTC.h | 13 +++-- src/config.h | 4 +- 5 files changed, 72 insertions(+), 90 deletions(-) diff --git a/src/WatchFace.cpp b/src/WatchFace.cpp index db7bba0..46f840b 100644 --- a/src/WatchFace.cpp +++ b/src/WatchFace.cpp @@ -92,7 +92,7 @@ void WatchFace::DrawWatchFace(bool partialRefresh) DrawBatteryIcon(); tmElements_t currentTime; - m_RTC.read(currentTime, m_tzOffset); + m_RTC.Get(currentTime, m_tzOffset); SevenSegment sevenSegment(30, 60, 6, 5, 5); if (currentTime.Hour < 10) { diff --git a/src/Watchy.cpp b/src/Watchy.cpp index 0442586..183e6ff 100644 --- a/src/Watchy.cpp +++ b/src/Watchy.cpp @@ -17,9 +17,13 @@ void Watchy::Init() esp_sleep_wakeup_cause_t wakeup_reason; wakeup_reason = esp_sleep_get_wakeup_cause(); - Wire.begin(SDA, SCL); - m_RTC.init(); + if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT0) { + if(!m_RTC.CheckWakeup()) { + DeepSleep(); + } + } + Wire.begin(SDA, SCL); m_display.epd2.selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0)); m_display.init(0, g_displayFullInit, 10, true); SetBusyCallback(); @@ -63,6 +67,7 @@ void Watchy::Init() default: { BmaConfig(); + m_RTC.Init(); ConnectWiFi(); SyncNTPTime(); DisconnectWiFi(); @@ -83,7 +88,6 @@ void Watchy::DeepSleep() } g_displayFullInit = false; // Notify not to init it again - m_RTC.clearAlarm(); // resets the alarm flag in the RTC // Set GPIOs 0-39 to input to avoid power leaking out const uint64_t ignore = 0b11110001000000110000100111000010; // Ignore some GPIOs due to resets @@ -97,6 +101,9 @@ void Watchy::DeepSleep() esp_sleep_enable_ext1_wakeup( BTN_PIN_MASK | ACC_INT_MASK, ESP_EXT1_WAKEUP_ANY_HIGH); + + m_RTC.SetTimer(); + esp_deep_sleep_start(); } @@ -158,7 +165,7 @@ void Watchy::SyncNTPTime() if (success) { tmElements_t tm; breakTime((time_t)timeClient.getEpochTime(), tm); - m_RTC.set(tm); + m_RTC.Set(tm); } else { Serial.begin(9600); Serial.println("Failed to get NTP time"); diff --git a/src/WatchyRTC.cpp b/src/WatchyRTC.cpp index 5eed0ab..55d25a4 100644 --- a/src/WatchyRTC.cpp +++ b/src/WatchyRTC.cpp @@ -1,58 +1,53 @@ #include "WatchyRTC.h" +#if (UPDATE_INTERVAL > 255) +#error "UPDATE_INTERVAL must be either a multiple of 60, or less than 256 seconds" +#endif + +RTC_DATA_ATTR bool WatchyRTC::m_timerSet = false; +RTC_DATA_ATTR bool WatchyRTC::m_initialTimer = true; + WatchyRTC::WatchyRTC() { } -void WatchyRTC::init() { - byte error; - Wire.beginTransmission(RTC_PCF_ADDR); - error = Wire.endTransmission(); -} - -void WatchyRTC::config(String datetime) { // String datetime format is YYYY:MM:DD:HH:MM:SS - _PCFConfig(datetime); -} - -void WatchyRTC::clearAlarm() { - int nextAlarmMinute = 0; - rtc_pcf.clearAlarm(); // resets the alarm flag in the RTC - - int second = rtc_pcf.getSecond(); - int minute = rtc_pcf.getMinute(); - int hour = rtc_pcf.getHour(); - - minute += UPDATE_INTERVAL; - - if (minute >= 60) { - hour += minute / 60; - minute = minute % 60; - } - - rtc_pcf.setAlarm(minute, hour, 99, 99); -} - -void WatchyRTC::read(tmElements_t & tm, int offsetInSeconds) +void WatchyRTC::Init() { - rtc_pcf.getDate(); - tm.Year = y2kYearToTm(rtc_pcf.getYear()); +} + +void WatchyRTC::Get(tmElements_t & tm, int offsetInSeconds) +{ + rtc_pcf.getDateTime(); + tm.Year = y2kYearToTm(rtc_pcf.getYear()); tm.Month = rtc_pcf.getMonth(); - tm.Day = rtc_pcf.getDay(); - tm.Wday = rtc_pcf.getWeekday() + 1; // TimeLib & DS3231 has Wday range of 1-7, but PCF8563 stores day of week in 0-6 range - tm.Hour = rtc_pcf.getHour(); + tm.Day = rtc_pcf.getDay(); + tm.Wday = rtc_pcf.getWeekday() + 1; // TimeLib has Wday range of 1-7, but PCF8563 stores day of week in 0-6 range + tm.Hour = rtc_pcf.getHour(); tm.Minute = rtc_pcf.getMinute(); tm.Second = rtc_pcf.getSecond(); - OffsetTime(tm, offsetInSeconds); } -void WatchyRTC::set(tmElements_t tm) { +void WatchyRTC::Set(tmElements_t tm) +{ time_t t = makeTime(tm); // make and break to calculate tm.Wday breakTime(t, tm); // day, weekday, month, century(1=1900, 0=2000), year(0-99) - rtc_pcf.setDate(tm.Day, tm.Wday - 1, tm.Month, 0, tmYearToY2k(tm.Year)); - rtc_pcf.setTime(tm.Hour, tm.Minute, tm.Second); - clearAlarm(); + rtc_pcf.setDateTime(tm.Day, tm.Wday - 1, tm.Month, 0, tmYearToY2k(tm.Year), tm.Hour, tm.Minute, tm.Second); +} + +void WatchyRTC::SetTimer() +{ + if (!m_timerSet) { + rtc_pcf.getDateTime(); + // Sleep just long enough to get to a multiple of the update interval, makes updates happen on exact turn of the minute + int seconds = UPDATE_INTERVAL - (rtc_pcf.getSecond() % UPDATE_INTERVAL); + + // Non repeating + rtc_pcf.setTimer(seconds, TMR_1Hz, false); + m_timerSet = true; + m_initialTimer = true; + } } void WatchyRTC::OffsetTime(tmElements_t & tm, int offsetInSeconds) @@ -119,44 +114,25 @@ void WatchyRTC::OffsetTime(tmElements_t & tm, int offsetInSeconds) tm.Second = second; } -void WatchyRTC::_PCFConfig(String datetime) { // String datetime is YYYY:MM:DD:HH:MM:SS - if (datetime != "") { - tmElements_t tm; - tm.Year = CalendarYrToTm(_getValue(datetime, ':', 0).toInt()); // YYYY - - // 1970 - tm.Month = _getValue(datetime, ':', 1).toInt(); - tm.Day = _getValue(datetime, ':', 2).toInt(); - tm.Hour = _getValue(datetime, ':', 3).toInt(); - tm.Minute = _getValue(datetime, ':', 4).toInt(); - tm.Second = _getValue(datetime, ':', 5).toInt(); - time_t t = makeTime(tm); // make and break to calculate tm.Wday - breakTime(t, tm); - // day, weekday, month, century(1=1900, 0=2000), year(0-99) - rtc_pcf.setDate( - tm.Day, tm.Wday - 1, tm.Month, 0, - tmYearToY2k(tm.Year)); // TimeLib & DS3231 has Wday range of 1-7, but - // PCF8563 stores day of week in 0-6 range - // hr, min, sec - rtc_pcf.setTime(tm.Hour, tm.Minute, tm.Second); - } +// TODO: implement more advanced wakeup logic, i.e. > 255 seconds that are not a multiple of 60 +bool WatchyRTC::CheckWakeup() +{ + if(m_initialTimer) { + m_initialTimer = false; - // on POR event, PCF8563 sets month to 0, which will give an error since - // months are 1-12 - clearAlarm(); -} - -String WatchyRTC::_getValue(String data, char separator, int index) { - int found = 0; - int strIndex[] = {0, -1}; - int maxIndex = data.length() - 1; - - for (int i = 0; i <= maxIndex && found <= index; i++) { - if (data.charAt(i) == separator || i == maxIndex) { - found++; - strIndex[0] = strIndex[1] + 1; - strIndex[1] = (i == maxIndex) ? i + 1 : i; + byte frequency, interval; + if (UPDATE_INTERVAL % 60 == 0) { + frequency = TMR_1MIN; // 1 minute + interval = UPDATE_INTERVAL / 60; + } else { + frequency = TMR_1Hz; // 1 second + interval = UPDATE_INTERVAL; } + + rtc_pcf.setTimer(interval, frequency, true); + + return true; } - return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; -} + return true; +} \ No newline at end of file diff --git a/src/WatchyRTC.h b/src/WatchyRTC.h index 7f3ede7..59f6be3 100644 --- a/src/WatchyRTC.h +++ b/src/WatchyRTC.h @@ -15,17 +15,16 @@ public: public: WatchyRTC(); - void init(); - void config(String datetime); // String datetime format is YYYY:MM:DD:HH:MM:SS - void clearAlarm(); - void read(tmElements_t & tm, int offsetInSeconds = 0); - void set(tmElements_t tm); + void Init(); + void Get(tmElements_t & tm, int offsetInSeconds = 0); + void Set(tmElements_t tm); + void SetTimer(); + bool CheckWakeup(); // Checks to really wake up or not, also resets the timer after the initial sleep static void OffsetTime(tmElements_t & tm, int offsetInSeconds); private: - void _PCFConfig(String datetime); - String _getValue(String data, char separator, int index); + static RTC_DATA_ATTR bool m_timerSet, m_initialTimer; }; #endif \ No newline at end of file diff --git a/src/config.h b/src/config.h index e07c817..98847c1 100644 --- a/src/config.h +++ b/src/config.h @@ -30,5 +30,5 @@ #define NTP_SERVER "pool.ntp.org" -#define UPDATE_INTERVAL 1 -#define WAKE_ON_ACCEL_EVENTS false \ No newline at end of file +#define UPDATE_INTERVAL 60 // seconds +#define WAKE_ON_ACCEL_EVENTS false // useful if saving battery by not updating every minute \ No newline at end of file From 29fa5486cb33029db0758db12485704b49957ff5 Mon Sep 17 00:00:00 2001 From: Lewis Jackson <> Date: Wed, 31 May 2023 04:04:58 +0300 Subject: [PATCH 2/5] Use local bugfixed Rtc_Pcf8563 library --- lib/Rtc_Pcf8563/README.rst | 47 ++ lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp | 740 ++++++++++++++++++ lib/Rtc_Pcf8563/Rtc_Pcf8563.h | 307 ++++++++ .../examples/lcd_clock/lcd_clock.pde | 63 ++ .../examples/set_alarm/set_alarm.pde | 87 ++ .../examples/set_clock/set_clock.pde | 52 ++ lib/Rtc_Pcf8563/keywords.txt | 66 ++ lib/Rtc_Pcf8563/library.json | 16 + platformio.ini | 1 - 9 files changed, 1378 insertions(+), 1 deletion(-) create mode 100644 lib/Rtc_Pcf8563/README.rst create mode 100644 lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp create mode 100644 lib/Rtc_Pcf8563/Rtc_Pcf8563.h create mode 100644 lib/Rtc_Pcf8563/examples/lcd_clock/lcd_clock.pde create mode 100644 lib/Rtc_Pcf8563/examples/set_alarm/set_alarm.pde create mode 100644 lib/Rtc_Pcf8563/examples/set_clock/set_clock.pde create mode 100644 lib/Rtc_Pcf8563/keywords.txt create mode 100644 lib/Rtc_Pcf8563/library.json diff --git a/lib/Rtc_Pcf8563/README.rst b/lib/Rtc_Pcf8563/README.rst new file mode 100644 index 0000000..ab9acfb --- /dev/null +++ b/lib/Rtc_Pcf8563/README.rst @@ -0,0 +1,47 @@ + +This is my fixed/expanded version of the original Rtc_Pcf8563 library for Arduino. +All credits go to the original authors. + +My version differs for + +* a few more methods, mostly useful for debugging +* a single bug fix in RTCC_ALARM_AF + + +NAME +---- +Pcf8563 Real Time Clock support routines + +AUTHOR +------ +Joe Robertson, jmr +orbitalair@bellsouth.net +http://orbitalair.wikispaces.com/Arduino + +CREATION DATE +------------- +9/24/06, init - built off of usart demo. using mikroC + +NOTES +----- + +HISTORY +------- + +* 10/14/06 ported to CCS compiler, jmr +* 2/21/09 changed all return values to hex val and not bcd, jmr +* 1/10/10 ported to arduino, jmr +* 2/14/10 added 3 world date formats, jmr +* 28/02/2012 A. Pasotti + * fixed a bug in RTCC_ALARM_AF, + * added a few (not really useful) methods +* 24/01/2018 add getTimestamp method (return unix timestamp), gilou + + +TODO +---- + +Add Euro date format +Add short time (hh:mm) format +Add 24h/12h format +Add timer support \ No newline at end of file diff --git a/lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp b/lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp new file mode 100644 index 0000000..546b333 --- /dev/null +++ b/lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp @@ -0,0 +1,740 @@ +/***** + * NAME + * Pcf8563 Real Time Clock support routines + * AUTHOR + * Joe Robertson, jmr + * orbitalair@bellsouth.net + * http://orbitalair.wikispaces.com/Arduino + * CREATION DATE + * 9/24/06, init - built off of usart demo. using mikroC + * NOTES + * HISTORY + * 10/14/06 ported to CCS compiler, jmr + * 2/21/09 changed all return values to hex val and not bcd, jmr + * 1/10/10 ported to arduino, jmr + * 2/14/10 added 3 world date formats, jmr + * 28/02/2012 A. Pasotti + * fixed a bug in RTCC_ALARM_AF, + * added a few (not really useful) methods + * 22/10/2014 Fix whitespace, tabs, and newlines, cevich + * 22/10/2014 add voltLow get/set, cevich + * 22/10/2014 add century get, cevich + * 22/10/2014 Fix get/set date/time race condition, cevich + * 22/10/2014 Header/Code rearranging, alarm/timer flag masking + * extern Wire, cevich + * 26/11/2014 Add zeroClock(), initialize to lowest possible + * values, cevich + * 22/10/2014 add timer support, cevich + * + * TODO + * x Add Euro date format + * Add short time (hh:mm) format + * Add 24h/12h format + ****** + * Robodoc embedded documentation. + * http://www.xs4all.nl/~rfsber/Robo/robodoc.html + */ + +#include +#include "Rtc_Pcf8563.h" + +Rtc_Pcf8563::Rtc_Pcf8563(void) +{ + Wire.begin(); + Rtcc_Addr = RTCC_R>>1; +} + +Rtc_Pcf8563::Rtc_Pcf8563(int sdaPin, int sdlPin) +{ + Wire.begin(sdaPin, sdlPin); + Rtcc_Addr = RTCC_R>>1; +} + +/* Private internal functions, but useful to look at if you need a similar func. */ +byte Rtc_Pcf8563::decToBcd(byte val) +{ + return ( (val/10*16) + (val%10) ); +} + +byte Rtc_Pcf8563::bcdToDec(byte val) +{ + return ( (val/16*10) + (val%16) ); +} + +void Rtc_Pcf8563::zeroClock() +{ + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)0x0); // start address + + Wire.write((byte)0x0); //control/status1 + Wire.write((byte)0x0); //control/status2 + Wire.write((byte)0x00); //set seconds to 0 & VL to 0 + Wire.write((byte)0x00); //set minutes to 0 + Wire.write((byte)0x00); //set hour to 0 + Wire.write((byte)0x01); //set day to 1 + Wire.write((byte)0x00); //set weekday to 0 + Wire.write((byte)0x81); //set month to 1, century to 1900 + Wire.write((byte)0x00); //set year to 0 + Wire.write((byte)0x80); //minute alarm value reset to 00 + Wire.write((byte)0x80); //hour alarm value reset to 00 + Wire.write((byte)0x80); //day alarm value reset to 00 + Wire.write((byte)0x80); //weekday alarm value reset to 00 + Wire.write((byte)SQW_32KHZ); //set SQW to default, see: setSquareWave + Wire.write((byte)0x0); //timer off + Wire.endTransmission(); +} + +void Rtc_Pcf8563::clearStatus() +{ + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)0x0); + Wire.write((byte)0x0); //control/status1 + Wire.write((byte)0x0); //control/status2 + Wire.endTransmission(); +} + +/* +* Read status byte +*/ +byte Rtc_Pcf8563::readStatus2() +{ + getDateTime(); + return getStatus2(); +} + +void Rtc_Pcf8563::clearVoltLow(void) +{ + getDateTime(); + // Only clearing is possible on device (I tried) + setDateTime(getDay(), getWeekday(), getMonth(), + getCentury(), getYear(), getHour(), + getMinute(), getSecond()); +} + +/* +* Atomicly read all device registers in one operation +*/ +void Rtc_Pcf8563::getDateTime(void) +{ + /* Start at beginning, read entire memory in one go */ + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT1_ADDR); + Wire.endTransmission(); + + /* As per data sheet, have to read everything all in one operation */ + uint8_t readBuffer[16] = {0}; + Wire.requestFrom(Rtcc_Addr, 16); + for (uint8_t i=0; i < 16; i++) + readBuffer[i] = Wire.read(); + + // status bytes + status1 = readBuffer[0]; + status2 = readBuffer[1]; + + // time bytes + //0x7f = 0b01111111 + volt_low = readBuffer[2] & RTCC_VLSEC_MASK; //VL_Seconds + sec = bcdToDec(readBuffer[2] & ~RTCC_VLSEC_MASK); + minute = bcdToDec(readBuffer[3] & 0x7f); + //0x3f = 0b00111111 + hour = bcdToDec(readBuffer[4] & 0x3f); + + // date bytes + //0x3f = 0b00111111 + day = bcdToDec(readBuffer[5] & 0x3f); + //0x07 = 0b00000111 + weekday = bcdToDec(readBuffer[6] & 0x07); + //get raw month data byte and set month and century with it. + month = readBuffer[7]; + if (month & RTCC_CENTURY_MASK) + century = true; + else + century = false; + //0x1f = 0b00011111 + month = month & 0x1f; + month = bcdToDec(month); + year = bcdToDec(readBuffer[8]); + + // alarm bytes + alarm_minute = readBuffer[9]; + if(B10000000 & alarm_minute) + alarm_minute = RTCC_NO_ALARM; + else + alarm_minute = bcdToDec(alarm_minute & B01111111); + alarm_hour = readBuffer[10]; + if(B10000000 & alarm_hour) + alarm_hour = RTCC_NO_ALARM; + else + alarm_hour = bcdToDec(alarm_hour & B00111111); + alarm_day = readBuffer[11]; + if(B10000000 & alarm_day) + alarm_day = RTCC_NO_ALARM; + else + alarm_day = bcdToDec(alarm_day & B00111111); + alarm_weekday = readBuffer[12]; + if(B10000000 & alarm_weekday) + alarm_weekday = RTCC_NO_ALARM; + else + alarm_weekday = bcdToDec(alarm_weekday & B00000111); + + // CLKOUT_control 0x03 = 0b00000011 + squareWave = readBuffer[13] & 0x03; + + // timer bytes + timer_control = readBuffer[14] & 0x03; + timer_value = readBuffer[15]; // current value != set value when running +} + + +void Rtc_Pcf8563::setDateTime(byte day, byte weekday, byte month, + bool century, byte year, byte hour, + byte minute, byte sec) +{ + /* year val is 00 to 99, xx + with the highest bit of month = century + 0=20xx + 1=19xx + */ + month = decToBcd(month); + if (century) + month |= RTCC_CENTURY_MASK; + else + month &= ~RTCC_CENTURY_MASK; + + /* As per data sheet, have to set everything all in one operation */ + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write(RTCC_SEC_ADDR); // send addr low byte, req'd + Wire.write(decToBcd(sec) &~RTCC_VLSEC_MASK); //set sec, clear VL bit + Wire.write(decToBcd(minute)); //set minutes + Wire.write(decToBcd(hour)); //set hour + Wire.write(decToBcd(day)); //set day + Wire.write(decToBcd(weekday)); //set weekday + Wire.write(month); //set month, century to 1 + Wire.write(decToBcd(year)); //set year to 99 + Wire.endTransmission(); + // Keep values in-sync with device + getDateTime(); +} + +/** +* Get alarm, set values to RTCC_NO_ALARM (99) if alarm flag is not set +*/ +void Rtc_Pcf8563::getAlarm() +{ + getDateTime(); +} + +/* +* Returns true if AIE is on +* +*/ +bool Rtc_Pcf8563::alarmEnabled() +{ + return getStatus2() & RTCC_ALARM_AIE; +} + +/* +* Returns true if AF is on +* +*/ +bool Rtc_Pcf8563::alarmActive() +{ + return getStatus2() & RTCC_ALARM_AF; +} + +/* enable alarm interrupt + * whenever the clock matches these values an int will + * be sent out pin 3 of the Pcf8563 chip + */ +void Rtc_Pcf8563::enableAlarm() +{ + getDateTime(); // operate on current values + //set status2 AF val to zero + status2 &= ~RTCC_ALARM_AF; + //set TF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_TIMER_TF; + //enable the interrupt + status2 |= RTCC_ALARM_AIE; + + //enable the interrupt + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); +} + +/* set the alarm values + * whenever the clock matches these values an int will + * be sent out pin 3 of the Pcf8563 chip + */ +void Rtc_Pcf8563::setAlarm(byte min, byte hour, byte day, byte weekday) +{ + getDateTime(); // operate on current values + if (min <99) { + min = constrain(min, 0, 59); + min = decToBcd(min); + min &= ~RTCC_ALARM; + } else { + min = 0x0; min |= RTCC_ALARM; + } + + if (hour <99) { + hour = constrain(hour, 0, 23); + hour = decToBcd(hour); + hour &= ~RTCC_ALARM; + } else { + hour = 0x0; hour |= RTCC_ALARM; + } + + if (day <99) { + day = constrain(day, 1, 31); + day = decToBcd(day); day &= ~RTCC_ALARM; + } else { + day = 0x0; day |= RTCC_ALARM; + } + + if (weekday <99) { + weekday = constrain(weekday, 0, 6); + weekday = decToBcd(weekday); + weekday &= ~RTCC_ALARM; + } else { + weekday = 0x0; weekday |= RTCC_ALARM; + } + + alarm_hour = hour; + alarm_minute = min; + alarm_weekday = weekday; + alarm_day = day; + + // First set alarm values, then enable + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)RTCC_ALRM_MIN_ADDR); + Wire.write((byte)alarm_minute); + Wire.write((byte)alarm_hour); + Wire.write((byte)alarm_day); + Wire.write((byte)alarm_weekday); + Wire.endTransmission(); + + Rtc_Pcf8563::enableAlarm(); +} + +void Rtc_Pcf8563::clearAlarm() +{ + //set status2 AF val to zero to reset alarm + status2 &= ~RTCC_ALARM_AF; + //set TF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_TIMER_TF; + //turn off the interrupt + status2 &= ~RTCC_ALARM_AIE; + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); +} + +/** +* Reset the alarm leaving interrupt unchanged +*/ +void Rtc_Pcf8563::resetAlarm() +{ + //set status2 AF val to zero to reset alarm + status2 &= ~RTCC_ALARM_AF; + //set TF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_TIMER_TF; + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); +} + +// true if timer interrupt and control is enabled +bool Rtc_Pcf8563::timerEnabled() +{ + if (getStatus2() & RTCC_TIMER_TIE) + if (timer_control & RTCC_TIMER_TE) + return true; + return false; +} + + +// true if timer is active +bool Rtc_Pcf8563::timerActive() +{ + return getStatus2() & RTCC_TIMER_TF; +} + + +// enable timer and interrupt +void Rtc_Pcf8563::enableTimer(void) +{ + getDateTime(); + //set TE to 1 + timer_control |= RTCC_TIMER_TE; + //set status2 TF val to zero + status2 &= ~RTCC_TIMER_TF; + //set AF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_ALARM_AF; + //enable the interrupt + status2 |= RTCC_TIMER_TIE; + + // Enable interrupt first, then enable timer + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_TIMER1_ADDR); + Wire.write((byte)timer_control); // Timer starts ticking now! + Wire.endTransmission(); +} + + +// set count-down value and frequency +void Rtc_Pcf8563::setTimer(byte value, byte frequency, bool is_pulsed) +{ + getDateTime(); + if (is_pulsed) + status2 |= 0x01 << 4; + else + status2 &= ~(0x01 << 4); + timer_value = value; + // TE set to 1 in enableTimer(), leave 0 for now + timer_control |= (frequency & RTCC_TIMER_TD10); // use only last 2 bits + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_TIMER1_ADDR); + Wire.write((byte)timer_control); + Wire.write((byte)timer_value); + Wire.endTransmission(); + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); + + enableTimer(); +} + + +// clear timer flag and interrupt +void Rtc_Pcf8563::clearTimer(void) +{ + getDateTime(); + //set status2 TF val to zero + status2 &= ~RTCC_TIMER_TF; + //set AF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_ALARM_AF; + //turn off the interrupt + status2 &= ~RTCC_TIMER_TIE; + //turn off the timer + timer_control = 0; + + // Stop timer first + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_TIMER1_ADDR); + Wire.write((byte)timer_control); + Wire.endTransmission(); + + // clear flag and interrupt + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); +} + + +// clear timer flag but leave interrupt unchanged */ +void Rtc_Pcf8563::resetTimer(void) +{ + getDateTime(); + //set status2 TF val to zero to reset timer + status2 &= ~RTCC_TIMER_TF; + //set AF to 1 masks it from changing, as per data-sheet + status2 |= RTCC_ALARM_AF; + + Wire.beginTransmission(Rtcc_Addr); + Wire.write((byte)RTCC_STAT2_ADDR); + Wire.write((byte)status2); + Wire.endTransmission(); +} + +/** +* Set the square wave pin output +*/ +void Rtc_Pcf8563::setSquareWave(byte frequency) +{ + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)RTCC_SQW_ADDR); + Wire.write((byte)frequency); + Wire.endTransmission(); +} + +void Rtc_Pcf8563::clearSquareWave() +{ + Rtc_Pcf8563::setSquareWave(SQW_DISABLE); +} + +const char *Rtc_Pcf8563::formatTime(byte style) +{ + getTime(); + switch (style) { + case RTCC_TIME_HM: + strOut[0] = '0' + (hour / 10); + strOut[1] = '0' + (hour % 10); + strOut[2] = ':'; + strOut[3] = '0' + (minute / 10); + strOut[4] = '0' + (minute % 10); + strOut[5] = '\0'; + break; + case RTCC_TIME_HMS: + default: + strOut[0] = '0' + (hour / 10); + strOut[1] = '0' + (hour % 10); + strOut[2] = ':'; + strOut[3] = '0' + (minute / 10); + strOut[4] = '0' + (minute % 10); + strOut[5] = ':'; + strOut[6] = '0' + (sec / 10); + strOut[7] = '0' + (sec % 10); + strOut[8] = '\0'; + break; + } + return strOut; +} + + +const char *Rtc_Pcf8563::formatDate(byte style) +{ + getDate(); + + switch (style) { + + case RTCC_DATE_ASIA: + //do the asian style, yyyy-mm-dd + if (century ){ + strDate[0] = '1'; + strDate[1] = '9'; + } + else { + strDate[0] = '2'; + strDate[1] = '0'; + } + strDate[2] = '0' + (year / 10 ); + strDate[3] = '0' + (year % 10); + strDate[4] = '-'; + strDate[5] = '0' + (month / 10); + strDate[6] = '0' + (month % 10); + strDate[7] = '-'; + strDate[8] = '0' + (day / 10); + strDate[9] = '0' + (day % 10); + strDate[10] = '\0'; + break; + case RTCC_DATE_US: + //the pitiful US style, mm/dd/yyyy + strDate[0] = '0' + (month / 10); + strDate[1] = '0' + (month % 10); + strDate[2] = '/'; + strDate[3] = '0' + (day / 10); + strDate[4] = '0' + (day % 10); + strDate[5] = '/'; + if (century){ + strDate[6] = '1'; + strDate[7] = '9'; + } + else { + strDate[6] = '2'; + strDate[7] = '0'; + } + strDate[8] = '0' + (year / 10 ); + strDate[9] = '0' + (year % 10); + strDate[10] = '\0'; + break; + case RTCC_DATE_WORLD: + default: + //do the world style, dd-mm-yyyy + strDate[0] = '0' + (day / 10); + strDate[1] = '0' + (day % 10); + strDate[2] = '-'; + strDate[3] = '0' + (month / 10); + strDate[4] = '0' + (month % 10); + strDate[5] = '-'; + + if (century){ + strDate[6] = '1'; + strDate[7] = '9'; + } + else { + strDate[6] = '2'; + strDate[7] = '0'; + } + strDate[8] = '0' + (year / 10 ); + strDate[9] = '0' + (year % 10); + strDate[10] = '\0'; + break; + + } + return strDate; +} + +void Rtc_Pcf8563::initClock() +{ + Wire.beginTransmission(Rtcc_Addr); // Issue I2C start signal + Wire.write((byte)0x0); // start address + + Wire.write((byte)0x0); //control/status1 + Wire.write((byte)0x0); //control/status2 + Wire.write((byte)0x81); //set seconds & VL + Wire.write((byte)0x01); //set minutes + Wire.write((byte)0x01); //set hour + Wire.write((byte)0x01); //set day + Wire.write((byte)0x01); //set weekday + Wire.write((byte)0x01); //set month, century to 1 + Wire.write((byte)0x01); //set year to 99 + Wire.write((byte)0x80); //minute alarm value reset to 00 + Wire.write((byte)0x80); //hour alarm value reset to 00 + Wire.write((byte)0x80); //day alarm value reset to 00 + Wire.write((byte)0x80); //weekday alarm value reset to 00 + Wire.write((byte)0x0); //set SQW, see: setSquareWave + Wire.write((byte)0x0); //timer off + Wire.endTransmission(); +} + +void Rtc_Pcf8563::setTime(byte hour, byte minute, byte sec) +{ + getDateTime(); + setDateTime(getDay(), getWeekday(), getMonth(), + getCentury(), getYear(), hour, minute, sec); +} + +void Rtc_Pcf8563::setDate(byte day, byte weekday, byte month, bool century, byte year) +{ + getDateTime(); + setDateTime(day, weekday, month, century, year, + getHour(), getMinute(), getSecond()); +} + +void Rtc_Pcf8563::getDate() +{ + getDateTime(); +} + +void Rtc_Pcf8563::getTime() +{ + getDateTime(); +} + +bool Rtc_Pcf8563::getVoltLow(void) +{ + return volt_low; +} + +byte Rtc_Pcf8563::getSecond() { + return sec; +} + +byte Rtc_Pcf8563::getMinute() { + return minute; +} + +byte Rtc_Pcf8563::getHour() { + return hour; +} + +byte Rtc_Pcf8563::getAlarmMinute() { + return alarm_minute; +} + +byte Rtc_Pcf8563::getAlarmHour() { + return alarm_hour; +} + +byte Rtc_Pcf8563::getAlarmDay() { + return alarm_day; +} + +byte Rtc_Pcf8563::getAlarmWeekday() { + return alarm_weekday; +} + +byte Rtc_Pcf8563::getTimerControl() { + return timer_control; +} + +byte Rtc_Pcf8563::getTimerValue() { + // Impossible to freeze this value, it could + // be changing during read. Multiple reads + // required to check for consistency. + uint8_t last_value; + do { + last_value = timer_value; + getDateTime(); + } while (timer_value != last_value); + return timer_value; +} + +byte Rtc_Pcf8563::getDay() { + return day; +} + +byte Rtc_Pcf8563::getMonth() { + return month; +} + +byte Rtc_Pcf8563::getYear() { + return year; +} + +bool Rtc_Pcf8563::getCentury() { + return century; +} + +byte Rtc_Pcf8563::getWeekday() { + return weekday; +} + +byte Rtc_Pcf8563::getStatus1() { + return status1; +} + +byte Rtc_Pcf8563::getStatus2() { + return status2; +} + +unsigned long Rtc_Pcf8563::getTimestamp(){ + getDateTime(); // update date and time + unsigned long timestamp = 0; + + // Convert years in days + timestamp = (year-epoch_year)*365; // convert years in days + + if((year-epoch_year)>1) // add a dy when it's a leap year + { + for(unsigned char i = epoch_year; i2&&isLeapYear(century, year)) timestamp++; // test for the year's febuary + + // add months converted in days + if(month>1) timestamp += months_days[month-2]; + + // add days + timestamp += (day-epoch_day); + + timestamp*= 86400; // convert days in seconds + + // convert time to second and add it to timestamp + unsigned long timeTemp = hour*60+ minute; + timeTemp *=60; + timeTemp += sec; + + timestamp += timeTemp; // add hours +minutes + seconds + + timestamp += EPOCH_TIMESTAMP; // add epoch reference + + return timestamp; +} diff --git a/lib/Rtc_Pcf8563/Rtc_Pcf8563.h b/lib/Rtc_Pcf8563/Rtc_Pcf8563.h new file mode 100644 index 0000000..3096030 --- /dev/null +++ b/lib/Rtc_Pcf8563/Rtc_Pcf8563.h @@ -0,0 +1,307 @@ +/***** + * NAME + * Pcf8563 Real Time Clock support routines + * AUTHOR + * Joe Robertson, jmr + * orbitalair@bellsouth.net + * http://orbitalair.wikispaces.com/Arduino + * CREATION DATE + * 9/24/06, init - built off of usart demo. using mikroC + * NOTES + * HISTORY + * 10/14/06 ported to CCS compiler, jmr + * 2/21/09 changed all return values to hex val and not bcd, jmr + * 1/10/10 ported to arduino, jmr + * 2/14/10 added 3 world date formats, jmr + * 28/02/2012 A. Pasotti + * fixed a bug in RTCC_ALARM_AF, + * added a few (not really useful) methods + * 22/10/2014 Fix whitespace, tabs, and newlines, cevich + * 22/10/2014 add voltLow get/set, cevich + * 22/10/2014 add century get, cevich + * 22/10/2014 Fix get/set date/time race condition, cevich + * 22/10/2014 Header/Code rearranging, alarm/timer flag masking, + * extern Wire, cevich + * 26/11/2014 Add zeroClock(), initialize to lowest possible + * values, cevich + * 22/10/2014 add timer support, cevich + * 24/01/2018 add getTimestamp, gilou + * + * TODO + * x Add Euro date format + * Add short time (hh:mm) format + * Add 24h/12h format + ****** + * Robodoc embedded documentation. + * http://www.xs4all.nl/~rfsber/Robo/robodoc.html + */ + +#ifndef Rtc_Pcf8563_H +#define Rtc_Pcf8563_H + +/* the read and write values for pcf8563 rtcc */ +/* these are adjusted for arduino */ +#define RTCC_R 0xa3 +#define RTCC_W 0xa2 + +#define RTCC_SEC 1 +#define RTCC_MIN 2 +#define RTCC_HR 3 +#define RTCC_DAY 4 +#define RTCC_WEEKDAY 5 +#define RTCC_MONTH 6 +#define RTCC_YEAR 7 +#define RTCC_CENTURY 8 + +/* register addresses in the rtc */ +#define RTCC_STAT1_ADDR 0x0 +#define RTCC_STAT2_ADDR 0x01 +#define RTCC_SEC_ADDR 0x02 +#define RTCC_MIN_ADDR 0x03 +#define RTCC_HR_ADDR 0x04 +#define RTCC_DAY_ADDR 0x05 +#define RTCC_WEEKDAY_ADDR 0x06 +#define RTCC_MONTH_ADDR 0x07 +#define RTCC_YEAR_ADDR 0x08 +#define RTCC_ALRM_MIN_ADDR 0x09 +#define RTCC_SQW_ADDR 0x0D +#define RTCC_TIMER1_ADDR 0x0E +#define RTCC_TIMER2_ADDR 0x0F + +/* setting the alarm flag to 0 enables the alarm. + * set it to 1 to disable the alarm for that value. + */ +#define RTCC_ALARM 0x80 +#define RTCC_ALARM_AIE 0x02 +#define RTCC_ALARM_AF 0x08 +/* optional val for no alarm setting */ +#define RTCC_NO_ALARM 99 + +#define RTCC_TIMER_TIE 0x01 // Timer Interrupt Enable + +#define RTCC_TIMER_TF 0x04 // Timer Flag, read/write active state + // When clearing, be sure to set RTCC_TIMER_AF + // to 1 (see note above). +#define RTCC_TIMER_TI_TP 0x10 // 0: INT is active when TF is active + // (subject to the status of TIE) + // 1: INT pulses active + // (subject to the status of TIE); + // Note: TF stays active until cleared + // no matter what RTCC_TIMER_TI_TP is. +#define RTCC_TIMER_TD10 0x03 // Timer source clock, TMR_1MIN saves power +#define RTCC_TIMER_TE 0x80 // Timer 1:enable/0:disable + +/* Timer source-clock frequency constants */ +#define TMR_4096HZ B00000000 +#define TMR_64Hz B00000001 +#define TMR_1Hz B00000010 +#define TMR_1MIN B00000011 + +#define RTCC_CENTURY_MASK 0x80 +#define RTCC_VLSEC_MASK 0x80 + +/* date format flags */ +#define RTCC_DATE_WORLD 0x01 +#define RTCC_DATE_ASIA 0x02 +#define RTCC_DATE_US 0x04 +/* time format flags */ +#define RTCC_TIME_HMS 0x01 +#define RTCC_TIME_HM 0x02 + +/* square wave constants */ +#define SQW_DISABLE B00000000 +#define SQW_32KHZ B10000000 +#define SQW_1024HZ B10000001 +#define SQW_32HZ B10000010 +#define SQW_1HZ B10000011 + +/* epoch timestamp constants : 01/01/2016 à 00:00:00 : 1451599200 */ +#define epoch_day 1 +#define epoch_month 1 +#define epoch_year 16 +#define EPOCH_TIMESTAMP 1451606400 +const unsigned int months_days[]={31,59,90,120,151,181,212,243,273,304,334}; // days count for each month + +#include +#include + +extern TwoWire Wire; + +/* arduino class */ +class Rtc_Pcf8563 { + public: + Rtc_Pcf8563(); + Rtc_Pcf8563(int, int); /* Construct using different pins for sda & sdl */ + + void zeroClock(); /* Zero date/time, alarm / timer, default clkout */ + void clearStatus(); /* set both status bytes to zero */ + byte readStatus2(); + void clearVoltLow(void); /* Only clearing is possible */ + + void getDateTime(); /* get date and time vals to local vars */ + void setDateTime(byte day, byte weekday, byte month, bool century, byte year, + byte hour, byte minute, byte sec); + void getAlarm(); // same as getDateTime + bool alarmEnabled(); // true if alarm interrupt is enabled + bool alarmActive(); // true if alarm is active (going off) + void enableAlarm(); /* activate alarm flag and interrupt */ + /* set alarm vals, 99=ignore */ + void setAlarm(byte min, byte hour, byte day, byte weekday); + void clearAlarm(); /* clear alarm flag and interrupt */ + void resetAlarm(); /* clear alarm flag but leave interrupt unchanged */ + + bool timerEnabled(); // true if timer and interrupt is enabled + bool timerActive(); // true if timer is active (going off) + void enableTimer(void); // activate timer flag and interrupt + void setTimer(byte value, byte frequency, bool is_pulsed); // set value & frequency + void clearTimer(void); // clear timer flag, and interrupt, leave value unchanged + void resetTimer(void); // same as clearTimer() but leave interrupt unchanged */ + + void setSquareWave(byte frequency); + void clearSquareWave(); + + /* get a output string, these call getTime/getDate for latest vals */ + const char *formatTime(byte style=RTCC_TIME_HMS); + /* date supports 3 styles as listed in the wikipedia page about world date/time. */ + const char *formatDate(byte style=RTCC_DATE_US); + + /* Return leap-days between start (inclusive) and end (exclusive) */ + int leapDaysBetween(byte century_start, byte year_start, + byte century_end, byte year_end) const; + /* Return True if century (1: 1900, 0:2000) + decade is a leap year. */ + bool isLeapYear(byte century, int year) const; + /* Return number of days in any month of any decade of any year */ + byte daysInMonth(byte century, byte year, byte month) const; + /* Return the number of days since the beginning of a particular year*/ + byte daysInYear(byte century, byte year, byte month, byte day) const; + /* Return the weekday for any date after 1900 */ + byte whatWeekday(byte day, byte month, byte century, int year) const; + + bool getVoltLow(); + byte getSecond(); + byte getMinute(); + byte getHour(); + byte getDay(); + byte getMonth(); + byte getYear(); + bool getCentury(); + byte getWeekday(); + byte getStatus1(); + byte getStatus2(); + + byte getAlarmMinute(); + byte getAlarmHour(); + byte getAlarmDay(); + byte getAlarmWeekday(); + + byte getTimerControl(); + byte getTimerValue(); + + unsigned long getTimestamp(); // return unix timestamp + + // Sets date/time to static fixed values, disable all alarms + // use zeroClock() above to guarantee lowest possible values instead. + void initClock(); + // Slightly unsafe, don't use for new code, use above instead! + void setTime(byte hour, byte minute, byte sec); + void getTime(); // unsafe, don't use + void setDate(byte day, byte weekday, byte month, bool century, byte year); + void getDate(); // unsafe, don't use + + private: + /* methods */ + byte decToBcd(byte value); + byte bcdToDec(byte value); + /* time variables */ + byte hour; + byte minute; + bool volt_low; + byte sec; + byte day; + byte weekday; + byte month; + byte year; + /* alarm */ + byte alarm_hour; + byte alarm_minute; + byte alarm_weekday; + byte alarm_day; + /* CLKOUT */ + byte squareWave; + /* timer */ + byte timer_control; + byte timer_value; + /* support */ + byte status1; + byte status2; + bool century; + + char strOut[9]; + char strDate[11]; + + int Rtcc_Addr; +}; + + +inline int Rtc_Pcf8563::leapDaysBetween(byte century_start, byte year_start, + byte century_end, byte year_end) const { + // Credit: Victor Haydin via stackoverflow.com + int span_start = 2000 - (century_start * 100) + year_start; + int span_end = 2000 - (century_end * 100) + year_end - 1; // less year_end + // Subtract leap-years before span_start, from leap-years before span_end + return ((span_end / 4) - (span_end / 100) + (span_end / 400)) - + ((span_start / 4) - (span_start / 100) + (span_start / 400)); +} + + +inline bool Rtc_Pcf8563::isLeapYear(byte century, int year) const +{ + year = 2000 - (century * 100) + year; + if ((year % 4) != 0) + return false; + else if ((year % 100) != 0) + return true; + else if ((year % 400) != 0) + return false; + else + return true; +} + + +inline byte Rtc_Pcf8563::daysInMonth(byte century, + byte year, + byte month) const +{ + const int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + byte dim = days[month]; + if (month == 2 && isLeapYear(century, year)) + dim += 1; + return dim; +} + + +inline byte Rtc_Pcf8563::daysInYear(byte century, + byte year, + byte month, + byte day) const +{ + const int days[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + byte total = days[month - 1] + day; + if ((month > 2) and isLeapYear(century, year)) + total += 1; + return total; +} + + +inline byte Rtc_Pcf8563::whatWeekday(byte day, byte month, + byte century, int year) const +{ + year = 2000 - (century * 100) + year; + // Credit: Tomohiko Sakamoto + // http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week + year -= month < 3; + static int trans[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; + return (year + year / 4 - year / 100 + year / 400 + + trans[month - 1] + day) % 7; +} +#endif diff --git a/lib/Rtc_Pcf8563/examples/lcd_clock/lcd_clock.pde b/lib/Rtc_Pcf8563/examples/lcd_clock/lcd_clock.pde new file mode 100644 index 0000000..5299367 --- /dev/null +++ b/lib/Rtc_Pcf8563/examples/lcd_clock/lcd_clock.pde @@ -0,0 +1,63 @@ +/* Demonstration of Rtc_Pcf8563 Clock on LCD. + * + * I used a RBBB with Arduino IDE, the pins are mapped a + * bit differently. Change for your hw. + * SCK - A5, SDA - A4, INT - D3/INT1 + * + * This sketch connects a lcd to display the clock output. + * + * setup: see Pcf8563 data sheet. + * 1x 10Kohm pullup on Pin3 INT + * No pullups on Pin5 or Pin6 (I2C internals used) + * 1x 0.1pf on power + * 1x 32khz chrystal + * 1x h44780 LCD + * + * Joe Robertson, jmr + * orbitalair@bellsouth.net + */ + +#include +#include +/* add the lcd support */ +#include + +//init the real time clock +Rtc_Pcf8563 rtc; + +/* initialize the library objects */ +/* LiquidCrystal lcd(rs, en, d4, d5, d6, d7); */ +LiquidCrystal lcd(4 ,9 ,5 ,6 ,7 ,8); + +void setup() +{ + // set up the LCD's number of rows and columns: + lcd.begin(16, 2); + + //clear out all the registers + rtc.initClock(); + //set a time to start with. + //day, weekday, month, century, year + rtc.setDate(14, 6, 3, 0, 10); + //hr, min, sec + rtc.setTime(1, 15, 40); +} + +void loop() +{ + // set the cursor to column 0, line 1 + // (note: line 1 is the second row, since counting begins with 0): + lcd.setCursor(0, 0); + //lcd.print(rtc.formatTime(RTCC_TIME_HM)); + lcd.print(rtc.formatTime()); + lcd.setCursor(0, 1); + //lcd.print(rtc.formatDate(RTCC_DATE_ASIA)); + lcd.print(rtc.formatDate()); + + delay(1000); + +} + + + + diff --git a/lib/Rtc_Pcf8563/examples/set_alarm/set_alarm.pde b/lib/Rtc_Pcf8563/examples/set_alarm/set_alarm.pde new file mode 100644 index 0000000..a0bc0b8 --- /dev/null +++ b/lib/Rtc_Pcf8563/examples/set_alarm/set_alarm.pde @@ -0,0 +1,87 @@ +/* Demonstration of Rtc_Pcf8563 Alarms. + * + * The Pcf8563 has an interrupt output, Pin3. + * Pull Pin3 HIGH with a resistor, I used a 10kohm to 5v. + * I used a RBBB with Arduino IDE, the pins are mapped a + * bit differently. Change for your hw. + * SCK - A5, SDA - A4, INT - D3/INT1 + * + * After loading and starting the sketch, use the serial monitor + * to see the clock output. + * + * setup: see Pcf8563 data sheet. + * 1x 10Kohm pullup on Pin3 INT + * No pullups on Pin5 or Pin6 (I2C internals used) + * 1x 0.1pf on power + * 1x 32khz chrystal + * + * Joe Robertson, jmr + * orbitalair@bellsouth.net + */ +#include +#include + +/* get a real time clock object */ +Rtc_Pcf8563 rtc; + +/* a flag for the interrupt */ +volatile int alarm_flag=0; + +/* the interrupt service routine */ +void blink() +{ + alarm_flag=1; +} + +void setup() +{ + pinMode(3, INPUT); // set pin to input + digitalWrite(3, HIGH); // turn on pullup resistors + + Serial.begin(9600); + + /* setup int on pin 3 of arduino */ + attachInterrupt(1, blink, FALLING); + /* clear out all the registers */ + rtc.initClock(); + /* set a time to start with. + * day, weekday, month, century, year */ + rtc.setDate(14, 6, 3, 0, 10); + /* hr, min, sec */ + rtc.setTime(1, 15, 40); + /* set an alarm for 20 secs later... + * alarm pin goes low when match occurs + * this triggers the interrupt routine + * min, hr, day, weekday + * 99 = no alarm value to be set + */ + rtc.setAlarm(16, 99, 99, 99); +} + +void loop() +{ + /* each sec update the display */ + Serial.print(rtc.formatTime()); + Serial.print(" "); + Serial.print(rtc.formatDate()); + Serial.print(" 0x"); + Serial.print(rtc.getStatus2(), HEX); + Serial.print("\r\n"); + delay(1000); + if (alarm_flag==1){ + clr_alarm(); + } + +} + +void clr_alarm() +{ + detachInterrupt(1); + Serial.print("blink!\r\n"); + + rtc.clearAlarm(); + delay(1000); + alarm_flag=0; + attachInterrupt(1, blink, FALLING); +} + diff --git a/lib/Rtc_Pcf8563/examples/set_clock/set_clock.pde b/lib/Rtc_Pcf8563/examples/set_clock/set_clock.pde new file mode 100644 index 0000000..7b7b451 --- /dev/null +++ b/lib/Rtc_Pcf8563/examples/set_clock/set_clock.pde @@ -0,0 +1,52 @@ +/* Demonstration of Rtc_Pcf8563 Set Time. + * Set the clock to a time then loop over reading time and + * output the time and date to the serial console. + * + * I used a RBBB with Arduino IDE, the pins are mapped a + * bit differently. Change for your hw + * SCK - A5, SDA - A4, INT - D3/INT1 + * + * After loading and starting the sketch, use the serial monitor + * to see the clock output. + * + * setup: see Pcf8563 data sheet. + * 1x 10Kohm pullup on Pin3 INT + * No pullups on Pin5 or Pin6 (I2C internals used) + * 1x 0.1pf on power + * 1x 32khz chrystal + * + * Joe Robertson, jmr + * orbitalair@bellsouth.net + */ +#include +#include + +//init the real time clock +Rtc_Pcf8563 rtc; + +void setup() +{ + //clear out the registers + rtc.initClock(); + //set a time to start with. + //day, weekday, month, century(1=1900, 0=2000), year(0-99) + rtc.setDate(14, 6, 3, 1, 10); + //hr, min, sec + rtc.setTime(1, 15, 0); + Serial.begin(9600); +} + +void loop() +{ + //both format functions call the internal getTime() so that the + //formatted strings are at the current time/date. + Serial.print(rtc.formatTime()); + Serial.print("\r\n"); + Serial.print(rtc.formatDate()); + Serial.print("\r\n"); + delay(1000); +} + + + + diff --git a/lib/Rtc_Pcf8563/keywords.txt b/lib/Rtc_Pcf8563/keywords.txt new file mode 100644 index 0000000..95328e5 --- /dev/null +++ b/lib/Rtc_Pcf8563/keywords.txt @@ -0,0 +1,66 @@ +####################################### +# Syntax Coloring Map For Rtc_Pcf8563 +####################################### +# Datatypes (KEYWORD1) +####################################### + +Rtc_Pcf8563 KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +zeroClock KEYWORD2 +initClock KEYWORD2 +clearStatus KEYWORD2 +getDateTime KEYWORD2 +getDate KEYWORD2 +setDateTime KEYWORD2 +setDate KEYWORD2 +getTime KEYWORD2 +setTime KEYWORD2 +setAlarm KEYWORD2 +clearAlarm KEYWORD2 +getStatus1 KEYWORD2 +getStatus2 KEYWORD2 +getVoltLow KEYWORD2 +getSecond KEYWORD2 +getMinute KEYWORD2 +getHour KEYWORD2 +getDay KEYWORD2 +getWeekDay KEYWORD2 +getMonth KEYWORD2 +getYear KEYWORD2 +getCentury KEYWORD2 +formatTime KEYWORD2 +formatDate KEYWORD2 +timerEnabled KEYWORD2 +timerActive KEYWORD2 +enableTImer KEYWORD2 +setTimer KEYWORD2 +clearTimer KEYWORD2 +resetTimer KEYWORD2 +getTimerControl KEYWORD2 +getTimerValue KEYWORD2 + + +####################################### +# Constants (LITERAL1) +####################################### + +RTCC_DATE_WORLD LITERAL1 +RTCC_DATE_ASIA LITERAL1 +RTCC_DATE_US LITERAL1 +RTCC_TIME_HMS LITERAL1 +RTCC_TIME_HM LITERAL1 + +SQW_DISABLE LITERAL1 +SQW_32KHZ LITERAL1 +SQW_1024HZ LITERAL1 +SQW_32HZ LITERAL1 +SQW_1HZ LITERAL1 + +TMR_4096HZ LITERAL1 +TMR_64Hz LITERAL1 +TMR_1Hz LITERAL1 +TMR_1MIN LITERAL1 diff --git a/lib/Rtc_Pcf8563/library.json b/lib/Rtc_Pcf8563/library.json new file mode 100644 index 0000000..3e590a0 --- /dev/null +++ b/lib/Rtc_Pcf8563/library.json @@ -0,0 +1,16 @@ +{ + "name": "Rtc_Pcf8563", + "keywords": "rtc, clock, time, calendar", + "description": "Real-Time Clock (RTC) and calendar optimized for low power consumption", + "repository": + { + "type": "git", + "url": "https://github.com/elpaso/Rtc_Pcf8563.git" + }, + "authors": { + "name": "Joe Robertson", + "url": "http://orbitalair.wikispaces.com/Arduino" + }, + "frameworks": "arduino", + "platforms": "atmelavr" +} diff --git a/platformio.ini b/platformio.ini index 76172bd..8b9076c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,7 +16,6 @@ lib_deps = adafruit/Adafruit GFX Library@^1.11.5 GxEPD2 Time - Rtc_Pcf8563 NTPClient lib_ldf_mode = deep+ From 3ada2fb9a998c705af12730155bfa67c09d6ef95 Mon Sep 17 00:00:00 2001 From: Lewis Jackson <> Date: Wed, 31 May 2023 04:21:19 +0300 Subject: [PATCH 3/5] Need to init clock for timer to work properly --- src/WatchyRTC.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/WatchyRTC.cpp b/src/WatchyRTC.cpp index 55d25a4..e96812c 100644 --- a/src/WatchyRTC.cpp +++ b/src/WatchyRTC.cpp @@ -13,6 +13,10 @@ WatchyRTC::WatchyRTC() void WatchyRTC::Init() { + tmElements_t tm; + Get(tm, 0); + rtc_pcf.initClock(); + Set(tm); } void WatchyRTC::Get(tmElements_t & tm, int offsetInSeconds) From 5367843ac10d9d0d0409b365d99bf01ca8b7b077 Mon Sep 17 00:00:00 2001 From: Lewis Jackson <> Date: Wed, 31 May 2023 21:20:55 +0300 Subject: [PATCH 4/5] Experimental code to allow timer resyncing after NTP resync --- src/WatchFace.cpp | 1 + src/WatchyRTC.cpp | 43 +++++++++++++++++++++++++++++++------------ src/WatchyRTC.h | 1 + 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/WatchFace.cpp b/src/WatchFace.cpp index 46f840b..ed31646 100644 --- a/src/WatchFace.cpp +++ b/src/WatchFace.cpp @@ -269,6 +269,7 @@ void WatchFace::MenuNTPSyncSelected() ConnectWiFi(); SyncNTPTime(); DisconnectWiFi(); + m_RTC.Resync(); if (m_inMenu) { m_inMenu = false; diff --git a/src/WatchyRTC.cpp b/src/WatchyRTC.cpp index e96812c..8fee1b3 100644 --- a/src/WatchyRTC.cpp +++ b/src/WatchyRTC.cpp @@ -13,10 +13,6 @@ WatchyRTC::WatchyRTC() void WatchyRTC::Init() { - tmElements_t tm; - Get(tm, 0); - rtc_pcf.initClock(); - Set(tm); } void WatchyRTC::Get(tmElements_t & tm, int offsetInSeconds) @@ -43,14 +39,7 @@ void WatchyRTC::Set(tmElements_t tm) void WatchyRTC::SetTimer() { if (!m_timerSet) { - rtc_pcf.getDateTime(); - // Sleep just long enough to get to a multiple of the update interval, makes updates happen on exact turn of the minute - int seconds = UPDATE_INTERVAL - (rtc_pcf.getSecond() % UPDATE_INTERVAL); - - // Non repeating - rtc_pcf.setTimer(seconds, TMR_1Hz, false); - m_timerSet = true; - m_initialTimer = true; + Resync(); } } @@ -133,10 +122,40 @@ bool WatchyRTC::CheckWakeup() interval = UPDATE_INTERVAL; } + // Timer doesn't work reliably unless it's cleared first + rtc_pcf.clearTimer(); rtc_pcf.setTimer(interval, frequency, true); return true; } return true; +} + +void WatchyRTC::Resync() +{ + rtc_pcf.getDateTime(); + // Sleep just long enough to get to a multiple of the update interval, makes updates happen on exact turn of the minute + int seconds = UPDATE_INTERVAL - (rtc_pcf.getSecond() % UPDATE_INTERVAL); + + if (seconds < 0) { + seconds = 0; + } + + // Timer doesn't work reliably unless it's cleared first + rtc_pcf.clearTimer(); + + if (seconds == 0) { + // If there's no time to wait, just call CheckWakeup() immediately and it'll set the repeating timer + m_timerSet = true; + m_initialTimer = true; + CheckWakeup(); + } else { + rtc_pcf.setTimer(seconds, TMR_1Hz, false); + + m_timerSet = true; + m_initialTimer = true; + } + + seconds = 0; } \ No newline at end of file diff --git a/src/WatchyRTC.h b/src/WatchyRTC.h index 59f6be3..c745929 100644 --- a/src/WatchyRTC.h +++ b/src/WatchyRTC.h @@ -20,6 +20,7 @@ public: void Set(tmElements_t tm); void SetTimer(); bool CheckWakeup(); // Checks to really wake up or not, also resets the timer after the initial sleep + void Resync(); // Resync the timer cycle, both initially and after RTC resync static void OffsetTime(tmElements_t & tm, int offsetInSeconds); From 1b843ce4b452f275476b2ea6d4203657b04fd28b Mon Sep 17 00:00:00 2001 From: Lewis Jackson <> Date: Wed, 31 May 2023 22:47:18 +0300 Subject: [PATCH 5/5] Update workflow to use preprepared docker image Clean up YML Specify path for pio explicitly --- .forgejo/workflows/compile.yml | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/.forgejo/workflows/compile.yml b/.forgejo/workflows/compile.yml index 555140d..31b3338 100644 --- a/.forgejo/workflows/compile.yml +++ b/.forgejo/workflows/compile.yml @@ -5,17 +5,10 @@ jobs: Compile: runs-on: ubuntu-latest steps: - - name: Setup PlatformIO + - name: Checkout code + uses: actions/checkout@v3 + + - name: Compile run: | - apt update - apt -y install curl git python3 python3-pip - python3 -m pip install --upgrade pip - pip3 install -U platformio - pio upgrade --dev - - name: Setup node - run: | - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - - apt-get install -y nodejs - - uses: actions/checkout@v3 - - run: cd ${{ github.workspace }} - - run: pio run + cd ${{ github.workspace }} + /root/.platformio/penv/bin/pio run