Merge branch 'feature/experimental-rtc-modifications'
This commit is contained in:
commit
f564f8c6e8
14 changed files with 1474 additions and 90 deletions
47
lib/Rtc_Pcf8563/README.rst
Normal file
47
lib/Rtc_Pcf8563/README.rst
Normal 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
|
740
lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp
Normal file
740
lib/Rtc_Pcf8563/Rtc_Pcf8563.cpp
Normal 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;
|
||||||
|
}
|
307
lib/Rtc_Pcf8563/Rtc_Pcf8563.h
Normal file
307
lib/Rtc_Pcf8563/Rtc_Pcf8563.h
Normal 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
|
63
lib/Rtc_Pcf8563/examples/lcd_clock/lcd_clock.pde
Normal file
63
lib/Rtc_Pcf8563/examples/lcd_clock/lcd_clock.pde
Normal 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
87
lib/Rtc_Pcf8563/examples/set_alarm/set_alarm.pde
Normal file
87
lib/Rtc_Pcf8563/examples/set_alarm/set_alarm.pde
Normal 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);
|
||||||
|
}
|
||||||
|
|
52
lib/Rtc_Pcf8563/examples/set_clock/set_clock.pde
Normal file
52
lib/Rtc_Pcf8563/examples/set_clock/set_clock.pde
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
66
lib/Rtc_Pcf8563/keywords.txt
Normal file
66
lib/Rtc_Pcf8563/keywords.txt
Normal 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
|
16
lib/Rtc_Pcf8563/library.json
Normal file
16
lib/Rtc_Pcf8563/library.json
Normal 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"
|
||||||
|
}
|
|
@ -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+
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in a new issue