Merge branch 'feature/experimental-rtc-modifications'

This commit is contained in:
Lewis Jackson 2023-06-01 15:18:24 +03:00
commit f564f8c6e8
14 changed files with 1474 additions and 90 deletions

View file

@ -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

View file

@ -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 <Arduino.h>
#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; i<year;i++)
{
if(isLeapYear(century, i)) timestamp++; // add a day for each leap years
}
}
if(month>2&&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;
}

View file

@ -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 <Arduino.h>
#include <Wire.h>
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

View file

@ -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 <Wire.h>
#include <Rtc_Pcf8563.h>
/* add the lcd support */
#include <LiquidCrystal.h>
//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);
}

View file

@ -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 <Wire.h>
#include <Rtc_Pcf8563.h>
/* 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);
}

View file

@ -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 <Wire.h>
#include <Rtc_Pcf8563.h>
//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);
}

View file

@ -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

View file

@ -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"
}

View file

@ -16,7 +16,6 @@ lib_deps =
adafruit/Adafruit GFX Library@^1.11.5 adafruit/Adafruit GFX Library@^1.11.5
GxEPD2 GxEPD2
Time Time
Rtc_Pcf8563
NTPClient NTPClient
lib_ldf_mode = deep+ lib_ldf_mode = deep+

View file

@ -92,7 +92,7 @@ void WatchFace::DrawWatchFace(bool partialRefresh)
DrawBatteryIcon(); DrawBatteryIcon();
tmElements_t currentTime; tmElements_t currentTime;
m_RTC.read(currentTime, m_tzOffset); m_RTC.Get(currentTime, m_tzOffset);
SevenSegment sevenSegment(30, 60, 6, 5, 5); SevenSegment sevenSegment(30, 60, 6, 5, 5);
if (currentTime.Hour < 10) { if (currentTime.Hour < 10) {
@ -269,6 +269,7 @@ void WatchFace::MenuNTPSyncSelected()
ConnectWiFi(); ConnectWiFi();
SyncNTPTime(); SyncNTPTime();
DisconnectWiFi(); DisconnectWiFi();
m_RTC.Resync();
if (m_inMenu) { if (m_inMenu) {
m_inMenu = false; m_inMenu = false;

View file

@ -17,9 +17,13 @@ void Watchy::Init()
esp_sleep_wakeup_cause_t wakeup_reason; esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause(); wakeup_reason = esp_sleep_get_wakeup_cause();
Wire.begin(SDA, SCL); if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT0) {
m_RTC.init(); if(!m_RTC.CheckWakeup()) {
DeepSleep();
}
}
Wire.begin(SDA, SCL);
m_display.epd2.selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0)); m_display.epd2.selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0));
m_display.init(0, g_displayFullInit, 10, true); m_display.init(0, g_displayFullInit, 10, true);
SetBusyCallback(); SetBusyCallback();
@ -63,6 +67,7 @@ void Watchy::Init()
default: default:
{ {
BmaConfig(); BmaConfig();
m_RTC.Init();
ConnectWiFi(); ConnectWiFi();
SyncNTPTime(); SyncNTPTime();
DisconnectWiFi(); DisconnectWiFi();
@ -83,7 +88,6 @@ void Watchy::DeepSleep()
} }
g_displayFullInit = false; // Notify not to init it again 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 // Set GPIOs 0-39 to input to avoid power leaking out
const uint64_t ignore = 0b11110001000000110000100111000010; // Ignore some GPIOs due to resets const uint64_t ignore = 0b11110001000000110000100111000010; // Ignore some GPIOs due to resets
@ -97,6 +101,9 @@ void Watchy::DeepSleep()
esp_sleep_enable_ext1_wakeup( esp_sleep_enable_ext1_wakeup(
BTN_PIN_MASK | ACC_INT_MASK, BTN_PIN_MASK | ACC_INT_MASK,
ESP_EXT1_WAKEUP_ANY_HIGH); ESP_EXT1_WAKEUP_ANY_HIGH);
m_RTC.SetTimer();
esp_deep_sleep_start(); esp_deep_sleep_start();
} }
@ -158,7 +165,7 @@ void Watchy::SyncNTPTime()
if (success) { if (success) {
tmElements_t tm; tmElements_t tm;
breakTime((time_t)timeClient.getEpochTime(), tm); breakTime((time_t)timeClient.getEpochTime(), tm);
m_RTC.set(tm); m_RTC.Set(tm);
} else { } else {
Serial.begin(9600); Serial.begin(9600);
Serial.println("Failed to get NTP time"); Serial.println("Failed to get NTP time");

View file

@ -1,58 +1,46 @@
#include "WatchyRTC.h" #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() WatchyRTC::WatchyRTC()
{ {
} }
void WatchyRTC::init() { 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)
{ {
rtc_pcf.getDate(); }
void WatchyRTC::Get(tmElements_t & tm, int offsetInSeconds)
{
rtc_pcf.getDateTime();
tm.Year = y2kYearToTm(rtc_pcf.getYear()); tm.Year = y2kYearToTm(rtc_pcf.getYear());
tm.Month = rtc_pcf.getMonth(); tm.Month = rtc_pcf.getMonth();
tm.Day = rtc_pcf.getDay(); 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.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.Hour = rtc_pcf.getHour();
tm.Minute = rtc_pcf.getMinute(); tm.Minute = rtc_pcf.getMinute();
tm.Second = rtc_pcf.getSecond(); tm.Second = rtc_pcf.getSecond();
OffsetTime(tm, offsetInSeconds); 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 time_t t = makeTime(tm); // make and break to calculate tm.Wday
breakTime(t, tm); breakTime(t, tm);
// day, weekday, month, century(1=1900, 0=2000), year(0-99) // 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.setDateTime(tm.Day, tm.Wday - 1, tm.Month, 0, tmYearToY2k(tm.Year), tm.Hour, tm.Minute, tm.Second);
rtc_pcf.setTime(tm.Hour, tm.Minute, tm.Second); }
clearAlarm();
void WatchyRTC::SetTimer()
{
if (!m_timerSet) {
Resync();
}
} }
void WatchyRTC::OffsetTime(tmElements_t & tm, int offsetInSeconds) void WatchyRTC::OffsetTime(tmElements_t & tm, int offsetInSeconds)
@ -119,44 +107,55 @@ void WatchyRTC::OffsetTime(tmElements_t & tm, int offsetInSeconds)
tm.Second = second; tm.Second = second;
} }
void WatchyRTC::_PCFConfig(String datetime) { // String datetime is YYYY:MM:DD:HH:MM:SS // TODO: implement more advanced wakeup logic, i.e. > 255 seconds that are not a multiple of 60
if (datetime != "") { bool WatchyRTC::CheckWakeup()
tmElements_t tm; {
tm.Year = CalendarYrToTm(_getValue(datetime, ':', 0).toInt()); // YYYY - if(m_initialTimer) {
// 1970 m_initialTimer = false;
tm.Month = _getValue(datetime, ':', 1).toInt();
tm.Day = _getValue(datetime, ':', 2).toInt(); byte frequency, interval;
tm.Hour = _getValue(datetime, ':', 3).toInt(); if (UPDATE_INTERVAL % 60 == 0) {
tm.Minute = _getValue(datetime, ':', 4).toInt(); frequency = TMR_1MIN; // 1 minute
tm.Second = _getValue(datetime, ':', 5).toInt(); interval = UPDATE_INTERVAL / 60;
time_t t = makeTime(tm); // make and break to calculate tm.Wday } else {
breakTime(t, tm); frequency = TMR_1Hz; // 1 second
// day, weekday, month, century(1=1900, 0=2000), year(0-99) interval = UPDATE_INTERVAL;
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);
} }
// on POR event, PCF8563 sets month to 0, which will give an error since // Timer doesn't work reliably unless it's cleared first
// months are 1-12 rtc_pcf.clearTimer();
clearAlarm(); rtc_pcf.setTimer(interval, frequency, true);
return true;
}
return true;
} }
String WatchyRTC::_getValue(String data, char separator, int index) { void WatchyRTC::Resync()
int found = 0; {
int strIndex[] = {0, -1}; rtc_pcf.getDateTime();
int maxIndex = data.length() - 1; // 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);
for (int i = 0; i <= maxIndex && found <= index; i++) { if (seconds < 0) {
if (data.charAt(i) == separator || i == maxIndex) { seconds = 0;
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i + 1 : i;
}
} }
return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; // 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;
} }

View file

@ -15,17 +15,17 @@ public:
public: public:
WatchyRTC(); WatchyRTC();
void init(); void Init();
void config(String datetime); // String datetime format is YYYY:MM:DD:HH:MM:SS void Get(tmElements_t & tm, int offsetInSeconds = 0);
void clearAlarm(); void Set(tmElements_t tm);
void read(tmElements_t & tm, int offsetInSeconds = 0); void SetTimer();
void set(tmElements_t tm); 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); static void OffsetTime(tmElements_t & tm, int offsetInSeconds);
private: private:
void _PCFConfig(String datetime); static RTC_DATA_ATTR bool m_timerSet, m_initialTimer;
String _getValue(String data, char separator, int index);
}; };
#endif #endif

View file

@ -30,5 +30,5 @@
#define NTP_SERVER "pool.ntp.org" #define NTP_SERVER "pool.ntp.org"
#define UPDATE_INTERVAL 1 #define UPDATE_INTERVAL 60 // seconds
#define WAKE_ON_ACCEL_EVENTS false #define WAKE_ON_ACCEL_EVENTS false // useful if saving battery by not updating every minute