first commit

This commit is contained in:
Jérôme Delacotte
2025-03-06 11:15:32 +01:00
commit 7b30d6e298
5276 changed files with 2108927 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@@ -0,0 +1,24 @@
# Rtc
Arduino Real Time Clock library.
An RTC library with deep device support.
[![Donate](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6AA97KE54UJR4)
For quick questions and support:
* [Try the new Github Discussions](https://github.com/Makuna/Rtc/discussions)
* Or, jump on Gitter
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Makuna/Rtc?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
For bugs, make sure there isn't an active issue and then create one.
## Documentation
[See Wiki](https://github.com/Makuna/Rtc/wiki)
## Installing This Library (prefered, you just want to use it)
Open the Library Manager and search for "Rtc by Makuna" and install
## Installing This Library From GitHub (advanced, you want to contribute)
Create a directory in your Arduino\Library folder named "Rtc"
Clone (Git) this project into that folder.
It should now show up in the import list when you restart Arduino IDE.

View File

@@ -0,0 +1,8 @@
# Support
For questions and help, jump on Gitter and ask away.
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Makuna/Rtc?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
For documentation [See the Wiki](https://github.com/Makuna/Rtc/wiki)
For bugs, make sure there isn't an active issue and then create one. Understand that issues are for bugs found in the library and not issues you are having with the library.

View File

@@ -0,0 +1,175 @@
// CONNECTIONS:
// At24c32 SDA --> SDA
// At24c32 SCL --> SCL
// At24c32 VCC --> 5v
// At24c32 GND --> GND
/* for software wire use below
#include <SoftwareWire.h> // must be included here so that Arduino library object file references work
#include <EepromAT24C32.h>
SoftwareWire myWire(SDA, SCL);
EepromAt24c32<SoftwareWire> Rtc(myWire);
/* for software wire use above */
/* for normal hardware wire use below */
#include <Wire.h> // must be included here so that Arduino library object file references work
#include <EepromAT24C32.h>
EepromAt24c32<TwoWire> RtcEeprom(Wire);
// if the above doesn't work, and you don't have "address" pads or switches on
// the board to set the address, then try the common address of 7
// EepromAt24c32<TwoWire> RtcEeprom(Wire, 0b111);
//
// If you have any of the address pads on the board soldered together
// then you need to provide the state of those pads, normally they
// are connected to vcc with a reading of 1, if soldered they are
// grounded with a reading of 0. The bits are in the order A2 A1 A0
// thus the following would have the A2 soldered together
// EepromAt24c32<TwoWire> RtcEeprom(Wire, 0b011);
/* for normal hardware wire use above */
// nothing longer than 32 bytes
// rtc eeprom memory is 32 byte pages
// writing is limited to each page, so it will wrap at page
// boundaries.
// But reading is only limited by the buffer in Wire class which
// by default is 32
// example settings objec that will be serialized into and out of EEPROM
struct Settings
{
uint8_t size; // size of this structure for versioning/validation
uint8_t value1 : 6; // only 6 bits (0-63)
uint8_t value2 : 6; // only 6 bits (0-63)
uint8_t value3 : 6; // only 6 bits (0-63)
uint32_t value4;
};
// where in EEPROM we will store the settings
const uint8_t SettingsAddress = 0;
// handy routine to return true if there was an error
// but it will also print out an error message with the given topic
bool wasError(const char* errorTopic = "")
{
uint8_t error = RtcEeprom.LastError();
if (error != 0)
{
// we have a communications error
// see https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/
// for what the number means
Serial.print("[");
Serial.print(errorTopic);
Serial.print("] WIRE communications error (");
Serial.print(error);
Serial.print(") : ");
switch (error)
{
case Rtc_Wire_Error_None:
Serial.println("(none?!)");
break;
case Rtc_Wire_Error_TxBufferOverflow:
Serial.println("transmit buffer overflow");
break;
case Rtc_Wire_Error_NoAddressableDevice:
Serial.println("no device responded");
break;
case Rtc_Wire_Error_UnsupportedRequest:
Serial.println("device doesn't support request");
break;
case Rtc_Wire_Error_Unspecific:
Serial.println("unspecified error");
break;
case Rtc_Wire_Error_CommunicationTimeout:
Serial.println("communications timed out");
break;
}
return true;
}
return false;
}
void setup ()
{
Serial.begin(115200);
//--------RTC EEPROM SETUP ------------
// if you are using ESP-01 then uncomment the line below to define pins for
// those available for SDA, SCL
// RtcEeprom.Begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
RtcEeprom.Begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(3000 /* us */, true /* reset_on_timeout */);
#endif
/* comment out on a second run to see that the info is stored long term */
// Store something in memory on the Eeprom
{
Settings mySettings =
{
sizeof(Settings), // size
42, // value1
21, // value2
7, // value3
18875, // value4
};
// store the settings, nothing longer than 32 bytes due to paging
uint8_t written = RtcEeprom.SetMemory(SettingsAddress,
reinterpret_cast<const uint8_t*>(&mySettings),
sizeof(mySettings));
wasError("setup setMemory settings");
}
/* end of comment out section */
}
void loop ()
{
delay(5000);
// read data
{
Settings retrievedSettings = { 0, 0, 0, 0, 0 }; // init to zero
// get our data from the address with the given size
uint8_t gotten = RtcEeprom.GetMemory(SettingsAddress,
reinterpret_cast<uint8_t*>(&retrievedSettings),
sizeof(Settings));
if (!wasError("loop getMemory settings"))
{
if (gotten != sizeof(Settings) ||
retrievedSettings.size != sizeof(Settings))
{
Serial.print("something didn't match, requested = ");
Serial.print(sizeof(Settings));
Serial.print(", gotten = ");
Serial.print(gotten);
Serial.print(", size = ");
Serial.print(retrievedSettings.size);
Serial.println();
}
Serial.print("data read (");
Serial.print(gotten);
Serial.println(")");
Serial.print(" size = ");
Serial.println(retrievedSettings.size);
Serial.print(" value1 = ");
Serial.println(retrievedSettings.value1);
Serial.print(" value2 = ");
Serial.println(retrievedSettings.value2);
Serial.print(" value3 = ");
Serial.println(retrievedSettings.value3);
Serial.print(" value4 = ");
Serial.println(retrievedSettings.value4);
Serial.println();
}
}
}

View File

@@ -0,0 +1,102 @@
#include "RtcAlarmManager.h"
#include <Wire.h>
#include <RtcDS1307.h> // Replace with the RTC you have
// foreward declare our alarm manager callback
void alarmCallback(uint8_t id, const RtcDateTime& alarm);
// global instance of the manager with three possible alarms
RtcAlarmManager<alarmCallback> Alarms(3);
// Replace with the RTC you have
RtcDS1307<TwoWire> Rtc(Wire);
void setup ()
{
Serial.begin(115200);
Serial.println("Initializing...");
//--------RTC SETUP ------------
Rtc.Begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(3000 /* us */, true /* reset_on_timeout */);
#endif
// get the real date and time from a source like an already
// configured RTC module
RtcDateTime now = Rtc.GetDateTime();
// Sync the Alarms to current time
Alarms.Sync(now);
// NOTE: Due to this sketch not deleting alarms, the returned ids from
// AddAlarm can be assumed to start at zero and increment from there.
// Otherwise the ids would need to be captured and used in the callback
//
int8_t result;
// add an alarm to sync time from rtc at a regular interval,
// due to CPU timing variance, the Alarms time can get off over
// time, so this alarm will trigger a resync every 20 minutes
result = Alarms.AddAlarm(now, 20 * c_MinuteAsSeconds); // every 20 minutes
if (result < 0)
{
// an error happened
Serial.print("AddAlarm Sync failed : ");
Serial.print(result);
}
// add a daily alarm at 5:30am
RtcDateTime working(now.Year(), now.Month(), now.Day(), 5, 30, 0);
result = Alarms.AddAlarm(working, AlarmPeriod_Daily);
if (result < 0)
{
// an error happened
Serial.print("AddAlarm Daily failed : ");
Serial.print(result);
}
// add a weekly alarm for Saturday at 7:30am
working = RtcDateTime(now.Year(), now.Month(), now.Day(), 7, 30, 0);
working = working.NextDayOfWeek(DayOfWeek_Saturday);
result = Alarms.AddAlarm(working, AlarmPeriod_Weekly);
if (result < 0)
{
// an error happened
Serial.print("AddAlarm Weekly failed : ");
Serial.print(result);
}
Serial.println("Running...");
}
void loop ()
{
delay(1000); // simulating other work your sketch will do
Alarms.ProcessAlarms();
}
void alarmCallback(uint8_t id, [[maybe_unused]] const RtcDateTime& alarm)
{
// NOTE: Due to this sketch not deleting alarms, the returned ids from
// AddAlarm can be assumed to start at zero and increment from there.
// Otherwise the ids would need to be captured and used here
//
switch (id)
{
case 0:
{
// periodic sync from trusted source to minimize
// drift due to inaccurate CPU timing
RtcDateTime now = Rtc.GetDateTime();
Alarms.Sync(now);
}
break;
case 1:
Serial.println("DAILY ALARM: Its 5:30am!");
break;
case 2:
Serial.println("WEEKLY ALARM: Its Saturday at 7:30am!");
break;
}
}

View File

@@ -0,0 +1,134 @@
// CONNECTIONS:
// DS1302 CLK/SCLK --> 5
// DS1302 DAT/IO --> 4
// DS1302 RST/CE --> 2
// DS1302 VCC --> 3.3v - 5v
// DS1302 GND --> GND
#include <RtcDS1302.h>
ThreeWire myWire(4,5,2); // IO, SCLK, CE
RtcDS1302<ThreeWire> Rtc(myWire);
#define countof(a) (sizeof(a) / sizeof(a[0]))
const char data[] = "what time is it";
void setup ()
{
Serial.begin(57600);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
Rtc.Begin();
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid())
{
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
if (Rtc.GetIsWriteProtected())
{
Serial.println("RTC was write protected, enabling writing now");
Rtc.SetIsWriteProtected(false);
}
if (!Rtc.GetIsRunning())
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
RtcDateTime now = Rtc.GetDateTime();
if (now < compiled)
{
Serial.println("RTC is older than compile time! (Updating DateTime)");
Rtc.SetDateTime(compiled);
}
/* comment out on a second run to see that the info is stored long term */
// Store something in memory on the RTC
uint8_t count = sizeof(data);
uint8_t written = Rtc.SetMemory((const uint8_t*)data, count); // this includes a null terminator for the string
if (written != count)
{
Serial.print("something didn't match, count = ");
Serial.print(count, DEC);
Serial.print(", written = ");
Serial.print(written, DEC);
Serial.println();
}
/* end of comment out section */
}
void loop ()
{
RtcDateTime now = Rtc.GetDateTime();
printDateTime(now);
Serial.println(" +");
if (!now.IsValid())
{
// Common Causes:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
delay(5000);
// read data
uint8_t buff[20];
const uint8_t count = sizeof(buff);
// get our data
uint8_t gotten = Rtc.GetMemory(buff, count);
if (gotten != count)
{
Serial.print("something didn't match, count = ");
Serial.print(count, DEC);
Serial.print(", gotten = ");
Serial.print(gotten, DEC);
Serial.println();
}
Serial.print("data read (");
Serial.print(gotten);
Serial.print(") = \"");
// print the string, but terminate if we get a null
for (uint8_t ch = 0; ch < gotten && buff[ch]; ch++)
{
Serial.print((char)buff[ch]);
}
Serial.println("\"");
delay(5000);
}
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,100 @@
// CONNECTIONS:
// DS1302 CLK/SCLK --> 5
// DS1302 DAT/IO --> 4
// DS1302 RST/CE --> 2
// DS1302 VCC --> 3.3v - 5v
// DS1302 GND --> GND
#include <RtcDS1302.h>
ThreeWire myWire(4,5,2); // IO, SCLK, CE
RtcDS1302<ThreeWire> Rtc(myWire);
void setup ()
{
Serial.begin(57600);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
Rtc.Begin();
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid())
{
// Common Causes:
// 1) first time you ran and the device wasn't running yet
// 2) the battery on the device is low or even missing
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
if (Rtc.GetIsWriteProtected())
{
Serial.println("RTC was write protected, enabling writing now");
Rtc.SetIsWriteProtected(false);
}
if (!Rtc.GetIsRunning())
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
RtcDateTime now = Rtc.GetDateTime();
if (now < compiled)
{
Serial.println("RTC is older than compile time! (Updating DateTime)");
Rtc.SetDateTime(compiled);
}
else if (now > compiled)
{
Serial.println("RTC is newer than compile time. (this is expected)");
}
else if (now == compiled)
{
Serial.println("RTC is the same as compile time! (not expected but all is fine)");
}
}
void loop ()
{
RtcDateTime now = Rtc.GetDateTime();
printDateTime(now);
Serial.println();
if (!now.IsValid())
{
// Common Causes:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
delay(10000); // ten seconds
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,241 @@
// CONNECTIONS:
// DS1307 SDA --> SDA
// DS1307 SCL --> SCL
// DS1307 VCC --> 5v
// DS1307 GND --> GND
#define countof(a) (sizeof(a) / sizeof(a[0]))
/* for software wire use below
#include <SoftwareWire.h> // must be included here so that Arduino library object file references work
#include <RtcDS1307.h>
SoftwareWire myWire(SDA, SCL);
RtcDS1307<SoftwareWire> Rtc(myWire);
for software wire use above */
/* for normal hardware wire use below */
#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcDS1307.h>
RtcDS1307<TwoWire> Rtc(Wire);
/* for normal hardware wire use above */
// nothing longer than 32 bytes
// rtc eeprom memory is 32 byte pages
// writing is limited to each page, so it will wrap at page
// boundaries.
// But reading is only limited by the buffer in Wire class which
// by default is 32
// example settings objec that will be serialized into and out of EEPROM
struct Settings
{
uint8_t size; // size of this structure for versioning/validation
uint8_t value1;
uint16_t value2;
uint32_t value3;
float value4;
};
// where in EEPROM we will store the settings
const uint8_t SettingsAddress = 0;
// handy routine to return true if there was an error
// but it will also print out an error message with the given topic
bool wasError(const char* errorTopic = "")
{
uint8_t error = Rtc.LastError();
if (error != 0)
{
// we have a communications error
// see https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/
// for what the number means
Serial.print("[");
Serial.print(errorTopic);
Serial.print("] WIRE communications error (");
Serial.print(error);
Serial.print(") : ");
switch (error)
{
case Rtc_Wire_Error_None:
Serial.println("(none?!)");
break;
case Rtc_Wire_Error_TxBufferOverflow:
Serial.println("transmit buffer overflow");
break;
case Rtc_Wire_Error_NoAddressableDevice:
Serial.println("no device responded");
break;
case Rtc_Wire_Error_UnsupportedRequest:
Serial.println("device doesn't support request");
break;
case Rtc_Wire_Error_Unspecific:
Serial.println("unspecified error");
break;
case Rtc_Wire_Error_CommunicationTimeout:
Serial.println("communications timed out");
break;
}
return true;
}
return false;
}
void setup ()
{
Serial.begin(115200);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
//--------RTC SETUP ------------
// if you are using ESP-01 then uncomment the line below to reset the pins to
// the available pins for SDA, SCL
// Rtc.Begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
Rtc.Begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(3000 /* us */, true /* reset_on_timeout */);
#endif
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid())
{
if (!wasError("setup IsDateTimeValid"))
{
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
}
if (!Rtc.GetIsRunning())
{
if (!wasError("setup GetIsRunning"))
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("setup GetDateTime"))
{
if (now < compiled)
{
Serial.println("RTC is older than compile time, updating DateTime");
Rtc.SetDateTime(compiled);
}
}
// never assume the Rtc was last configured by you, so
// just clear them to your needed state
Rtc.SetSquareWavePin(DS1307SquareWaveOut_Low);
wasError("setup SetSquareWavePin");
/* comment out on a second run to see that the info is stored long term */
// Store something in memory on the RTC
{
Settings mySettings =
{
sizeof(Settings), // size
42, // value1
420, // value2
18875, // value3
3.14159f, // value4
};
// store the settings, nothing longer than 32 bytes due to paging
uint8_t written = Rtc.SetMemory(SettingsAddress,
reinterpret_cast<const uint8_t*>(&mySettings),
sizeof(mySettings));
wasError("setup setMemory settings");
}
/* end of comment out section */
}
void loop ()
{
if (!Rtc.IsDateTimeValid())
{
if (!wasError("loop IsDateTimeValid"))
{
// Common Causes:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("loop GetDateTime"))
{
printDateTime(now);
Serial.println();
}
delay(5000);
// read data
{
Settings retrievedSettings = { 0, 0, 0, 0, 0 }; // init to zero
// get our data from the address with the given size
uint8_t gotten = Rtc.GetMemory(SettingsAddress,
reinterpret_cast<uint8_t*>(&retrievedSettings),
sizeof(Settings));
if (!wasError("loop getMemory settings"))
{
if (gotten != sizeof(Settings) ||
retrievedSettings.size != sizeof(Settings))
{
Serial.print("something didn't match, requested = ");
Serial.print(sizeof(Settings));
Serial.print(", gotten = ");
Serial.print(gotten);
Serial.print(", size = ");
Serial.print(retrievedSettings.size);
Serial.println();
}
Serial.print("data read (");
Serial.print(gotten);
Serial.println(")");
Serial.print(" size = ");
Serial.println(retrievedSettings.size);
Serial.print(" value1 = ");
Serial.println(retrievedSettings.value1);
Serial.print(" value2 = ");
Serial.println(retrievedSettings.value2);
Serial.print(" value3 = ");
Serial.println(retrievedSettings.value3);
Serial.print(" value4 = ");
Serial.println(retrievedSettings.value4);
Serial.println();
}
}
delay(5000);
}
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,176 @@
// CONNECTIONS:
// DS1307 SDA --> SDA
// DS1307 SCL --> SCL
// DS1307 VCC --> 5v
// DS1307 GND --> GND
/* for software wire use below
#include <SoftwareWire.h> // must be included here so that Arduino library object file references work
#include <RtcDS1307.h>
SoftwareWire myWire(SDA, SCL);
RtcDS1307<SoftwareWire> Rtc(myWire);
for software wire use above */
/* for normal hardware wire use below */
#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcDS1307.h>
RtcDS1307<TwoWire> Rtc(Wire);
/* for normal hardware wire use above */
// handy routine to return true if there was an error
// but it will also print out an error message with the given topic
bool wasError(const char* errorTopic = "")
{
uint8_t error = Rtc.LastError();
if (error != 0)
{
// we have a communications error
// see https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/
// for what the number means
Serial.print("[");
Serial.print(errorTopic);
Serial.print("] WIRE communications error (");
Serial.print(error);
Serial.print(") : ");
switch (error)
{
case Rtc_Wire_Error_None:
Serial.println("(none?!)");
break;
case Rtc_Wire_Error_TxBufferOverflow:
Serial.println("transmit buffer overflow");
break;
case Rtc_Wire_Error_NoAddressableDevice:
Serial.println("no device responded");
break;
case Rtc_Wire_Error_UnsupportedRequest:
Serial.println("device doesn't support request");
break;
case Rtc_Wire_Error_Unspecific:
Serial.println("unspecified error");
break;
case Rtc_Wire_Error_CommunicationTimeout:
Serial.println("communications timed out");
break;
}
return true;
}
return false;
}
void setup ()
{
Serial.begin(115200);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
//--------RTC SETUP ------------
// if you are using ESP-01 then uncomment the line below to reset the pins to
// the available pins for SDA, SCL
// Rtc.Begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
Rtc.Begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(3000 /* us */, true /* reset_on_timeout */);
#endif
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid())
{
if (!wasError("setup IsDateTimeValid"))
{
// Common Causes:
// 1) first time you ran and the device wasn't running yet
// 2) the battery on the device is low or even missing
Serial.println("RTC lost confidence in the DateTime!");
// following line sets the RTC to the date & time this sketch was compiled
// it will also reset the valid flag internally unless the Rtc device is
// having an issue
Rtc.SetDateTime(compiled);
}
}
if (!Rtc.GetIsRunning())
{
if (!wasError("setup GetIsRunning"))
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("setup GetDateTime"))
{
if (now < compiled)
{
Serial.println("RTC is older than compile time, updating DateTime");
Rtc.SetDateTime(compiled);
}
else if (now > compiled)
{
Serial.println("RTC is newer than compile time, this is expected");
}
else if (now == compiled)
{
Serial.println("RTC is the same as compile time, while not expected all is still fine");
}
}
// never assume the Rtc was last configured by you, so
// just clear them to your needed state
Rtc.SetSquareWavePin(DS1307SquareWaveOut_Low);
wasError("setup SetSquareWavePin");
}
void loop ()
{
if (!Rtc.IsDateTimeValid())
{
if (!wasError("loop IsDateTimeValid"))
{
// Common Causes:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("loop GetDateTime"))
{
printDateTime(now);
Serial.println();
}
delay(10000); // ten seconds
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,257 @@
// CONNECTIONS:
// DS3231 SDA --> SDA
// DS3231 SCL --> SCL
// DS3231 VCC --> 3.3v or 5v
// DS3231 GND --> GND
// SQW ---> (Pin19) Don't forget to pullup (4.7k to 10k to VCC)
/* for software wire use below
#include <SoftwareWire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3231.h>
SoftwareWire myWire(SDA, SCL);
RtcDS3231<SoftwareWire> Rtc(myWire);
for software wire use above */
/* for normal hardware wire use below */
#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3231.h>
RtcDS3231<TwoWire> Rtc(Wire);
/* for normal hardware wire use above */
// Interrupt Pin Lookup Table
// (copied from Arduino Docs)
//
// CAUTION: The interrupts are Arduino numbers NOT Atmel numbers
// and may not match (example, Mega2560 int.4 is actually Atmel Int2)
// this is only an issue if you plan to use the lower level interupt features
//
// Board int.0 int.1 int.2 int.3 int.4 int.5
// ---------------------------------------------------------------
// Uno, Ethernet 2 3
// Mega2560 2 3 21 20 [19] 18
// Leonardo 3 2 0 1 7
#define RtcSquareWavePin 19 // Mega2560
#define RtcSquareWaveInterrupt 4 // Mega2560
// marked volatile so interrupt can safely modify them and
// other code can safely read and modify them
volatile uint16_t interuptCount = 0;
volatile bool interuptFlag = false;
void ISR_ATTR InteruptServiceRoutine()
{
// since this interupted any other running code,
// don't do anything that takes long and especially avoid
// any communications calls within this routine
interuptCount++;
interuptFlag = true;
}
// handy routine to return true if there was an error
// but it will also print out an error message with the given topic
bool wasError(const char* errorTopic = "")
{
uint8_t error = Rtc.LastError();
if (error != 0)
{
// we have a communications error
// see https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/
// for what the number means
Serial.print("[");
Serial.print(errorTopic);
Serial.print("] WIRE communications error (");
Serial.print(error);
Serial.print(") : ");
switch (error)
{
case Rtc_Wire_Error_None:
Serial.println("(none?!)");
break;
case Rtc_Wire_Error_TxBufferOverflow:
Serial.println("transmit buffer overflow");
break;
case Rtc_Wire_Error_NoAddressableDevice:
Serial.println("no device responded");
break;
case Rtc_Wire_Error_UnsupportedRequest:
Serial.println("device doesn't support request");
break;
case Rtc_Wire_Error_Unspecific:
Serial.println("unspecified error");
break;
case Rtc_Wire_Error_CommunicationTimeout:
Serial.println("communications timed out");
break;
}
return true;
}
return false;
}
void setup ()
{
Serial.begin(115200);
// set the interupt pin to input mode
pinMode(RtcSquareWavePin, INPUT);
//--------RTC SETUP ------------
// if you are using ESP-01 then uncomment the line below to reset the pins to
// the available pins for SDA, SCL
// Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
Rtc.Begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(3000 /* us */, true /* reset_on_timeout */);
#endif
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
if (!Rtc.IsDateTimeValid())
{
if (!wasError("setup IsDateTimeValid"))
{
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
}
if (!Rtc.GetIsRunning())
{
if (!wasError("setup GetIsRunning"))
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("setup GetDateTime"))
{
if (now < compiled)
{
Serial.println("RTC is older than compile time, updating DateTime");
Rtc.SetDateTime(compiled);
}
}
Rtc.Enable32kHzPin(false);
wasError("setup Enable32kHzPin");
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeAlarmBoth);
wasError("setup SetSquareWavePin");
// Alarm 1 set to trigger every day when
// the hours, minutes, and seconds match
RtcDateTime alarmTime = now + 88; // into the future
DS3231AlarmOne alarm1(
alarmTime.Day(),
alarmTime.Hour(),
alarmTime.Minute(),
alarmTime.Second(),
DS3231AlarmOneControl_HoursMinutesSecondsMatch);
Rtc.SetAlarmOne(alarm1);
wasError("setup SetAlarmOne");
// Alarm 2 set to trigger at the top of the minute
DS3231AlarmTwo alarm2(
0,
0,
0,
DS3231AlarmTwoControl_OncePerMinute);
Rtc.SetAlarmTwo(alarm2);
wasError("setup SetAlarmTwo");
// throw away any old alarm state before we ran
Rtc.LatchAlarmsTriggeredFlags();
wasError("setup LatchAlarmsTriggeredFlags");
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
// setup external interupt
// for some Arduino hardware they use interrupt number for the first param
attachInterrupt(RtcSquareWaveInterrupt, InteruptServiceRoutine, FALLING);
#else
// for some Arduino hardware they use interrupt pin for the first param
attachInterrupt(RtcSquareWavePin, InteruptServiceRoutine, FALLING);
#endif
}
void loop ()
{
if (!Rtc.IsDateTimeValid())
{
if (!wasError("loop IsDateTimeValid"))
{
Serial.println("RTC lost confidence in the DateTime!");
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("loop GetDateTime"))
{
printDateTime(now);
Serial.println();
}
// we only want to show time every 10 seconds
// but we want to show responce to the interupt firing
for (int timeCount = 0; timeCount < 20; timeCount++)
{
if (Alarmed())
{
Serial.print(">>Interupt Count: ");
Serial.print(interuptCount);
Serial.println("<<");
}
delay(500);
}
}
bool Alarmed()
{
bool wasAlarmed = false;
if (interuptFlag) // check our flag that gets sets in the interupt
{
wasAlarmed = true;
interuptFlag = false; // reset the flag
// this gives us which alarms triggered and
// then allows for others to trigger again
DS3231AlarmFlag flag = Rtc.LatchAlarmsTriggeredFlags();
if (!wasError("alarmed LatchAlarmsTriggeredFlags"))
{
if (flag & DS3231AlarmFlag_Alarm1)
{
Serial.println("alarm one triggered");
}
if (flag & DS3231AlarmFlag_Alarm2)
{
Serial.println("alarm two triggered");
}
}
}
return wasAlarmed;
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,188 @@
// CONNECTIONS:
// DS3231 SDA --> SDA
// DS3231 SCL --> SCL
// DS3231 VCC --> 3.3v or 5v
// DS3231 GND --> GND
/* for software wire use below
#include <SoftwareWire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3231.h>
SoftwareWire myWire(SDA, SCL);
RtcDS3231<SoftwareWire> Rtc(myWire);
for software wire use above */
/* for normal hardware wire use below */
#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3231.h>
RtcDS3231<TwoWire> Rtc(Wire);
/* for normal hardware wire use above */
// handy routine to return true if there was an error
// but it will also print out an error message with the given topic
bool wasError(const char* errorTopic = "")
{
uint8_t error = Rtc.LastError();
if (error != 0)
{
// we have a communications error
// see https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/
// for what the number means
Serial.print("[");
Serial.print(errorTopic);
Serial.print("] WIRE communications error (");
Serial.print(error);
Serial.print(") : ");
switch (error)
{
case Rtc_Wire_Error_None:
Serial.println("(none?!)");
break;
case Rtc_Wire_Error_TxBufferOverflow:
Serial.println("transmit buffer overflow");
break;
case Rtc_Wire_Error_NoAddressableDevice:
Serial.println("no device responded");
break;
case Rtc_Wire_Error_UnsupportedRequest:
Serial.println("device doesn't support request");
break;
case Rtc_Wire_Error_Unspecific:
Serial.println("unspecified error");
break;
case Rtc_Wire_Error_CommunicationTimeout:
Serial.println("communications timed out");
break;
}
return true;
}
return false;
}
void setup ()
{
Serial.begin(115200);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
//--------RTC SETUP ------------
// if you are using ESP-01 then uncomment the line below to reset the pins to
// the available pins for SDA, SCL
// Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
Rtc.Begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(3000 /* us */, true /* reset_on_timeout */);
#endif
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid())
{
if (!wasError("setup IsDateTimeValid"))
{
// Common Causes:
// 1) first time you ran and the device wasn't running yet
// 2) the battery on the device is low or even missing
Serial.println("RTC lost confidence in the DateTime!");
// following line sets the RTC to the date & time this sketch was compiled
// it will also reset the valid flag internally unless the Rtc device is
// having an issue
Rtc.SetDateTime(compiled);
}
}
if (!Rtc.GetIsRunning())
{
if (!wasError("setup GetIsRunning"))
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("setup GetDateTime"))
{
if (now < compiled)
{
Serial.println("RTC is older than compile time, updating DateTime");
Rtc.SetDateTime(compiled);
}
else if (now > compiled)
{
Serial.println("RTC is newer than compile time, this is expected");
}
else if (now == compiled)
{
Serial.println("RTC is the same as compile time, while not expected all is still fine");
}
}
// never assume the Rtc was last configured by you, so
// just clear them to your needed state
Rtc.Enable32kHzPin(false);
wasError("setup Enable32kHzPin");
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone);
wasError("setup SetSquareWavePin");
}
void loop ()
{
if (!Rtc.IsDateTimeValid())
{
if (!wasError("loop IsDateTimeValid"))
{
// Common Causes:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("loop GetDateTime"))
{
printDateTime(now);
Serial.println();
}
RtcTemperature temp = Rtc.GetTemperature();
if (!wasError("loop GetTemperature"))
{
temp.Print(Serial);
// you may also get the temperature as a float and print it
// Serial.print(temp.AsFloatDegC());
Serial.println("C");
}
delay(10000); // ten seconds
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,163 @@
// CONNECTIONS:
// DS3231 SDA --> SDA
// DS3231 SCL --> SCL
// DS3231 VCC --> 3.3v or 5v
// DS3231 GND --> GND
/* for software wire use below
#include <SoftwareWire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3231.h>
SoftwareWire myWire(SDA, SCL);
RtcDS3231<SoftwareWire> Rtc(myWire);
for software wire use above */
/* for normal hardware wire use below */
#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3231.h>
RtcDS3231<TwoWire> Rtc(Wire);
/* for normal hardware wire use above */
// handy routine to return true if there was an error
// but it will also print out an error message with the given topic
bool wasError(const char* errorTopic = "")
{
uint8_t error = Rtc.LastError();
if (error != 0)
{
// we have a communications error
// see https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/
// for what the number means
Serial.print("[");
Serial.print(errorTopic);
Serial.print("] WIRE communications error (");
Serial.print(error);
Serial.print(") : ");
switch (error)
{
case Rtc_Wire_Error_None:
Serial.println("(none?!)");
break;
case Rtc_Wire_Error_TxBufferOverflow:
Serial.println("transmit buffer overflow");
break;
case Rtc_Wire_Error_NoAddressableDevice:
Serial.println("no device responded");
break;
case Rtc_Wire_Error_UnsupportedRequest:
Serial.println("device doesn't support request");
break;
case Rtc_Wire_Error_Unspecific:
Serial.println("unspecified error");
break;
case Rtc_Wire_Error_CommunicationTimeout:
Serial.println("communications timed out");
break;
}
return true;
}
return false;
}
void setup ()
{
Serial.begin(115200);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
//--------RTC SETUP ------------
// if you are using ESP-01 then uncomment the line below to reset the pins to
// the available pins for SDA, SCL
// Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
Rtc.Begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(3000 /* us */, true /* reset_on_timeout */);
#endif
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid())
{
if (!wasError("setup IsDateTimeValid"))
{
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
}
if (!Rtc.GetIsRunning())
{
if (!wasError("setup GetIsRunning"))
{
Serial.println("RTC was not actively running");
}
}
else
{
Serial.println("RTC is actively running");
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("setup GetDateTime"))
{
if (now < compiled)
{
Serial.println("RTC is older than compile time, updating DateTime");
Rtc.SetDateTime(compiled);
}
}
// never assume the Rtc was last configured by you, so
// just clear them to your needed state
Rtc.Enable32kHzPin(false);
wasError("setup Enable32kHzPin");
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone);
wasError("setup SetSquareWavePin");
}
void loop ()
{
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("loop GetDateTime"))
{
printDateTime(now);
Serial.println();
}
for(;;)
{
Rtc.SetIsRunning(false);
if (!wasError("loop SetIsRunning"))
{
Serial.println(">>> Rtc ready for storage <<<");
}
delay(10000); // ten seconds
}
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,247 @@
// CONNECTIONS:
// DS3232 SDA --> SDA
// DS3232 SCL --> SCL
// DS3232 VCC --> 5v
// DS3232 GND --> GND
#define countof(a) (sizeof(a) / sizeof(a[0]))
/* for software wire use below
#include <SoftwareWire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3232.h>
SoftwareWire myWire(SDA, SCL);
RtcDS3232<SoftwareWire> Rtc(myWire);
for software wire use above */
/* for normal hardware wire use below */
#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3232.h>
RtcDS3232<TwoWire> Rtc(Wire);
/* for normal hardware wire use above */
// nothing longer than 32 bytes
// rtc eeprom memory is 32 byte pages
// writing is limited to each page, so it will wrap at page
// boundaries.
// But reading is only limited by the buffer in Wire class which
// by default is 32
// example settings objec that will be serialized into and out of EEPROM
struct Settings
{
uint8_t size; // size of this structure for versioning/validation
uint8_t value1;
uint16_t value2;
uint32_t value3;
float value4;
};
// where in EEPROM we will store the settings
const uint8_t SettingsAddress = 0;
// handy routine to return true if there was an error
// but it will also print out an error message with the given topic
bool wasError(const char* errorTopic = "")
{
uint8_t error = Rtc.LastError();
if (error != 0)
{
// we have a communications error
// see https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/
// for what the number means
Serial.print("[");
Serial.print(errorTopic);
Serial.print("] WIRE communications error (");
Serial.print(error);
Serial.print(") : ");
switch (error)
{
case Rtc_Wire_Error_None:
Serial.println("(none?!)");
break;
case Rtc_Wire_Error_TxBufferOverflow:
Serial.println("transmit buffer overflow");
break;
case Rtc_Wire_Error_NoAddressableDevice:
Serial.println("no device responded");
break;
case Rtc_Wire_Error_UnsupportedRequest:
Serial.println("device doesn't support request");
break;
case Rtc_Wire_Error_Unspecific:
Serial.println("unspecified error");
break;
case Rtc_Wire_Error_CommunicationTimeout:
Serial.println("communications timed out");
break;
}
return true;
}
return false;
}
void setup ()
{
Serial.begin(115200);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
//--------RTC SETUP ------------
// if you are using ESP-01 then uncomment the line below to reset the pins to
// the available pins for SDA, SCL
// Rtc.Begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
Rtc.Begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(3000 /* us */, true /* reset_on_timeout */);
#endif
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid())
{
if (!wasError("setup IsDateTimeValid"))
{
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
}
if (!Rtc.GetIsRunning())
{
if (!wasError("setup GetIsRunning"))
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("setup GetDateTime"))
{
if (now < compiled)
{
Serial.println("RTC is older than compile time, updating DateTime");
Rtc.SetDateTime(compiled);
}
}
// never assume the Rtc was last configured by you, so
// just clear them to your needed state
Rtc.Enable32kHzPin(false);
wasError("setup Enable32kHzPin");
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone);
wasError("setup SetSquareWavePin");
/* comment out on a second run to see that the info is stored long term */
// Store something in memory on the RTC
{
Settings mySettings =
{
sizeof(Settings), // size
42, // value1
420, // value2
18875, // value3
3.14159f, // value4
};
// store the settings, nothing longer than 32 bytes due to I2C buffer
uint8_t written = Rtc.SetMemory(SettingsAddress,
reinterpret_cast<const uint8_t*>(&mySettings),
sizeof(mySettings));
wasError("setup setMemory settings");
if (written != sizeof(mySettings))
{
Serial.println("setup setMemory failed to write complete settings");
}
}
/* end of comment out section */
}
void loop ()
{
if (!Rtc.IsDateTimeValid())
{
if (!wasError("loop IsDateTimeValid"))
{
// Common Causes:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("loop GetDateTime"))
{
printDateTime(now);
Serial.println();
}
delay(5000);
// read data
{
Settings retrievedSettings = { 0,0,0,0,0 }; // init size to zero
// get our data from the address with the given size
uint8_t gotten = Rtc.GetMemory(SettingsAddress,
reinterpret_cast<uint8_t*>(&retrievedSettings),
sizeof(Settings));
if (!wasError("loop getMemory settings"))
{
if (gotten != sizeof(Settings) ||
retrievedSettings.size != sizeof(Settings))
{
Serial.print("something didn't match, requested = ");
Serial.print(sizeof(Settings));
Serial.print(", gotten = ");
Serial.print(gotten);
Serial.print(", size = ");
Serial.print(retrievedSettings.size);
Serial.println();
}
Serial.print("data read (");
Serial.print(gotten);
Serial.println(")");
Serial.print(" size = ");
Serial.println(retrievedSettings.size);
Serial.print(" value1 = ");
Serial.println(retrievedSettings.value1);
Serial.print(" value2 = ");
Serial.println(retrievedSettings.value2);
Serial.print(" value3 = ");
Serial.println(retrievedSettings.value3);
Serial.print(" value4 = ");
Serial.println(retrievedSettings.value4);
Serial.println();
}
}
delay(5000);
}
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,179 @@
// Reference for connecting SPI see https://www.arduino.cc/en/Reference/SPI
// CONNECTIONS:
// DS3234 MISO --> MISO
// DS3234 MOSI --> MOSI
// DS3234 CLK --> CLK (SCK)
// DS3234 CS (SS) --> 5 (pin used to select the DS3234 on the SPI)
// DS3234 VCC --> 3.3v or 5v
// DS3234 GND --> GND
// SQW ---> (Pin19) Don't forget to pullup (4.7k to 10k to VCC)
const uint8_t DS3234_CS_PIN = 5;
#include <SPI.h>
#include <RtcDS3234.h>
RtcDS3234<SPIClass> Rtc(SPI, DS3234_CS_PIN);
// Interrupt Pin Lookup Table
// (copied from Arduino Docs)
//
// CAUTION: The interrupts are Arduino numbers NOT Atmel numbers
// and may not match (example, Mega2560 int.4 is actually Atmel Int2)
// this is only an issue if you plan to use the lower level interupt features
//
// Board int.0 int.1 int.2 int.3 int.4 int.5
// ---------------------------------------------------------------
// Uno, Ethernet 2 3
// Mega2560 2 3 21 20 [19] 18
// Leonardo 3 2 0 1 7
// esp8266 (pin and interrupt should be the same thing)
// esp32 (pin and interrupt should be the same thing)
#define RtcSquareWavePin 19 // Mega2560
#define RtcSquareWaveInterrupt 4 // Mega2560
// marked volatile so interrupt can safely modify them and
// other code can safely read and modify them
volatile uint16_t interuptCount = 0;
volatile bool interuptFlag = false;
void ISR_ATTR InteruptServiceRoutine()
{
// since this interupted any other running code,
// don't do anything that takes long and especially avoid
// any communications calls within this routine
interuptCount++;
interuptFlag = true;
}
void setup ()
{
Serial.begin(115200);
while (!Serial);
// set the interupt pin to input mode
pinMode(RtcSquareWavePin, INPUT_PULLUP); // external pullup maybe required still
SPI.begin();
Rtc.Begin();
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
if (!Rtc.IsDateTimeValid())
{
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
if (!Rtc.GetIsRunning())
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
RtcDateTime now = Rtc.GetDateTime();
if (now < compiled)
{
Serial.println("RTC is older than compile time! (Updating DateTime)");
Rtc.SetDateTime(compiled);
}
Rtc.Enable32kHzPin(false);
Rtc.SetSquareWavePin(DS3234SquareWavePin_ModeAlarmBoth);
// Alarm 1 set to trigger every day when
// the hours, minutes, and seconds match
RtcDateTime alarmTime = now + 88; // into the future
DS3234AlarmOne alarm1(
alarmTime.Day(),
alarmTime.Hour(),
alarmTime.Minute(),
alarmTime.Second(),
DS3234AlarmOneControl_HoursMinutesSecondsMatch);
Rtc.SetAlarmOne(alarm1);
// Alarm 2 set to trigger at the top of the minute
DS3234AlarmTwo alarm2(
0,
0,
0,
DS3234AlarmTwoControl_OncePerMinute);
Rtc.SetAlarmTwo(alarm2);
// throw away any old alarm state before we ran
Rtc.LatchAlarmsTriggeredFlags();
// setup external interupt
attachInterrupt(RtcSquareWaveInterrupt, InteruptServiceRoutine, FALLING);
}
void loop ()
{
if (!Rtc.IsDateTimeValid())
{
Serial.println("RTC lost confidence in the DateTime!");
}
RtcDateTime now = Rtc.GetDateTime();
printDateTime(now);
Serial.println();
// we only want to show time every 10 seconds
// but we want to show responce to the interupt firing
for (int timeCount = 0; timeCount < 20; timeCount++)
{
if (Alarmed())
{
Serial.print(">>Interupt Count: ");
Serial.print(interuptCount);
Serial.println("<<");
}
delay(500);
}
}
bool Alarmed()
{
bool wasAlarmed = false;
if (interuptFlag) // check our flag that gets sets in the interupt
{
wasAlarmed = true;
interuptFlag = false; // reset the flag
// this gives us which alarms triggered and
// then allows for others to trigger again
DS3234AlarmFlag flag = Rtc.LatchAlarmsTriggeredFlags();
if (flag & DS3234AlarmFlag_Alarm1)
{
Serial.println("alarm one triggered");
}
if (flag & DS3234AlarmFlag_Alarm2)
{
Serial.println("alarm two triggered");
}
}
return wasAlarmed;
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,139 @@
// Reference for connecting SPI see https://www.arduino.cc/en/Reference/SPI
// CONNECTIONS:
// DS3234 MISO --> MISO
// DS3234 MOSI --> MOSI
// DS3234 CLK --> CLK (SCK)
// DS3234 CS (SS) --> 5 (pin used to select the DS3234 on the SPI)
// DS3234 VCC --> 3.3v or 5v
// DS3234 GND --> GND
const uint8_t DS3234_CS_PIN = 5;
#include <SPI.h>
#include <RtcDS3234.h>
RtcDS3234<SPIClass> Rtc(SPI, DS3234_CS_PIN);
const char data[] = "what time is it";
void setup ()
{
Serial.begin(115200);
while (!Serial);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
SPI.begin();
Rtc.Begin();
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid())
{
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
if (!Rtc.GetIsRunning())
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
RtcDateTime now = Rtc.GetDateTime();
if (now < compiled)
{
Serial.println("RTC is older than compile time! (Updating DateTime)");
Rtc.SetDateTime(compiled);
}
// never assume the Rtc was last configured by you, so
// just clear them to your needed state
Rtc.SetSquareWavePin(DS3234SquareWavePin_ModeNone);
/* comment out on a second run to see that the info is stored long term */
// Store something in memory on the RTC
Rtc.SetMemory(0, 13); // address of a data item
uint8_t written = Rtc.SetMemory(13, (const uint8_t*)data, sizeof(data) - 1); // remove the null terminator strings add
Rtc.SetMemory(1, written); // size of data time
/* end of comment out section */
}
void loop ()
{
if (!Rtc.IsDateTimeValid())
{
// Common Causes:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
RtcDateTime now = Rtc.GetDateTime();
printDateTime(now);
Serial.println();
delay(5000);
// read data
// get the offset we stored our data from address zero
uint8_t address = Rtc.GetMemory(0);
if (address != 13)
{
Serial.println("address didn't match");
}
else
{
// get the size of the data from address 1
uint8_t count = Rtc.GetMemory(1);
uint8_t buff[20];
// get our data from the address with the given size
uint8_t gotten = Rtc.GetMemory(address, buff, count);
if (gotten != count ||
count != sizeof(data) - 1) // remove the extra null terminator strings add
{
Serial.print("something didn't match, count = ");
Serial.print(count, DEC);
Serial.print(", gotten = ");
Serial.print(gotten, DEC);
Serial.println();
}
Serial.print("data read (");
Serial.print(gotten);
Serial.print(") = \"");
for (uint8_t ch = 0; ch < gotten; ch++)
{
Serial.print((char)buff[ch]);
}
Serial.println("\"");
}
delay(5000);
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,115 @@
// Reference for connecting SPI see https://www.arduino.cc/en/Reference/SPI
// CONNECTIONS:
// DS3234 MISO --> MISO
// DS3234 MOSI --> MOSI
// DS3234 CLK --> CLK (SCK)
// DS3234 CS (SS) --> 5 (pin used to select the DS3234 on the SPI)
// DS3234 VCC --> 3.3v or 5v
// DS3234 GND --> GND
const uint8_t DS3234_CS_PIN = 5;
#include <SPI.h>
#include <RtcDS3234.h>
RtcDS3234<SPIClass> Rtc(SPI, DS3234_CS_PIN);
void setup ()
{
Serial.begin(115200);
while (!Serial);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
SPI.begin();
Rtc.Begin();
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid())
{
// Common Causes:
// 1) first time you ran and the device wasn't running yet
// 2) the battery on the device is low or even missing
Serial.println("RTC lost confidence in the DateTime!");
// following line sets the RTC to the date & time this sketch was compiled
// it will also reset the valid flag internally unless the Rtc device is
// having an issue
Rtc.SetDateTime(compiled);
}
if (!Rtc.GetIsRunning())
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
RtcDateTime now = Rtc.GetDateTime();
if (now < compiled)
{
Serial.println("RTC is older than compile time! (Updating DateTime)");
Rtc.SetDateTime(compiled);
}
else if (now > compiled)
{
Serial.println("RTC is newer than compile time. (this is expected)");
}
else if (now == compiled)
{
Serial.println("RTC is the same as compile time! (not expected but all is fine)");
}
// never assume the Rtc was last configured by you, so
// just clear them to your needed state
Rtc.Enable32kHzPin(false);
Rtc.SetSquareWavePin(DS3234SquareWavePin_ModeNone);
}
void loop ()
{
if (!Rtc.IsDateTimeValid())
{
// Common Causes:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
RtcDateTime now = Rtc.GetDateTime();
printDateTime(now);
Serial.println();
RtcTemperature temp = Rtc.GetTemperature();
temp.Print(Serial);
// you may also get the temperature as a float and print it
// Serial.print(temp.AsFloatDegC());
Serial.println("C");
delay(10000); // ten seconds
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,269 @@
// CONNECTIONS:
// PCF8563 SDA --> SDA
// PCF8563 SCL --> SCL
// PCF8563 VCC --> 3.3v or 5v
// PCF8563 GND --> GND
// PCF8563 INT ---> (Pin19) Don't forget to pullup (4.7k to 10k to VCC)
/* for software wire use below
#include <SoftwareWire.h> // must be included here so that Arduino library object file references work
#include <RtcPCF8563.h>
SoftwareWire myWire(SDA, SCL);
RtcPCF8563<SoftwareWire> Rtc(myWire);
for software wire use above */
/* for normal hardware wire use below */
#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcPCF8563.h>
RtcPCF8563<TwoWire> Rtc(Wire);
/* for normal hardware wire use above */
// Interrupt Pin Lookup Table
// (copied from Arduino Docs)
//
// CAUTION: The interrupts are Arduino numbers NOT Atmel numbers
// and may not match (example, Mega2560 int.4 is actually Atmel Int2)
// this is only an issue if you plan to use the lower level interupt features
//
// Board int.0 int.1 int.2 int.3 int.4 int.5
// ---------------------------------------------------------------
// Uno, Ethernet 2 3
// Mega2560 2 3 21 20 [19] 18
// Leonardo 3 2 0 1 7
#define RtcInterruptPin 19 // Mega2560
#define RtcInterruptInterrupt 4 // Mega2560
// marked volatile so interrupt can safely modify them and
// other code can safely read and modify them
volatile uint16_t interuptCount = 0;
volatile bool interuptFlag = false;
void ISR_ATTR InteruptServiceRoutine()
{
// since this interupted any other running code,
// don't do anything that takes long and especially avoid
// any communications calls within this routine
interuptCount++;
interuptFlag = true;
}
// handy routine to return true if there was an error
// but it will also print out an error message with the given topic
bool wasError(const char* errorTopic = "")
{
uint8_t error = Rtc.LastError();
if (error != 0)
{
// we have a communications error
// see https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/
// for what the number means
Serial.print("[");
Serial.print(errorTopic);
Serial.print("] WIRE communications error (");
Serial.print(error);
Serial.print(") : ");
switch (error)
{
case Rtc_Wire_Error_None:
Serial.println("(none?!)");
break;
case Rtc_Wire_Error_TxBufferOverflow:
Serial.println("transmit buffer overflow");
break;
case Rtc_Wire_Error_NoAddressableDevice:
Serial.println("no device responded");
break;
case Rtc_Wire_Error_UnsupportedRequest:
Serial.println("device doesn't support request");
break;
case Rtc_Wire_Error_Unspecific:
Serial.println("unspecified error");
break;
case Rtc_Wire_Error_CommunicationTimeout:
Serial.println("communications timed out");
break;
}
return true;
}
return false;
}
void setup ()
{
Serial.begin(115200);
// set the interupt pin to input mode
pinMode(RtcInterruptPin, INPUT);
//--------RTC SETUP ------------
// if you are using ESP-01 then uncomment the line below to reset the pins to
// the available pins for SDA, SCL
// Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
Rtc.Begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(3000 /* us */, true /* reset_on_timeout */);
#endif
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
if (!Rtc.IsDateTimeValid())
{
if (!wasError("setup IsDateTimeValid"))
{
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
}
if (!Rtc.GetIsRunning())
{
if (!wasError("setup GetIsRunning"))
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("setup GetDateTime"))
{
if (now < compiled)
{
Serial.println("RTC is older than compile time, updating DateTime");
Rtc.SetDateTime(compiled);
}
}
// never assume the Rtc was last configured by you, so
// just clear them to your needed state
Rtc.StopAlarm();
wasError("setup StopAlarm");
Rtc.StopTimer();
wasError("setup StopTimer");
Rtc.SetSquareWavePin(PCF8563SquareWavePinMode_None);
wasError("setup SetSquareWavePin");
// Alarm set to trigger every day when
// the hours and minutes match
RtcDateTime alarmTime = now + 88; // into the future
PCF8563Alarm alarm(
alarmTime.Day(), // will be ignored in this example
alarmTime.Hour(),
alarmTime.Minute(),
alarmTime.DayOfWeek(), // will be ignored in this example
PCF8563AlarmControl_MinuteMatch | PCF8563AlarmControl_HourMatch
);
Rtc.SetAlarm(alarm);
wasError("setup SetAlarm");
// Timer set to trigger in 100 seconds
//
Rtc.SetTimer(PCF8563TimerMode_Seconds, 100);
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
// setup external interupt
// for some Arduino hardware they use interrupt number for the first param
attachInterrupt(RtcInterruptInterrupt, InteruptServiceRoutine, FALLING);
#else
// for some Arduino hardware they use interrupt pin for the first param
attachInterrupt(RtcInterruptPin, InteruptServiceRoutine, FALLING);
#endif
}
void loop ()
{
if (!Rtc.IsDateTimeValid())
{
if (!wasError("loop IsDateTimeValid"))
{
Serial.println("RTC lost confidence in the DateTime!");
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("loop GetDateTime"))
{
printDateTime(now);
Serial.println();
}
// we only want to show time every 10 seconds
// but we want to show responce to the interupt firing
for (int timeCount = 0; timeCount < 20; timeCount++)
{
if (Alarmed())
{
Serial.print(">>Interupt Count: ");
Serial.print(interuptCount);
Serial.println("<<");
}
delay(500);
}
}
bool Alarmed()
{
bool result = false;
if (interuptFlag) // check our flag that gets sets in the interupt
{
interuptFlag = false; // reset the flag
// calling LatchAlarmTriggeredFlag() will return if
// the alarm was what triggered the interrupt and also
// resets the alarm flag inside the rtc which then allows
// for alarms to trigger again.
// note that the same int pin is also used for
// the timer trigger if also used
bool wasAlarmed = Rtc.LatchAlarmTriggeredFlag();
if (!wasError("alarmed LatchAlarmTriggeredFlag"))
{
if (wasAlarmed)
{
result = true;
Serial.println("alarm triggered");
}
}
// calling LatchTimerTriggeredFlag() will return if
// the timer was what triggered the interrupt and also
// resets the timer flag inside the rtc which then allows
// for timers to trigger again.
// note that the same int pin is also used for
// the alarm trigger if also used
bool wasTimerExpired = Rtc.LatchTimerTriggeredFlag();
if (!wasError("alarmed LatchTimerTriggeredFlag"))
{
if (wasTimerExpired)
{
result = true;
Serial.println("timer triggered");
}
}
}
return result;
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,181 @@
// CONNECTIONS:
// PCF8563 SDA --> SDA
// PCF8563 SCL --> SCL
// PCF8563 VCC --> 3.3v or 5v
// PCF8563 GND --> GND
/* for software wire use below
#include <SoftwareWire.h> // must be included here so that Arduino library object file references work
#include <RtcPCF8563.h>
SoftwareWire myWire(SDA, SCL);
RtcPCF8563<SoftwareWire> Rtc(myWire);
for software wire use above */
/* for normal hardware wire use below */
#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcPCF8563.h>
RtcPCF8563<TwoWire> Rtc(Wire);
/* for normal hardware wire use above */
// handy routine to return true if there was an error
// but it will also print out an error message with the given topic
bool wasError(const char* errorTopic = "")
{
uint8_t error = Rtc.LastError();
if (error != 0)
{
// we have a communications error
// see https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/
// for what the number means
Serial.print("[");
Serial.print(errorTopic);
Serial.print("] WIRE communications error (");
Serial.print(error);
Serial.print(") : ");
switch (error)
{
case Rtc_Wire_Error_None:
Serial.println("(none?!)");
break;
case Rtc_Wire_Error_TxBufferOverflow:
Serial.println("transmit buffer overflow");
break;
case Rtc_Wire_Error_NoAddressableDevice:
Serial.println("no device responded");
break;
case Rtc_Wire_Error_UnsupportedRequest:
Serial.println("device doesn't support request");
break;
case Rtc_Wire_Error_Unspecific:
Serial.println("unspecified error");
break;
case Rtc_Wire_Error_CommunicationTimeout:
Serial.println("communications timed out");
break;
}
return true;
}
return false;
}
void setup ()
{
Serial.begin(115200);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
//--------RTC SETUP ------------
// if you are using ESP-01 then uncomment the line below to reset the pins to
// the available pins for SDA, SCL
// Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL
Rtc.Begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(3000 /* us */, true /* reset_on_timeout */);
#endif
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid())
{
if (!wasError("setup IsDateTimeValid"))
{
// Common Causes:
// 1) first time you ran and the device wasn't running yet
// 2) the battery on the device is low or even missing
Serial.println("RTC lost confidence in the DateTime!");
// following line sets the RTC to the date & time this sketch was compiled
// it will also reset the valid flag internally unless the Rtc device is
// having an issue
Rtc.SetDateTime(compiled);
}
}
if (!Rtc.GetIsRunning())
{
if (!wasError("setup GetIsRunning"))
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("setup GetDateTime"))
{
if (now < compiled)
{
Serial.println("RTC is older than compile time, updating DateTime");
Rtc.SetDateTime(compiled);
}
else if (now > compiled)
{
Serial.println("RTC is newer than compile time, this is expected");
}
else if (now == compiled)
{
Serial.println("RTC is the same as compile time, while not expected all is still fine");
}
}
// never assume the Rtc was last configured by you, so
// just clear them to your needed state
Rtc.StopAlarm();
wasError("setup StopAlarm");
Rtc.StopTimer();
wasError("setup StopTimer");
Rtc.SetSquareWavePin(PCF8563SquareWavePinMode_None);
wasError("setup SetSquareWavePin");
}
void loop ()
{
if (!Rtc.IsDateTimeValid())
{
if (!wasError("loop IsDateTimeValid"))
{
// Common Causes:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
}
RtcDateTime now = Rtc.GetDateTime();
if (!wasError("loop GetDateTime"))
{
printDateTime(now);
Serial.println();
}
delay(10000); // ten seconds
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt)
{
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}

View File

@@ -0,0 +1,204 @@
// These tests do not rely on RTC hardware at all
//#include <Wire.h> // must be included here so that Arduino library object file references work
#include <RtcDS3231.h>
void PrintPassFail(bool passed)
{
if (passed)
{
Serial.print("passed");
}
else
{
Serial.print("failed");
}
}
void ComparePrintlnPassFail(RtcTemperature& rtcTemp, float compare)
{
Serial.print(rtcTemp.AsFloatDegC());
Serial.print("C ");
PrintPassFail(rtcTemp.AsFloatDegC() == compare);
Serial.println();
}
void ConstructorTests()
{
// RTC constructors
Serial.println("Constructors:");
{
RtcTemperature temp075Below(0b11111111, 0b01000000); // -0.75
ComparePrintlnPassFail(temp075Below, -0.75f);
RtcTemperature temp050Below(0b11111111, 0b10000000); // -0.5
ComparePrintlnPassFail(temp050Below, -0.50f);
RtcTemperature temp025Below(0b11111111, 0b11000000); // -0.25
ComparePrintlnPassFail(temp025Below, -0.25f);
RtcTemperature tempZero(0b00000000, 0b00000000); // 0.0
ComparePrintlnPassFail(tempZero, -0.0f);
RtcTemperature temp025Above(0b00000000, 0b01000000); // 0.25
ComparePrintlnPassFail(temp025Above, 0.25f);
RtcTemperature temp050Above(0b00000000, 0b10000000); // 0.5
ComparePrintlnPassFail(temp050Above, 0.5f);
RtcTemperature temp075Above(0b00000000, 0b11000000); // 0.75
ComparePrintlnPassFail(temp075Above, 0.75f);
RtcTemperature temp25Above(0b00011001, 0b00000000); // 25.0
ComparePrintlnPassFail(temp25Above, 25.0f);
RtcTemperature temp25Below(0b11100111, 0b00000000); // -25.0
ComparePrintlnPassFail(temp25Below, -25.0f);
}
Serial.println();
// SameType
{
Serial.print("same type ");
RtcTemperature temp25Below(0b11100111, 0b00000000); // -25.0
RtcTemperature test = temp25Below;
ComparePrintlnPassFail(test, -25.0f);
}
// CentiDegrees
{
Serial.print("centi degrees ");
RtcTemperature temp025Below(-25); // -0.25
ComparePrintlnPassFail(temp025Below, -0.25f);
Serial.print("centi degrees ");
RtcTemperature temp025Above(25); // 0.25
ComparePrintlnPassFail(temp025Above, 0.25f);
Serial.print("centi degrees ");
RtcTemperature temp25Below(-2500); // -25.0
ComparePrintlnPassFail(temp25Below, -25.0f);
Serial.print("centi degrees ");
RtcTemperature temp25Above(2500); // 25.0
ComparePrintlnPassFail(temp25Above, 25.0f);
}
Serial.println();
}
void PrintlnExpected(RtcTemperature& temp, uint16_t digits)
{
Serial.print(" = ");
Serial.print(temp.AsFloatDegC(), digits);
Serial.println();
}
void PrintTests()
{
Serial.println("Prints:");
RtcTemperature temp25Above(2500);
temp25Above.Print(Serial);
PrintlnExpected(temp25Above, 2);
RtcTemperature temp25Below(-2500);
temp25Below.Print(Serial);
PrintlnExpected(temp25Below, 2);
RtcTemperature temp025Above(25);
temp025Above.Print(Serial);
PrintlnExpected(temp025Above, 2);
temp025Above.Print(Serial, 1);
PrintlnExpected(temp025Above, 1);
RtcTemperature temp025Below(-25);
temp025Below.Print(Serial);
PrintlnExpected(temp025Below, 2);
temp025Below.Print(Serial, 1);
PrintlnExpected(temp025Below, 1);
RtcTemperature temp050Above(50);
temp050Above.Print(Serial);
PrintlnExpected(temp050Above, 2);
temp050Above.Print(Serial, 0);
PrintlnExpected(temp050Above, 0);
RtcTemperature temp050Below(-50);
temp050Below.Print(Serial);
PrintlnExpected(temp050Below, 2);
temp050Below.Print(Serial, 0);
PrintlnExpected(temp050Below, 0);
temp050Below.Print(Serial, 2, ',');
Serial.println(" == -0,50");
Serial.println();
}
void MathmaticalOperatorTests()
{
Serial.println("Mathmaticals:");
RtcTemperature temp050Below(-50);
RtcTemperature temp050Above(50);
RtcTemperature temp050Diff(100);
RtcTemperature temp050Same(-50);
RtcTemperature tempResult;
Serial.print("equality ");
PrintPassFail(temp050Below == temp050Same);
Serial.println();
Serial.print("inequality ");
PrintPassFail(temp050Below != temp050Above);
Serial.println();
Serial.print("less than ");
PrintPassFail(temp050Below < temp050Above);
Serial.println();
Serial.print("greater than ");
PrintPassFail(temp050Above > temp050Below);
Serial.println();
Serial.print("less than ");
PrintPassFail(temp050Below <= temp050Above);
Serial.print(" or equal ");
PrintPassFail(temp050Below <= temp050Same);
Serial.println();
Serial.print("greater than ");
PrintPassFail(temp050Above >= temp050Below);
Serial.print(" or equal ");
PrintPassFail(temp050Below >= temp050Same);
Serial.println();
tempResult = temp050Above - temp050Below;
Serial.print("subtraction ");
PrintPassFail(tempResult == temp050Diff);
Serial.println();
tempResult = temp050Above + temp050Above;
Serial.print("addition ");
PrintPassFail(tempResult == temp050Diff);
Serial.println();
Serial.println();
}
void setup ()
{
Serial.begin(115200);
while (!Serial);
Serial.println();
ConstructorTests();
PrintTests();
MathmaticalOperatorTests();
}
void loop ()
{
delay(500);
}

View File

@@ -0,0 +1,209 @@
#######################################
# Syntax Coloring Map RTC
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
ThreeWire KEYWORD1
RtcDS1302 KEYWORD1
RtcDS1307 KEYWORD1
DS3234AlarmOne KEYWORD1
DS3234AlarmTwo KEYWORD1
RtcDS3234 KEYWORD1
DS3231AlarmOne KEYWORD1
DS3231AlarmTwo KEYWORD1
RtcDS3231 KEYWORD1
EepromAt24c32 KEYWORD1
RtcPCF8563 KEYWORD1
PCF8563Alarm KEYWORD1
RtcAlarmManager KEYWORD1
RtcTemperature KEYWORD1
RtcDateTime KEYWORD1
DayOfWeek KEYWORD1
AlarmPeriod KEYWORD1
AlarmAddError KEYWORD1
RtcAlarmCallback KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
Begin KEYWORD2
LastError KEYWORD2
IsDateTimeValid KEYWORD2
GetIsRunning KEYWORD2
SetIsRunning KEYWORD2
GetIsWriteProtected KEYWORD2
SetIsWriteProtected KEYWORD2
SetDateTime KEYWORD2
GetDateTime KEYWORD2
GetTemperature KEYWORD2
Enable32kHzPin KEYWORD2
SetSquareWavePin KEYWORD2
SetSquareWavePinClockFrequency KEYWORD2
SetAlarmOne KEYWORD2
SetAlarmTwo KEYWORD2
GetAlarmOne KEYWORD2
GetAlarmTwo KEYWORD2
LatchAlarmsTriggeredFlags KEYWORD2
ForceTemperatureCompensationUpdate KEYWORD2
SetTemperatureCompensationRate KEYWORD2
GetTemperatureCompensationRate KEYWORD2
GetAgingOffset KEYWORD2
SetAgingOffset KEYWORD2
GetMemory KEYWORD2
SetMemory KEYWORD2
GetTrickleChargeSettings KEYWORD2
SetTrickleChargeSettings KEYWORD2
SetAlarm KEYWORD2
StopAlarm KEYWORD2
SetTimer KEYWORD2
StopTimer KEYWORD2
LatchAlarmTriggeredFlag KEYWORD2
LatchTimerTriggeredFlag KEYWORD2
GetAlarmTriggered KEYWORD2
GetTimerTriggered KEYWORD2
AsFloatDegC KEYWORD2
AsFloatDegF KEYWORD2
AsCentiDegC KEYWORD2
Print KEYWORD2
Year KEYWORD2
Month KEYWORD2
Day KEYWORD2
Hour KEYWORD2
Minute KEYWORD2
Second KEYWORD2
DayOfWeek KEYWORD2
TotalSeconds KEYWORD2
TotalSeconds64 KEYWORD2
TotalDays KEYWORD2
DayOf KEYWORD2
IsValid KEYWORD2
Unix32Time KEYWORD2
InitWithUnix32Time KEYWORD2
Unix64Time KEYWORD2
InitWithUnix64Time KEYWORD2
Ntp32Time KEYWORD2
InitWithNtp32Time KEYWORD2
Ntp64Time KEYWORD2
InitWithNtp64Time KEYWORD2
InitWithDateTimeFormatString KEYWORD2
ConvertDowToRtc KEYWORD2
ConvertRtcToDow KEYWORD2
ControlFlags KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
DS3231SquareWaveClock_1Hz LITERAL1
DS3231SquareWaveClock_1kHz LITERAL1
DS3231SquareWaveClock_4kHz LITERAL1
DS3231SquareWaveClock_8kHz LITERAL1
DS3231SquareWavePin_ModeNone LITERAL1
DS3231SquareWavePin_ModeClock LITERAL1
DS3231SquareWavePin_ModeAlarmOne LITERAL1
DS3231SquareWavePin_ModeAlarmTwo LITERAL1
DS3231SquareWavePin_ModeAlarmBoth LITERAL1
DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch LITERAL1
DS3231AlarmOneControl_OncePerSecond LITERAL1
DS3231AlarmOneControl_SecondsMatch LITERAL1
DS3231AlarmOneControl_MinutesSecondsMatch LITERAL1
DS3231AlarmOneControl_HoursMinutesSecondsMatch LITERAL1
DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch LITERAL1
DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch LITERAL1
DS3231AlarmTwoControl_OncePerMinute LITERAL1
DS3231AlarmTwoControl_MinutesMatch LITERAL1
DS3231AlarmTwoControl_HoursMinutesMatch LITERAL1
DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch LITERAL1
DS3231AlarmFlag_Alarm1 LITERAL1
DS3231AlarmFlag_Alarm2 LITERAL1
DS3231AlarmFlag_AlarmBoth LITERAL1
DS1302RamSize LITERAL1
DS1302Tcr_Disabled LITERAL1
DS1302TcrResistor_2KOhm LITERAL1
DS1302TcrResistor_4KOhm LITERAL1
DS1302TcrResistor_8KOhm LITERAL1
DS1302TcrDiodes_One LITERAL1
DS1302TcrDiodes_Two LITERAL1
DS1302TcrStatus_Enabled LITERAL1
DS1302TcrStatus_Disabled LITERAL1
DS1307SquareWaveOut_1Hz LITERAL1
DS1307SquareWaveOut_4kHz LITERAL1
DS1307SquareWaveOut_8kHz LITERAL1
DS1307SquareWaveOut_32kHz LITERAL1
DS1307SquareWaveOut_High LITERAL1
DS1307SquareWaveOut_Low LITERAL1
DS3234SquareWaveClock_1Hz LITERAL1
DS3234SquareWaveClock_1kHz LITERAL1
DS3234SquareWaveClock_4kHz LITERAL1
DS3234SquareWaveClock_8kHz LITERAL1
DS3234SquareWavePin_ModeNone LITERAL1
DS3234SquareWavePin_ModeBatteryBackup LITERAL1
DS3234SquareWavePin_ModeClock LITERAL1
DS3234SquareWavePin_ModeAlarmOne LITERAL1
DS3234SquareWavePin_ModeAlarmTwo LITERAL1
DS3234SquareWavePin_ModeAlarmBoth LITERAL1
DS3234AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch LITERAL1
DS3234AlarmOneControl_OncePerSecond LITERAL1
DS3234AlarmOneControl_SecondsMatch LITERAL1
DS3234AlarmOneControl_MinutesSecondsMatch LITERAL1
DS3234AlarmOneControl_HoursMinutesSecondsMatch LITERAL1
DS3234AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch LITERAL1
DS3234AlarmTwoControl_HoursMinutesDayOfMonthMatch LITERAL1
DS3234AlarmTwoControl_OncePerMinute LITERAL1
DS3234AlarmTwoControl_MinutesMatch LITERAL1
DS3234AlarmTwoControl_HoursMinutesMatch LITERAL1
DS3234AlarmTwoControl_HoursMinutesDayOfWeekMatch LITERAL1
DS3234AlarmFlag_Alarm1 LITERAL1
DS3234AlarmFlag_Alarm2 LITERAL1
DS3234AlarmFlag_AlarmBoth LITERAL1
DS3234TempCompensationRate_64Seconds LITERAL1
DS3234TempCompensationRate_128Seconds LITERAL1
DS3234TempCompensationRate_256Seconds LITERAL1
DS3234TempCompensationRate_512Seconds LITERAL1
PCF8563SquareWavePinMode_None LITERAL1
PCF8563SquareWavePinMode_32kHz LITERAL1
PCF8563SquareWavePinMode_1kHz LITERAL1
PCF8563SquareWavePinMode_32Hz LITERAL1
PCF8563SquareWavePinMode_1Hz LITERAL1
PCF8563TimerMode_None LITERAL1
PCF8563TimerMode_4096thOfASecond LITERAL1
PCF8563TimerMode_64thOfASecond LITERAL1
PCF8563TimerMode_Seconds LITERAL1
PCF8563TimerMode_Minutes LITERAL1
PCF8563AlarmControl_MinuteMatch LITERAL1
PCF8563AlarmControl_HourMatch LITERAL1
PCF8563AlarmControl_DayOfMonthMatch LITERAL1
PCF8563AlarmControl_DayOfWeekMatch LITERAL1
DayOfWeek_Sunday LITERAL1
DayOfWeek_Monday LITERAL1
DayOfWeek_Tuesday LITERAL1
DayOfWeek_Wednesday LITERAL1
DayOfWeek_Thursday LITERAL1
DayOfWeek_Friday LITERAL1
DayOfWeek_Saturday LITERAL1
Rtc_Wire_Error LITERAL1
Rtc_Wire_Error_None LITERAL1
Rtc_Wire_Error_TxBufferOverflow LITERAL1
Rtc_Wire_Error_NoAddressableDevice LITERAL1
Rtc_Wire_Error_UnsupportedRequest LITERAL1
Rtc_Wire_Error_Unspecific LITERAL1
Rtc_Wire_Error_CommunicationTimeout LITERAL1
RtcLocaleEnUs LITERAL1
RtcLocaleEn LITERAL1
AlarmPeriod_Expired LITERAL1
AlarmPeriod_SingleFire LITERAL1
AlarmPeriod_Yearly LITERAL1
AlarmPeriod_Monthly LITERAL1
AlarmPeriod_Monthly_LastDay LITERAL1
AlarmPeriod_Weekly LITERAL1
AlarmPeriod_Daily LITERAL1
AlarmPeriod_Hourly LITERAL1
AlarmAddError_PeriodInvalid LITERAL1
AlarmAddError_TimePast LITERAL1
AlarmAddError_TimeInvalid LITERAL1
AlarmAddError_CountExceeded LITERAL1

View File

@@ -0,0 +1,13 @@
{
"name": "RTC",
"keywords": "RTC, DS1302, DS1307, DS3231, DS3234, AT24C32, PCF8563, BM8563, clock",
"description": "A library that makes interfacing DS1302, DS1307, DS3231, DS3234, and PCF8563/BM8563 Real Time Clock modules easy.",
"repository": {
"type": "git",
"url": "https://github.com/Makuna/Rtc.git"
},
"version": "2.4.2",
"frameworks": "arduino",
"platforms": "*"
}

View File

@@ -0,0 +1,9 @@
name=Rtc by Makuna
version=2.4.2
author=Michael C. Miller (makuna@live.com)
maintainer=Michael C. Miller (makuna@live.com)
sentence=A library that makes interfacing DS1302, DS1307, DS3231, DS3234, and PCF8563/BM8563 Real Time Clock modules easy.
paragraph=Includes deep support of module features, including temperature, alarms and memory storage if present. Tested on esp8266.
category=Device Control
url=https://github.com/Makuna/Rtc/wiki
architectures=*

View File

@@ -0,0 +1,145 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include <Arduino.h>
#pragma once
//I2C Slave Address
const uint8_t AT24C32_ADDRESS = 0x50; // 0b0 1010 A2 A1 A0
template<class T_WIRE_METHOD> class EepromAt24c32
{
public:
EepromAt24c32(T_WIRE_METHOD& wire, uint8_t addressBits = 0b000) :
_address(AT24C32_ADDRESS | (addressBits & 0b00000111)),
_wire(wire),
_lastError(Rtc_Wire_Error_None)
{
}
void Begin()
{
_wire.begin();
}
void Begin(int sda, int scl)
{
_wire.begin(sda, scl);
}
uint8_t LastError()
{
return _lastError;
}
void SetMemory(uint16_t memoryAddress, uint8_t value)
{
SetMemory(memoryAddress, &value, 1);
}
uint8_t GetMemory(uint16_t memoryAddress)
{
uint8_t value;
GetMemory(memoryAddress, &value, 1);
return value;
}
// note: this method will write within a single page of eeprom.
// Pages are 32 bytes (5 bits), so writing past a page boundary will
// just wrap within the page of the starting memory address.
//
// xxxppppp pppaaaaa => p = page #, a = address within the page
//
// NOTE: hardware WIRE libraries often have a limit of a 32 byte send buffer. The
// effect of this is that only 30 bytes can be sent, 2 bytes for the address to write to,
// and then 30 bytes of the actual data.
uint8_t SetMemory(uint16_t memoryAddress, const uint8_t* pValue, uint8_t countBytes)
{
uint8_t countWritten = 0;
beginTransmission(memoryAddress);
while (countBytes > 0)
{
_wire.write(*pValue++);
delay(10); // per spec, memory writes
countBytes--;
countWritten++;
}
_lastError = _wire.endTransmission();
return countWritten;
}
// reading data does not wrap within pages, but due to only using
// 12 (32K) or 13 (64K) bits are used, they will wrap within the memory limits
// of the installed EEPROM
//
// NOTE: hardware WIRE libraries may have a limit of a 32 byte recieve buffer. The
// effect of this is that only 32 bytes can be read at one time.
uint8_t GetMemory(uint16_t memoryAddress, uint8_t* pValue, uint8_t countBytes)
{
// set address to read from
beginTransmission(memoryAddress);
_lastError = _wire.endTransmission();
if (_lastError != 0)
{
return 0;
}
// read the data
uint8_t countRead = 0;
countRead = _wire.requestFrom(_address, countBytes);
countBytes = countRead;
while (countBytes-- > 0)
{
*pValue++ = _wire.read();
}
return countRead;
}
private:
const uint8_t _address;
T_WIRE_METHOD& _wire;
uint8_t _lastError;
void beginTransmission(uint16_t memoryAddress)
{
_wire.beginTransmission(_address);
_wire.write(memoryAddress >> 8);
_wire.write(memoryAddress & 0xFf);
}
};

View File

@@ -0,0 +1,400 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
#include "RtcUtility.h"
#include "RtcDateTime.h"
enum AlarmPeriod
{
AlarmPeriod_Expired,
AlarmPeriod_SingleFire,
AlarmPeriod_Yearly,
AlarmPeriod_Monthly,
AlarmPeriod_Monthly_LastDay,
AlarmPeriod_Weekly,
AlarmPeriod_Daily,
AlarmPeriod_Hourly,
// the below values are for internal use only
AlarmPeriod_Yearly_Feb29th, // last of month in Feb if days less than and not a leap year,
AlarmPeriod_Monthly_29th, // last of month if days less than,
AlarmPeriod_Monthly_30th, // otherwise the day of month matching,
AlarmPeriod_Monthly_31st, // this will be set internally, just use monthly
AlarmPeriod_StartOfSpecifics = 60 // anything over this is considered a specific time in seconds
};
enum AlarmAddError
{
AlarmAddError_PeriodInvalid = -4,
AlarmAddError_TimePast,
AlarmAddError_TimeInvalid,
AlarmAddError_CountExceeded,
};
typedef void(*RtcAlarmCallback)(uint8_t id, const RtcDateTime& alarm);
template <RtcAlarmCallback V_CALLBACK> class RtcAlarmManager
{
public:
// construct
// count - the max number of active alarms
RtcAlarmManager(uint8_t count) :
_alarmsCount(count)
{
_alarms = new Alarm[_alarmsCount];
}
~RtcAlarmManager()
{
delete[] _alarms;
}
// Sync the time to the external trusted source, like
// a RTC module
// Do this at regular intervals as the internal CPU timing
// is not very accurate
int32_t Sync(const RtcDateTime& now)
{
uint32_t msNow = millis();
uint32_t secondsNow = now.TotalSeconds();
// calc an updated seconds for old information
uint32_t secondsOld = _seconds + (msNow - _msLast) / 1000;
// set new seconds and start tracking the millis
_msLast = msNow;
_seconds = secondsNow;
// return the delta from new seconds from old seconds
return (secondsNow - secondsOld);
}
// retrieve what the current time the AlarmManager thinks it is
// due to inacurrancy of the CPU timing this may not be exact,
// but it is good enough for most timing needs
// regular use of Sync() will improve this
RtcDateTime Now() const
{
uint32_t msNow = millis();
uint32_t secondsNow = _seconds + (msNow - _msLast) / 1000;
return RtcDateTime(secondsNow);
}
// add an alarm
// when - the date and time to start triggering alarms
// period - the type of alarm, does it repeat and how often, see AlarmPeriod enum
// return - if postive, the id of the Alarm, otherwise see AlarmAddError
int8_t AddAlarm(const RtcDateTime& when,
uint32_t period)
{
if (!when.IsValid())
{
return AlarmAddError_TimeInvalid;
}
if (period > AlarmPeriod_Monthly_31st &&
period < AlarmPeriod_StartOfSpecifics)
{
return AlarmAddError_PeriodInvalid;
}
int8_t result = AlarmAddError_TimeInvalid;
uint32_t seconds = when.TotalSeconds();
if (period == AlarmPeriod_Monthly_LastDay)
{
period = AlarmPeriod_Monthly_31st;
// adjust given when to last day of its set month
uint8_t daysInMonth = RtcDateTime::DaysInMonth(when.Year(), when.Month());
if (when.Day() < daysInMonth)
{
RtcDateTime temp(when.Year(),
when.Month(),
daysInMonth,
when.Hour(),
when.Minute(),
when.Second());
seconds = temp.TotalSeconds();
}
}
else if (period == AlarmPeriod_Monthly ||
(period >= AlarmPeriod_Monthly_29th && period <= AlarmPeriod_Monthly_31st))
{
period = AlarmPeriod_Monthly;
// adjust alarm period to store target day of month
// for when months have less days than the target
// it will trigger on the last day of the month but
// retain and trigger on specific day of month when
// available
if (when.Day() == 29)
{
period = AlarmPeriod_Monthly_29th;
}
else if (when.Day() == 30)
{
period = AlarmPeriod_Monthly_30th;
}
else if (when.Day() == 31)
{
period = AlarmPeriod_Monthly_31st;
}
}
else if (period == AlarmPeriod_Yearly)
{
if (when.Day() == 29 && when.Month() == 2)
{
// adjust alarm period to store target day of month
// for when Feb 29th is target but following year isn't
// a leap year it will trigger on the last day of Feb but
// retain and trigger on specific day of month when
// available
period = AlarmPeriod_Yearly_Feb29th;
}
}
Alarm alarm(seconds, period);
// if the alarm was added that was already in the past,
// we increment the when to the next repeat
// for non-repeatable alarms this may expire them
if (seconds <= _seconds)
{
alarm.IncrementWhen();
}
if (alarm.Period == AlarmPeriod_Expired)
{
result = AlarmAddError_TimePast;
}
else
{
result = AlarmAddError_CountExceeded;
for (uint8_t id = 0; id < _alarmsCount; id++)
{
if (_alarms[id].Period == AlarmPeriod_Expired)
{
_alarms[id] = alarm;
result = id;
break;
}
}
}
return result;
}
// remove an existing alarm
// id - previously returned id from AddAlarm()
void RemoveAlarm(uint8_t id)
{
if (id < _alarmsCount)
{
_alarms[id].Period = AlarmPeriod_Expired;
}
}
// check if the alarm is still active
// id - previously returned id from AddAlarm()
bool IsAlarmActive(uint8_t id)
{
if (id < _alarmsCount)
{
return (_alarms[id].Period != AlarmPeriod_Expired);
}
return false;
}
// process all the alarms which can trigger callbacks
// call at regular intervals, if you need seconds accuracy, call
// every second.
// There is little need to call this faster than a few
// times per second but it doesn't hurt anything
void ProcessAlarms()
{
uint32_t msNow = millis();
uint32_t msDelta = (msNow - _msLast);
if (msDelta > 1000)
{
// update seconds based on passed time using millis()
_seconds += msDelta / 1000;
_msLast = msNow - (msDelta % 1000); // retain fractional second
// used a local seconds in case a callback changes it
uint32_t seconds = _seconds;
for (uint8_t id = 0; id < _alarmsCount; id++)
{
if (_alarms[id].Period != AlarmPeriod_Expired)
{
if (_alarms[id].When <= seconds)
{
RtcDateTime alarm(_alarms[id].When);
if (_alarms[id].Period == AlarmPeriod_SingleFire)
{
// remove from list
_alarms[id].Period = AlarmPeriod_Expired;
}
else
{
_alarms[id].IncrementWhen();
}
// make callback
V_CALLBACK(id, alarm);
}
}
}
}
}
protected:
struct Alarm
{
uint32_t When; // seconds from RtcDateTime.TotalSeconds()
uint32_t Period;
Alarm(uint32_t when = 0, uint32_t period = AlarmPeriod_Expired) :
When(when),
Period(period)
{
}
void IncrementWhen()
{
switch (Period)
{
case AlarmPeriod_Expired:
break;
case AlarmPeriod_SingleFire:
Period = AlarmPeriod_Expired;
break;
case AlarmPeriod_Yearly:
{
RtcDateTime temp(When);
RtcDateTime next(temp.Year() + 1,
temp.Month(),
temp.Day(),
temp.Hour(),
temp.Minute(),
temp.Second());
When = next.TotalSeconds();
}
break;
case AlarmPeriod_Yearly_Feb29th:
{
RtcDateTime temp(When);
uint16_t year = temp.Year() + 1;
uint8_t day = 28;
if (RtcDateTime::IsLeapYear(year))
{
day = 29;
}
RtcDateTime next(year,
2,
day,
temp.Hour(),
temp.Minute(),
temp.Second());
When = next.TotalSeconds();
}
break;
case AlarmPeriod_Monthly:
case AlarmPeriod_Monthly_29th:
case AlarmPeriod_Monthly_30th:
case AlarmPeriod_Monthly_31st:
{
RtcDateTime temp(When);
uint16_t year = temp.Year();
uint8_t month = temp.Month() + 1;
uint8_t day;
if (month > 12)
{
year++;
month = 1;
}
if (Period == AlarmPeriod_Monthly)
{
// use the day of the month from previous When
day = temp.Day();
}
else
{
// use the day of the month cached as it may have
// been capped to the last day of the month in When
day = 29 + (Period - AlarmPeriod_Monthly_29th);
}
uint8_t daysInMonth = RtcDateTime::DaysInMonth(year, month);
if (day > daysInMonth)
{
day = daysInMonth;
}
RtcDateTime next(year,
month,
day,
temp.Hour(),
temp.Minute(),
temp.Second());
When = next.TotalSeconds();
}
break;
case AlarmPeriod_Weekly:
When += c_WeekAsSeconds;
break;
case AlarmPeriod_Daily:
When += c_DayAsSeconds;
break;
case AlarmPeriod_Hourly:
When += c_HourAsSeconds;
break;
default:
When += Period;
break;
}
}
};
Alarm* _alarms; // table of possible alarms
uint8_t _alarmsCount; // max alarms in _alarms
uint32_t _msLast; // the last call to millis()
uint32_t _seconds; // the approximate date time, as seconds from 2000
};

View File

@@ -0,0 +1,289 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
#include "RtcUtility.h"
#include "RtcDateTime.h"
#include "ThreeWire.h"
//DS1302 Register Addresses
const uint8_t DS1302_REG_TIMEDATE = 0x80;
const uint8_t DS1302_REG_TIMEDATE_BURST = 0xBE;
const uint8_t DS1302_REG_TCR = 0x90;
const uint8_t DS1302_REG_RAM_BURST = 0xFE;
const uint8_t DS1302_REG_RAMSTART = 0xc0;
const uint8_t DS1302_REG_RAMEND = 0xfd;
// ram read and write addresses are interleaved
const uint8_t DS1302RamSize = 31;
// DS1302 Trickle Charge Control Register Bits
enum DS1302TcrResistor
{
DS1302TcrResistor_Disabled = 0,
DS1302TcrResistor_2KOhm = B00000001,
DS1302TcrResistor_4KOhm = B00000010,
DS1302TcrResistor_8KOhm = B00000011,
DS1302TcrResistor_MASK = B00000011,
};
enum DS1302TcrDiodes
{
DS1302TcrDiodes_None = 0,
DS1302TcrDiodes_One = B00000100,
DS1302TcrDiodes_Two = B00001000,
DS1302TcrDiodes_Disabled = B00001100,
DS1302TcrDiodes_MASK = B00001100,
};
enum DS1302TcrStatus
{
DS1302TcrStatus_Enabled = B10100000,
DS1302TcrStatus_Disabled = B01010000,
DS1302TcrStatus_MASK = B11110000,
};
const uint8_t DS1302Tcr_Disabled = DS1302TcrStatus_Disabled | DS1302TcrDiodes_Disabled | DS1302TcrResistor_Disabled;
// DS1302 Clock Halt Register & Bits
const uint8_t DS1302_REG_CH = 0x80; // bit in the seconds register
const uint8_t DS1302_CH = 7;
// Write Protect Register & Bits
const uint8_t DS1302_REG_WP = 0x8E;
const uint8_t DS1302_WP = 7;
template<class T_WIRE_METHOD> class RtcDS1302
{
public:
RtcDS1302(T_WIRE_METHOD& wire) :
_wire(wire)
{
}
void Begin()
{
_wire.begin();
}
bool GetIsWriteProtected()
{
uint8_t wp = getReg(DS1302_REG_WP);
return !!(wp & _BV(DS1302_WP));
}
void SetIsWriteProtected(bool isWriteProtected)
{
uint8_t wp = getReg(DS1302_REG_WP);
if (isWriteProtected)
{
wp |= _BV(DS1302_WP);
}
else
{
wp &= ~_BV(DS1302_WP);
}
setReg(DS1302_REG_WP, wp);
}
bool IsDateTimeValid()
{
return GetDateTime().IsValid();
}
bool GetIsRunning()
{
uint8_t ch = getReg(DS1302_REG_CH);
return !(ch & _BV(DS1302_CH));
}
void SetIsRunning(bool isRunning)
{
uint8_t ch = getReg(DS1302_REG_CH);
if (isRunning)
{
ch &= ~_BV(DS1302_CH);
}
else
{
ch |= _BV(DS1302_CH);
}
setReg(DS1302_REG_CH, ch);
}
uint8_t GetTrickleChargeSettings()
{
uint8_t setting = getReg(DS1302_REG_TCR);
return setting;
}
void SetTrickleChargeSettings(uint8_t setting)
{
if ((setting & DS1302TcrResistor_MASK) == DS1302TcrResistor_Disabled)
{
// invalid resistor setting, set to disabled
setting = DS1302Tcr_Disabled;
}
else if ((setting & DS1302TcrDiodes_MASK) == DS1302TcrDiodes_Disabled ||
(setting & DS1302TcrDiodes_MASK) == DS1302TcrDiodes_None)
{
// invalid diode setting, set to disabled
setting = DS1302Tcr_Disabled;
}
else if ((setting & DS1302TcrStatus_MASK) != DS1302TcrStatus_Enabled)
{
// invalid status setting, set to disabled
setting = DS1302Tcr_Disabled;
}
setReg(DS1302_REG_TCR, setting);
}
void SetDateTime(const RtcDateTime& dt)
{
// set the date time
_wire.beginTransmission(DS1302_REG_TIMEDATE_BURST);
_wire.write(Uint8ToBcd(dt.Second()));
_wire.write(Uint8ToBcd(dt.Minute()));
_wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only
_wire.write(Uint8ToBcd(dt.Day()));
_wire.write(Uint8ToBcd(dt.Month()));
// RTC Hardware Day of Week is 1-7, 1 = Monday
// convert our Day of Week to Rtc Day of Week
uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());
_wire.write(Uint8ToBcd(rtcDow));
_wire.write(Uint8ToBcd(dt.Year() - 2000));
_wire.write(0); // no write protect, as all of this is ignored if it is protected
_wire.endTransmission();
}
RtcDateTime GetDateTime()
{
_wire.beginTransmission(DS1302_REG_TIMEDATE_BURST | THREEWIRE_READFLAG);
uint8_t second = BcdToUint8(_wire.read() & 0x7F);
uint8_t minute = BcdToUint8(_wire.read());
uint8_t hour = BcdToBin24Hour(_wire.read());
uint8_t dayOfMonth = BcdToUint8(_wire.read());
uint8_t month = BcdToUint8(_wire.read());
_wire.read(); // throwing away day of week as we calculate it
uint16_t year = BcdToUint8(_wire.read()) + 2000;
_wire.read(); // throwing away write protect flag
_wire.endTransmission();
return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
}
void SetMemory(uint8_t memoryAddress, uint8_t value)
{
// memory addresses interleaved read and write addresses
// so we need to calculate the offset
uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART;
if (address <= DS1302_REG_RAMEND)
{
setReg(address, value);
}
}
uint8_t GetMemory(uint8_t memoryAddress)
{
uint8_t value = 0;
// memory addresses interleaved read and write addresses
// so we need to calculate the offset
uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART;
if (address <= DS1302_REG_RAMEND)
{
value = getReg(address);
}
return value;
}
uint8_t SetMemory(const uint8_t* pValue, uint8_t countBytes)
{
uint8_t countWritten = 0;
_wire.beginTransmission(DS1302_REG_RAM_BURST);
while (countBytes > 0 && countWritten < DS1302RamSize)
{
_wire.write(*pValue++);
countBytes--;
countWritten++;
}
_wire.endTransmission();
return countWritten;
}
uint8_t GetMemory(uint8_t* pValue, uint8_t countBytes)
{
uint8_t countRead = 0;
_wire.beginTransmission(DS1302_REG_RAM_BURST | THREEWIRE_READFLAG);
while (countBytes > 0 && countRead < DS1302RamSize)
{
*pValue++ = _wire.read();
countRead++;
countBytes--;
}
_wire.endTransmission();
return countRead;
}
private:
T_WIRE_METHOD& _wire;
uint8_t getReg(uint8_t regAddress)
{
_wire.beginTransmission(regAddress | THREEWIRE_READFLAG);
uint8_t regValue = _wire.read();
_wire.endTransmission();
return regValue;
}
void setReg(uint8_t regAddress, uint8_t regValue)
{
_wire.beginTransmission(regAddress);
_wire.write(regValue);
_wire.endTransmission();
}
};

View File

@@ -0,0 +1,284 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
#include "RtcUtility.h"
#include "RtcDateTime.h"
//I2C Slave Address
const uint8_t DS1307_ADDRESS = 0x68;
//DS1307 Register Addresses
const uint8_t DS1307_REG_TIMEDATE = 0x00;
const uint8_t DS1307_REG_STATUS = 0x00;
const uint8_t DS1307_REG_CONTROL = 0x07;
const uint8_t DS1307_REG_RAMSTART = 0x08;
const uint8_t DS1307_REG_RAMEND = 0x3f;
const uint8_t DS1307_REG_RAMSIZE = DS1307_REG_RAMEND - DS1307_REG_RAMSTART;
//DS1307 Register Data Size if not just 1
const size_t DS1307_REG_TIMEDATE_SIZE = 7;
// DS1307 Control Register Bits
const uint8_t DS1307_RS0 = 0;
const uint8_t DS1307_RS1 = 1;
const uint8_t DS1307_SQWE = 4;
const uint8_t DS1307_OUT = 7;
// DS1307 Status Register Bits
const uint8_t DS1307_CH = 7;
enum DS1307SquareWaveOut
{
DS1307SquareWaveOut_1Hz = 0b00010000,
DS1307SquareWaveOut_4kHz = 0b00010001,
DS1307SquareWaveOut_8kHz = 0b00010010,
DS1307SquareWaveOut_32kHz = 0b00010011,
DS1307SquareWaveOut_High = 0b10000000,
DS1307SquareWaveOut_Low = 0b00000000,
};
template<class T_WIRE_METHOD> class RtcDS1307
{
public:
RtcDS1307(T_WIRE_METHOD& wire) :
_wire(wire),
_lastError(Rtc_Wire_Error_None)
{
}
void Begin()
{
_wire.begin();
}
void Begin(int sda, int scl)
{
_wire.begin(sda, scl);
}
uint8_t LastError()
{
return _lastError;
}
bool IsDateTimeValid()
{
return GetIsRunning();
}
bool GetIsRunning()
{
uint8_t sreg = getReg(DS1307_REG_STATUS);
return (!(sreg & _BV(DS1307_CH)) && (_lastError == Rtc_Wire_Error_None));
}
void SetIsRunning(bool isRunning)
{
uint8_t sreg = getReg(DS1307_REG_STATUS);
if (isRunning)
{
sreg &= ~_BV(DS1307_CH);
}
else
{
sreg |= _BV(DS1307_CH);
}
setReg(DS1307_REG_STATUS, sreg);
}
void SetDateTime(const RtcDateTime& dt)
{
// retain running state
uint8_t sreg = getReg(DS1307_REG_STATUS) & _BV(DS1307_CH);
// set the date time
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(DS1307_REG_TIMEDATE);
_wire.write(Uint8ToBcd(dt.Second()) | sreg);
_wire.write(Uint8ToBcd(dt.Minute()));
_wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only
// RTC Hardware Day of Week is 1-7, 1 = Monday
// convert our Day of Week to Rtc Day of Week
uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());
_wire.write(Uint8ToBcd(rtcDow));
_wire.write(Uint8ToBcd(dt.Day()));
_wire.write(Uint8ToBcd(dt.Month()));
_wire.write(Uint8ToBcd(dt.Year() - 2000));
_lastError = _wire.endTransmission();
}
RtcDateTime GetDateTime()
{
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(DS1307_REG_TIMEDATE);
_lastError = _wire.endTransmission();
if (_lastError != Rtc_Wire_Error_None)
{
RtcDateTime(0);
}
size_t bytesRead = _wire.requestFrom(DS1307_ADDRESS, DS1307_REG_TIMEDATE_SIZE);
if (DS1307_REG_TIMEDATE_SIZE != bytesRead)
{
_lastError = Rtc_Wire_Error_Unspecific;
RtcDateTime(0);
}
uint8_t second = BcdToUint8(_wire.read() & 0x7F);
uint8_t minute = BcdToUint8(_wire.read());
uint8_t hour = BcdToBin24Hour(_wire.read());
_wire.read(); // throwing away day of week as we calculate it
uint8_t dayOfMonth = BcdToUint8(_wire.read());
uint8_t month = BcdToUint8(_wire.read());
uint16_t year = BcdToUint8(_wire.read()) + 2000;
return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
}
void SetMemory(uint8_t memoryAddress, uint8_t value)
{
uint8_t address = memoryAddress + DS1307_REG_RAMSTART;
if (address <= DS1307_REG_RAMEND)
{
setReg(address, value);
}
}
uint8_t GetMemory(uint8_t memoryAddress)
{
uint8_t value = 0;
uint8_t address = memoryAddress + DS1307_REG_RAMSTART;
if (address <= DS1307_REG_RAMEND)
{
value = getReg(address);
}
return value;
}
uint8_t SetMemory(uint8_t memoryAddress, const uint8_t* pValue, uint8_t countBytes)
{
uint8_t address = memoryAddress + DS1307_REG_RAMSTART;
uint8_t countWritten = 0;
if (address <= DS1307_REG_RAMEND)
{
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(address);
while (countBytes > 0 && address <= DS1307_REG_RAMEND)
{
_wire.write(*pValue++);
address++;
countBytes--;
countWritten++;
}
_lastError = _wire.endTransmission();
}
return countWritten;
}
size_t GetMemory(uint8_t memoryAddress, uint8_t* pValue, size_t countBytes)
{
uint8_t address = memoryAddress + DS1307_REG_RAMSTART;
size_t countRead = 0;
if (address <= DS1307_REG_RAMEND)
{
if (countBytes > DS1307_REG_RAMSIZE)
{
countBytes = DS1307_REG_RAMSIZE;
}
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(address);
_lastError = _wire.endTransmission();
if (_lastError != Rtc_Wire_Error_None)
{
return 0;
}
countRead = _wire.requestFrom(DS1307_ADDRESS, countBytes);
countBytes = countRead;
while (countBytes-- > 0)
{
*pValue++ = _wire.read();
}
}
return countRead;
}
void SetSquareWavePin(DS1307SquareWaveOut pinMode)
{
setReg(DS1307_REG_CONTROL, pinMode);
}
private:
T_WIRE_METHOD& _wire;
uint8_t _lastError;
uint8_t getReg(uint8_t regAddress)
{
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(regAddress);
_lastError = _wire.endTransmission();
if (_lastError != Rtc_Wire_Error_None)
{
return 0;
}
// control register
size_t bytesRead = _wire.requestFrom(DS1307_ADDRESS, (size_t)1);
if (1 != bytesRead)
{
_lastError = Rtc_Wire_Error_Unspecific;
return 0;
}
uint8_t regValue = _wire.read();
return regValue;
}
void setReg(uint8_t regAddress, uint8_t regValue)
{
_wire.beginTransmission(DS1307_ADDRESS);
_wire.write(regAddress);
_wire.write(regValue);
_lastError = _wire.endTransmission();
}
};

View File

@@ -0,0 +1,683 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
#include "RtcUtility.h"
#include "RtcDateTime.h"
#include "RtcTemperature.h"
//I2C Slave Address
const uint8_t DS3231_ADDRESS = 0x68;
//DS3231 Register Addresses
const uint8_t DS3231_REG_TIMEDATE = 0x00;
const uint8_t DS3231_REG_ALARMONE = 0x07;
const uint8_t DS3231_REG_ALARMTWO = 0x0B;
const uint8_t DS3231_REG_CONTROL = 0x0E;
const uint8_t DS3231_REG_STATUS = 0x0F;
const uint8_t DS3231_REG_AGING = 0x10;
const uint8_t DS3231_REG_TEMP = 0x11;
//DS3231 Register Data Size if not just 1
const size_t DS3231_REG_TIMEDATE_SIZE = 7;
const size_t DS3231_REG_ALARMONE_SIZE = 4;
const size_t DS3231_REG_ALARMTWO_SIZE = 3;
const size_t DS3231_REG_TEMP_SIZE = 2;
// DS3231 Control Register Bits
const uint8_t DS3231_A1IE = 0;
const uint8_t DS3231_A2IE = 1;
const uint8_t DS3231_INTCN = 2;
const uint8_t DS3231_RS1 = 3;
const uint8_t DS3231_RS2 = 4;
const uint8_t DS3231_CONV = 5;
const uint8_t DS3231_BBSQW = 6;
const uint8_t DS3231_EOSC = 7;
const uint8_t DS3231_AIEMASK = (_BV(DS3231_A1IE) | _BV(DS3231_A2IE));
const uint8_t DS3231_RSMASK = (_BV(DS3231_RS1) | _BV(DS3231_RS2));
// DS3231 Status Register Bits
const uint8_t DS3231_A1F = 0;
const uint8_t DS3231_A2F = 1;
const uint8_t DS3231_BSY = 2;
const uint8_t DS3231_EN32KHZ = 3;
const uint8_t DS3231_OSF = 7;
const uint8_t DS3231_AIFMASK = (_BV(DS3231_A1F) | _BV(DS3231_A2F));
// seconds accuracy
enum DS3231AlarmOneControl
{
// bit order: A1M4 DY/DT A1M3 A1M2 A1M1
DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch = 0x00,
DS3231AlarmOneControl_OncePerSecond = 0x17,
DS3231AlarmOneControl_SecondsMatch = 0x16,
DS3231AlarmOneControl_MinutesSecondsMatch = 0x14,
DS3231AlarmOneControl_HoursMinutesSecondsMatch = 0x10,
DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch = 0x08,
};
class DS3231AlarmOne
{
public:
DS3231AlarmOne( uint8_t dayOf,
uint8_t hour,
uint8_t minute,
uint8_t second,
DS3231AlarmOneControl controlFlags) :
_flags(controlFlags),
_dayOf(dayOf),
_hour(hour),
_minute(minute),
_second(second)
{
}
uint8_t DayOf() const
{
return _dayOf;
}
uint8_t Hour() const
{
return _hour;
}
uint8_t Minute() const
{
return _minute;
}
uint8_t Second() const
{
return _second;
}
DS3231AlarmOneControl ControlFlags() const
{
return _flags;
}
bool operator == (const DS3231AlarmOne& other) const
{
return (_dayOf == other._dayOf &&
_hour == other._hour &&
_minute == other._minute &&
_second == other._second &&
_flags == other._flags);
}
bool operator != (const DS3231AlarmOne& other) const
{
return !(*this == other);
}
protected:
DS3231AlarmOneControl _flags;
uint8_t _dayOf;
uint8_t _hour;
uint8_t _minute;
uint8_t _second;
};
// minutes accuracy
enum DS3231AlarmTwoControl
{
// bit order: A2M4 DY/DT A2M3 A2M2
DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch = 0x00,
DS3231AlarmTwoControl_OncePerMinute = 0x0b,
DS3231AlarmTwoControl_MinutesMatch = 0x0a,
DS3231AlarmTwoControl_HoursMinutesMatch = 0x08,
DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch = 0x04,
};
class DS3231AlarmTwo
{
public:
DS3231AlarmTwo( uint8_t dayOf,
uint8_t hour,
uint8_t minute,
DS3231AlarmTwoControl controlFlags) :
_flags(controlFlags),
_dayOf(dayOf),
_hour(hour),
_minute(minute)
{
}
uint8_t DayOf() const
{
return _dayOf;
}
uint8_t Hour() const
{
return _hour;
}
uint8_t Minute() const
{
return _minute;
}
DS3231AlarmTwoControl ControlFlags() const
{
return _flags;
}
bool operator == (const DS3231AlarmTwo& other) const
{
return (_dayOf == other._dayOf &&
_hour == other._hour &&
_minute == other._minute &&
_flags == other._flags);
}
bool operator != (const DS3231AlarmTwo& other) const
{
return !(*this == other);
}
protected:
DS3231AlarmTwoControl _flags;
uint8_t _dayOf;
uint8_t _hour;
uint8_t _minute;
};
enum DS3231SquareWaveClock
{
DS3231SquareWaveClock_1Hz = 0b00000000,
DS3231SquareWaveClock_1kHz = 0b00001000,
DS3231SquareWaveClock_4kHz = 0b00010000,
DS3231SquareWaveClock_8kHz = 0b00011000,
};
enum DS3231SquareWavePinMode
{
DS3231SquareWavePin_ModeNone,
DS3231SquareWavePin_ModeAlarmOne,
DS3231SquareWavePin_ModeAlarmTwo,
// note: the same as DS3231SquareWavePin_ModeAlarmOne | DS3231SquareWavePin_ModeAlarmTwo
DS3231SquareWavePin_ModeAlarmBoth,
DS3231SquareWavePin_ModeClock
};
enum DS3231AlarmFlag
{
DS3231AlarmFlag_Alarm1 = 0x01,
DS3231AlarmFlag_Alarm2 = 0x02,
DS3231AlarmFlag_AlarmBoth = 0x03,
};
template<class T_WIRE_METHOD> class RtcDS3231
{
public:
RtcDS3231(T_WIRE_METHOD& wire) :
_wire(wire),
_lastError(Rtc_Wire_Error_None)
{
}
void Begin()
{
_wire.begin();
}
void Begin(int sda, int scl)
{
_wire.begin(sda, scl);
}
uint8_t LastError()
{
return _lastError;
}
bool IsDateTimeValid()
{
uint8_t status = getReg(DS3231_REG_STATUS);
return (!(status & _BV(DS3231_OSF)) && (_lastError == Rtc_Wire_Error_None));
}
bool GetIsRunning()
{
uint8_t creg = getReg(DS3231_REG_CONTROL);
return (!(creg & _BV(DS3231_EOSC)) && (_lastError == Rtc_Wire_Error_None));
}
void SetIsRunning(bool isRunning)
{
uint8_t creg = getReg(DS3231_REG_CONTROL);
if (isRunning)
{
creg &= ~_BV(DS3231_EOSC);
}
else
{
creg |= _BV(DS3231_EOSC);
}
setReg(DS3231_REG_CONTROL, creg);
}
void SetDateTime(const RtcDateTime& dt)
{
// clear the invalid flag
uint8_t status = getReg(DS3231_REG_STATUS);
status &= ~_BV(DS3231_OSF); // clear the flag
setReg(DS3231_REG_STATUS, status);
// set the date time
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_TIMEDATE);
_wire.write(Uint8ToBcd(dt.Second()));
_wire.write(Uint8ToBcd(dt.Minute()));
_wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only
uint8_t year = dt.Year() - 2000;
uint8_t centuryFlag = 0;
if (year >= 100)
{
year -= 100;
centuryFlag = _BV(7);
}
// RTC Hardware Day of Week is 1-7, 1 = Monday
// convert our Day of Week to Rtc Day of Week
uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());
_wire.write(Uint8ToBcd(rtcDow));
_wire.write(Uint8ToBcd(dt.Day()));
_wire.write(Uint8ToBcd(dt.Month()) | centuryFlag);
_wire.write(Uint8ToBcd(year));
_lastError = _wire.endTransmission();
}
RtcDateTime GetDateTime()
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_TIMEDATE);
_lastError = _wire.endTransmission();
if (_lastError != Rtc_Wire_Error_None)
{
return RtcDateTime(0);
}
size_t bytesRead = _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_TIMEDATE_SIZE);
if (DS3231_REG_TIMEDATE_SIZE != bytesRead)
{
_lastError = Rtc_Wire_Error_Unspecific;
return RtcDateTime(0);
}
uint8_t second = BcdToUint8(_wire.read() & 0x7F);
uint8_t minute = BcdToUint8(_wire.read());
uint8_t hour = BcdToBin24Hour(_wire.read());
_wire.read(); // throwing away day of week as we calculate it
uint8_t dayOfMonth = BcdToUint8(_wire.read());
uint8_t monthRaw = _wire.read();
uint16_t year = BcdToUint8(_wire.read()) + 2000;
if (monthRaw & _BV(7)) // century wrap flag
{
year += 100;
}
uint8_t month = BcdToUint8(monthRaw & 0x7f);
return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
}
RtcTemperature GetTemperature()
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_TEMP);
_lastError = _wire.endTransmission();
if (_lastError != Rtc_Wire_Error_None)
{
return RtcTemperature(0);
}
// Temperature is represented as a 10-bit code with a resolution
// of 1/4th °C and is accessable as a signed 16-bit integer at
// locations 11h and 12h.
//
// | r11h | DP | r12h |
// Bit: 15 14 13 12 11 10 9 8 . 7 6 5 4 3 2 1 0 -1 -2
// s i i i i i i i . f f 0 0 0 0 0 0
//
// As it takes (8) right-shifts to register the decimal point (DP) to
// the right of the 0th bit, the overall word scaling equals 256.
//
// For example, at +/- 25.25°C, concatenated registers <r11h:r12h> =
// 256 * (+/- 25+(1/4)) = +/- 6464, or 1940h / E6C0h.
size_t bytesRead = _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_TEMP_SIZE);
if (DS3231_REG_TEMP_SIZE != bytesRead)
{
_lastError = Rtc_Wire_Error_Unspecific;
return RtcTemperature(0);
}
int8_t r11h = _wire.read(); // MS byte, signed temperature
return RtcTemperature( r11h, _wire.read() ); // LS byte is r12h
}
void Enable32kHzPin(bool enable)
{
uint8_t sreg = getReg(DS3231_REG_STATUS);
if (enable == true)
{
sreg |= _BV(DS3231_EN32KHZ);
}
else
{
sreg &= ~_BV(DS3231_EN32KHZ);
}
setReg(DS3231_REG_STATUS, sreg);
}
void SetSquareWavePin(DS3231SquareWavePinMode pinMode, bool enableWhileInBatteryBackup = true)
{
uint8_t creg = getReg(DS3231_REG_CONTROL);
// clear all relevant bits to a known "off" state
creg &= ~(DS3231_AIEMASK | _BV(DS3231_BBSQW));
creg |= _BV(DS3231_INTCN); // set INTCN to disables clock SQW
if (pinMode != DS3231SquareWavePin_ModeNone)
{
if (pinMode == DS3231SquareWavePin_ModeClock)
{
creg &= ~_BV(DS3231_INTCN); // clear INTCN to enable clock SQW
}
else
{
if (pinMode & DS3231SquareWavePin_ModeAlarmOne)
{
creg |= _BV(DS3231_A1IE);
}
if (pinMode & DS3231SquareWavePin_ModeAlarmTwo)
{
creg |= _BV(DS3231_A2IE);
}
}
if (enableWhileInBatteryBackup)
{
creg |= _BV(DS3231_BBSQW); // set enable int/sqw while in battery backup flag
}
}
setReg(DS3231_REG_CONTROL, creg);
}
void SetSquareWavePinClockFrequency(DS3231SquareWaveClock freq)
{
uint8_t creg = getReg(DS3231_REG_CONTROL);
creg &= ~DS3231_RSMASK; // Set to 0
creg |= (freq & DS3231_RSMASK); // Set freq bits
setReg(DS3231_REG_CONTROL, creg);
}
void SetAlarmOne(const DS3231AlarmOne& alarm)
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_ALARMONE);
_wire.write(Uint8ToBcd(alarm.Second()) | ((alarm.ControlFlags() & 0x01) << 7));
_wire.write(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x02) << 6));
_wire.write(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x04) << 5)); // 24 hour mode only
uint8_t rtcDow = alarm.DayOf();
if (alarm.ControlFlags() == DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch)
{
rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow);
}
_wire.write(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x18) << 3));
_lastError = _wire.endTransmission();
}
void SetAlarmTwo(const DS3231AlarmTwo& alarm)
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_ALARMTWO);
_wire.write(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x01) << 7));
_wire.write(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x02) << 6)); // 24 hour mode only
// convert our Day of Week to Rtc Day of Week if needed
uint8_t rtcDow = alarm.DayOf();
if (alarm.ControlFlags() == DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch)
{
rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow);
}
_wire.write(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x0c) << 4));
_lastError = _wire.endTransmission();
}
DS3231AlarmOne GetAlarmOne()
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_ALARMONE);
_lastError = _wire.endTransmission();
if (_lastError != Rtc_Wire_Error_None)
{
return DS3231AlarmOne(0, 0, 0, 0, DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch);
}
size_t bytesRead = _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_ALARMONE_SIZE);
if (DS3231_REG_ALARMONE_SIZE != bytesRead)
{
_lastError = Rtc_Wire_Error_Unspecific;
return DS3231AlarmOne(0, 0, 0, 0, DS3231AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch);
}
uint8_t raw = _wire.read();
uint8_t flags = (raw & 0x80) >> 7;
uint8_t second = BcdToUint8(raw & 0x7F);
raw = _wire.read();
flags |= (raw & 0x80) >> 6;
uint8_t minute = BcdToUint8(raw & 0x7F);
raw = _wire.read();
flags |= (raw & 0x80) >> 5;
uint8_t hour = BcdToBin24Hour(raw & 0x7f);
raw = _wire.read();
flags |= (raw & 0xc0) >> 3;
uint8_t dayOf = BcdToUint8(raw & 0x3f);
if (flags == DS3231AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch)
{
dayOf = RtcDateTime::ConvertRtcToDow(dayOf);
}
return DS3231AlarmOne(dayOf, hour, minute, second, (DS3231AlarmOneControl)flags);
}
DS3231AlarmTwo GetAlarmTwo()
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(DS3231_REG_ALARMTWO);
_lastError = _wire.endTransmission();
if (_lastError != Rtc_Wire_Error_None)
{
return DS3231AlarmTwo(0, 0, 0, DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch);
}
size_t bytesRead = _wire.requestFrom(DS3231_ADDRESS, DS3231_REG_ALARMTWO_SIZE);
if (DS3231_REG_ALARMTWO_SIZE != bytesRead)
{
_lastError = Rtc_Wire_Error_Unspecific;
return DS3231AlarmTwo(0, 0, 0, DS3231AlarmTwoControl_HoursMinutesDayOfMonthMatch);
}
uint8_t raw = _wire.read();
uint8_t flags = (raw & 0x80) >> 7;
uint8_t minute = BcdToUint8(raw & 0x7F);
raw = _wire.read();
flags |= (raw & 0x80) >> 6;
uint8_t hour = BcdToBin24Hour(raw & 0x7f);
raw = _wire.read();
flags |= (raw & 0xc0) >> 4;
uint8_t dayOf = BcdToUint8(raw & 0x3f);
if (flags == DS3231AlarmTwoControl_HoursMinutesDayOfWeekMatch)
{
dayOf = RtcDateTime::ConvertRtcToDow(dayOf);
}
return DS3231AlarmTwo(dayOf, hour, minute, (DS3231AlarmTwoControl)flags);
}
// Latch must be called after an alarm otherwise it will not
// trigger again
DS3231AlarmFlag LatchAlarmsTriggeredFlags()
{
uint8_t sreg = getReg(DS3231_REG_STATUS);
uint8_t alarmFlags = (sreg & DS3231_AIFMASK);
sreg &= ~DS3231_AIFMASK; // clear the flags
setReg(DS3231_REG_STATUS, sreg);
return (DS3231AlarmFlag)alarmFlags;
}
DS3231AlarmFlag LatchAlarmOneFlag()
{
uint8_t sreg = getReg(DS3231_REG_STATUS);
uint8_t alarmFlags = (sreg & _BV(DS3231_A1F));
sreg &= ~_BV(DS3231_A1F); // clear alarm flag 1
setReg(DS3231_REG_STATUS, sreg);
return (DS3231AlarmFlag)alarmFlags;
}
DS3231AlarmFlag LatchAlarmTwoFlag()
{
uint8_t sreg = getReg(DS3231_REG_STATUS);
uint8_t alarmFlags = (sreg & _BV(DS3231_A2F));
sreg &= ~_BV(DS3231_A2F); // clear alarm flag 2
setReg(DS3231_REG_STATUS, sreg);
return (DS3231AlarmFlag)alarmFlags;
}
bool GetAlarmOneTriggered()
{
uint8_t sreg = getReg(DS3231_REG_STATUS);
return (sreg & _BV(DS3231_A1F));
}
bool GetAlarmTwoTriggered()
{
uint8_t sreg = getReg(DS3231_REG_STATUS);
return (sreg & _BV(DS3231_A2F));
}
void ForceTemperatureCompensationUpdate(bool block)
{
uint8_t creg = getReg(DS3231_REG_CONTROL);
creg |= _BV(DS3231_CONV); // Write CONV bit
setReg(DS3231_REG_CONTROL, creg);
while (block && (creg & _BV(DS3231_CONV)) != 0)
{
// Block until CONV is 0
creg = getReg(DS3231_REG_CONTROL);
}
}
int8_t GetAgingOffset()
{
return getReg(DS3231_REG_AGING);
}
void SetAgingOffset(int8_t value)
{
setReg(DS3231_REG_AGING, value);
}
protected:
T_WIRE_METHOD& _wire;
uint8_t _lastError;
uint8_t getReg(uint8_t regAddress)
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(regAddress);
_lastError = _wire.endTransmission();
if (_lastError != Rtc_Wire_Error_None)
{
return 0;
}
// control register
size_t bytesRead = _wire.requestFrom(DS3231_ADDRESS, (uint8_t)1);
if (1 != bytesRead)
{
_lastError = Rtc_Wire_Error_Unspecific;
return 0;
}
uint8_t regValue = _wire.read();
return regValue;
}
void setReg(uint8_t regAddress, uint8_t regValue)
{
_wire.beginTransmission(DS3231_ADDRESS);
_wire.write(regAddress);
_wire.write(regValue);
_lastError = _wire.endTransmission();
}
};

View File

@@ -0,0 +1,124 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include "RtcDS3231.h"
// DS3232 is the same as the DS3231 except it has SRAM
//
//
//I2C Slave Address
const uint8_t DS3232_ADDRESS = DS3231_ADDRESS;
const uint8_t DS3232_REG_SRAMFIRST = 0x14;
const uint8_t DS3232_REG_SRAMLAST = 0xff;
const uint8_t DS3232_REG_SRAMSIZE = DS3232_REG_SRAMLAST - DS3232_REG_SRAMFIRST + 1;
template<class T_WIRE_METHOD> class RtcDS3232 :
public RtcDS3231<T_WIRE_METHOD>
{
public:
RtcDS3232(T_WIRE_METHOD& wire) :
RtcDS3231<T_WIRE_METHOD>( wire )
{
}
void SetMemory(uint8_t memoryAddress, uint8_t value)
{
uint8_t address = memoryAddress + DS3232_REG_SRAMFIRST;
if (address <= DS3232_REG_SRAMLAST)
{
this->setReg(address, value);
}
}
uint8_t GetMemory(uint8_t memoryAddress)
{
uint8_t value = 0;
uint8_t address = memoryAddress + DS3232_REG_SRAMFIRST;
if (address <= DS3232_REG_SRAMLAST)
{
value = this->getReg(address);
}
return value;
}
uint8_t SetMemory(uint8_t memoryAddress, const uint8_t* pValue, uint8_t countBytes)
{
uint8_t address = memoryAddress + DS3232_REG_SRAMFIRST;
uint8_t countWritten = 0;
if (address <= DS3232_REG_SRAMLAST)
{
this->_wire.beginTransmission(DS3232_ADDRESS);
this->_wire.write(address);
while (countBytes > 0 && address <= DS3232_REG_SRAMLAST)
{
this->_wire.write(*pValue++);
address++;
countBytes--;
countWritten++;
}
this->_lastError = this->_wire.endTransmission();
}
return countWritten;
}
size_t GetMemory(uint8_t memoryAddress, uint8_t* pValue, size_t countBytes)
{
uint8_t address = memoryAddress + DS3232_REG_SRAMFIRST;
size_t countRead = 0;
if (address <= DS3232_REG_SRAMLAST)
{
if (countBytes > DS3232_REG_SRAMSIZE)
{
countBytes = DS3232_REG_SRAMSIZE;
}
this->_wire.beginTransmission(DS3232_ADDRESS);
this->_wire.write(address);
this->_lastError = this->_wire.endTransmission();
if (this->_lastError != Rtc_Wire_Error_None)
{
return 0;
}
countRead = this->_wire.requestFrom(DS3232_ADDRESS, countBytes);
countBytes = countRead;
while (countBytes-- > 0)
{
*pValue++ = this->_wire.read();
}
}
return countRead;
}
};

View File

@@ -0,0 +1,721 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
#include <SPI.h>
#include "RtcUtility.h"
#include "RtcDateTime.h"
#include "RtcTemperature.h"
//DS3234 Register Addresses
const uint8_t DS3234_REG_WRITE_FLAG = 0x80;
const uint8_t DS3234_REG_TIMEDATE = 0x00;
const uint8_t DS3234_REG_ALARMONE = 0x07;
const uint8_t DS3234_REG_ALARMTWO = 0x0B;
const uint8_t DS3234_REG_CONTROL = 0x0E;
const uint8_t DS3234_REG_STATUS = 0x0F;
const uint8_t DS3234_REG_AGING = 0x10;
const uint8_t DS3234_REG_TEMP = 0x11;
const uint8_t DS3234_REG_RAM_ADDRESS = 0x18;
const uint8_t DS3234_REG_RAM_DATA = 0x19;
const uint8_t DS3234_RAMSTART = 0x00;
const uint8_t DS3234_RAMEND = 0xFF;
const uint8_t DS3234_RAMSIZE = DS3234_RAMEND - DS3234_RAMSTART;
// DS3234 Control Register Bits
const uint8_t DS3234_A1IE = 0;
const uint8_t DS3234_A2IE = 1;
const uint8_t DS3234_INTCN = 2;
const uint8_t DS3234_RS1 = 3;
const uint8_t DS3234_RS2 = 4;
const uint8_t DS3234_CONV = 5;
const uint8_t DS3234_BBSQW = 6;
const uint8_t DS3234_EOSC = 7;
const uint8_t DS3234_AIEMASK = (_BV(DS3234_A1IE) | _BV(DS3234_A2IE));
const uint8_t DS3234_RSMASK = (_BV(DS3234_RS1) | _BV(DS3234_RS2));
// DS3234 Status Register Bits
const uint8_t DS3234_A1F = 0;
const uint8_t DS3234_A2F = 1;
const uint8_t DS3234_BSY = 2;
const uint8_t DS3234_EN32KHZ = 3;
const uint8_t DS3234_CRATE0 = 4;
const uint8_t DS3234_CRATE1 = 5;
const uint8_t DS3234_BB32KHZ = 6;
const uint8_t DS3234_OSF = 7;
const uint8_t DS3234_AIFMASK = (_BV(DS3234_A1F) | _BV(DS3234_A2F));
const uint8_t DS3234_CRATEMASK = (_BV(DS3234_CRATE0) | _BV(DS3234_CRATE1));
// seconds accuracy
enum DS3234AlarmOneControl
{
// bit order: A1M4 DY/DT A1M3 A1M2 A1M1
DS3234AlarmOneControl_HoursMinutesSecondsDayOfMonthMatch = 0x00,
DS3234AlarmOneControl_OncePerSecond = 0x17,
DS3234AlarmOneControl_SecondsMatch = 0x16,
DS3234AlarmOneControl_MinutesSecondsMatch = 0x14,
DS3234AlarmOneControl_HoursMinutesSecondsMatch = 0x10,
DS3234AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch = 0x08,
};
class DS3234AlarmOne
{
public:
DS3234AlarmOne( uint8_t dayOf,
uint8_t hour,
uint8_t minute,
uint8_t second,
DS3234AlarmOneControl controlFlags) :
_flags(controlFlags),
_dayOf(dayOf),
_hour(hour),
_minute(minute),
_second(second)
{
}
uint8_t DayOf() const
{
return _dayOf;
}
uint8_t Hour() const
{
return _hour;
}
uint8_t Minute() const
{
return _minute;
}
uint8_t Second() const
{
return _second;
}
DS3234AlarmOneControl ControlFlags() const
{
return _flags;
}
bool operator == (const DS3234AlarmOne& other) const
{
return (_dayOf == other._dayOf &&
_hour == other._hour &&
_minute == other._minute &&
_second == other._second &&
_flags == other._flags);
}
bool operator != (const DS3234AlarmOne& other) const
{
return !(*this == other);
}
protected:
DS3234AlarmOneControl _flags;
uint8_t _dayOf;
uint8_t _hour;
uint8_t _minute;
uint8_t _second;
};
// minutes accuracy
enum DS3234AlarmTwoControl
{
// bit order: A2M4 DY/DT A2M3 A2M2
DS3234AlarmTwoControl_HoursMinutesDayOfMonthMatch = 0x00,
DS3234AlarmTwoControl_OncePerMinute = 0x0b,
DS3234AlarmTwoControl_MinutesMatch = 0x0a,
DS3234AlarmTwoControl_HoursMinutesMatch = 0x08,
DS3234AlarmTwoControl_HoursMinutesDayOfWeekMatch = 0x04,
};
class DS3234AlarmTwo
{
public:
DS3234AlarmTwo( uint8_t dayOf,
uint8_t hour,
uint8_t minute,
DS3234AlarmTwoControl controlFlags) :
_flags(controlFlags),
_dayOf(dayOf),
_hour(hour),
_minute(minute)
{
}
uint8_t DayOf() const
{
return _dayOf;
}
uint8_t Hour() const
{
return _hour;
}
uint8_t Minute() const
{
return _minute;
}
DS3234AlarmTwoControl ControlFlags() const
{
return _flags;
}
bool operator == (const DS3234AlarmTwo& other) const
{
return (_dayOf == other._dayOf &&
_hour == other._hour &&
_minute == other._minute &&
_flags == other._flags);
}
bool operator != (const DS3234AlarmTwo& other) const
{
return !(*this == other);
}
protected:
DS3234AlarmTwoControl _flags;
uint8_t _dayOf;
uint8_t _hour;
uint8_t _minute;
};
enum DS3234SquareWaveClock
{
DS3234SquareWaveClock_1Hz = 0b00000000,
DS3234SquareWaveClock_1kHz = 0b00001000,
DS3234SquareWaveClock_4kHz = 0b00010000,
DS3234SquareWaveClock_8kHz = 0b00011000,
};
enum DS3234SquareWavePinMode
{
DS3234SquareWavePin_ModeNone,
DS3234SquareWavePin_ModeBatteryBackup,
DS3234SquareWavePin_ModeClock,
DS3234SquareWavePin_ModeAlarmOne,
DS3234SquareWavePin_ModeAlarmTwo,
DS3234SquareWavePin_ModeAlarmBoth
};
enum DS3234TempCompensationRate
{
DS3234TempCompensationRate_64Seconds,
DS3234TempCompensationRate_128Seconds,
DS3234TempCompensationRate_256Seconds,
DS3234TempCompensationRate_512Seconds,
};
enum DS3234AlarmFlag
{
DS3234AlarmFlag_Alarm1 = 0x01,
DS3234AlarmFlag_Alarm2 = 0x02,
DS3234AlarmFlag_AlarmBoth = 0x03,
};
const SPISettings c_Ds3234SpiSettings(1000000, MSBFIRST, SPI_MODE1); // CPHA must be used, so mode 1 or mode 3 are valid
template<class T_SPI_METHOD> class RtcDS3234
{
public:
RtcDS3234(T_SPI_METHOD& spi, uint8_t csPin) :
_spi(spi),
_csPin(csPin)
{
}
void Begin()
{
UnselectChip();
pinMode(_csPin, OUTPUT);
}
bool IsDateTimeValid()
{
uint8_t status = getReg(DS3234_REG_STATUS);
return !(status & _BV(DS3234_OSF));
}
bool GetIsRunning()
{
uint8_t creg = getReg(DS3234_REG_CONTROL);
return !(creg & _BV(DS3234_EOSC));
}
void SetIsRunning(bool isRunning)
{
uint8_t creg = getReg(DS3234_REG_CONTROL);
if (isRunning)
{
creg &= ~_BV(DS3234_EOSC);
}
else
{
creg |= _BV(DS3234_EOSC);
}
setReg(DS3234_REG_CONTROL, creg);
}
void SetDateTime(const RtcDateTime& dt)
{
// clear the invalid flag
uint8_t status = getReg(DS3234_REG_STATUS);
status &= ~_BV(DS3234_OSF); // clear the flag
setReg(DS3234_REG_STATUS, status);
// set the date time
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_TIMEDATE | DS3234_REG_WRITE_FLAG);
_spi.transfer(Uint8ToBcd(dt.Second()));
_spi.transfer(Uint8ToBcd(dt.Minute()));
_spi.transfer(Uint8ToBcd(dt.Hour())); // 24 hour mode only
uint8_t year = dt.Year() - 2000;
uint8_t centuryFlag = 0;
if (year >= 100)
{
year -= 100;
centuryFlag = _BV(7);
}
// RTC Hardware Day of Week is 1-7, 1 = Monday
// convert our Day of Week to Rtc Day of Week
uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek());
_spi.transfer(Uint8ToBcd(rtcDow));
_spi.transfer(Uint8ToBcd(dt.Day()));
_spi.transfer(Uint8ToBcd(dt.Month()) | centuryFlag);
_spi.transfer(Uint8ToBcd(year));
UnselectChip();
_spi.endTransaction();
}
RtcDateTime GetDateTime()
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_TIMEDATE);
uint8_t second = BcdToUint8(_spi.transfer(0));
uint8_t minute = BcdToUint8(_spi.transfer(0));
uint8_t hour = BcdToBin24Hour(_spi.transfer(0));
_spi.transfer(0); // throwing away day of week as we calculate it
uint8_t dayOfMonth = BcdToUint8(_spi.transfer(0));
uint8_t monthRaw = _spi.transfer(0);
uint16_t year = BcdToUint8(_spi.transfer(0)) + 2000;
UnselectChip();
_spi.endTransaction();
if (monthRaw & _BV(7)) // century wrap flag
{
year += 100;
}
uint8_t month = BcdToUint8(monthRaw & 0x7f);
return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
}
RtcTemperature GetTemperature()
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_TEMP);
// Temperature is represented as a 10-bit code with a resolution
// of 1/4th °C and is accessable as a signed 16-bit integer at
// locations 11h and 12h.
//
// | r11h | DP | r12h |
// Bit: 15 14 13 12 11 10 9 8 . 7 6 5 4 3 2 1 0 -1 -2
// s i i i i i i i . f f 0 0 0 0 0 0
//
// As it takes (8) right-shifts to register the decimal point (DP) to
// the right of the 0th bit, the overall word scaling equals 256.
//
// For example, at +/- 25.25°C, concatenated registers <r11h:r12h> =
// 256 * (+/- 25+(1/4)) = +/- 6464, or 1940h / E6C0h.
int8_t ms = _spi.transfer(0); // MS byte, signed temperature
uint8_t ls = _spi.transfer(0); // LS byte is r12h
UnselectChip();
_spi.endTransaction();
return RtcTemperature(ms, ls); // LS byte is r12h
}
void Enable32kHzPin(bool enable)
{
uint8_t sreg = getReg(DS3234_REG_STATUS);
if (enable == true)
{
sreg |= _BV(DS3234_EN32KHZ);
}
else
{
sreg &= ~_BV(DS3234_EN32KHZ);
}
setReg(DS3234_REG_STATUS, sreg);
}
void SetSquareWavePin(DS3234SquareWavePinMode pinMode)
{
uint8_t creg = getReg(DS3234_REG_CONTROL);
// clear all relevant bits to a known "off" state
creg &= ~(DS3234_AIEMASK | _BV(DS3234_BBSQW));
creg |= _BV(DS3234_INTCN); // set INTCN to disables SQW
switch (pinMode)
{
case DS3234SquareWavePin_ModeNone:
break;
case DS3234SquareWavePin_ModeBatteryBackup:
creg |= _BV(DS3234_BBSQW); // set battery backup flag
creg &= ~_BV(DS3234_INTCN); // clear INTCN to enable SQW
break;
case DS3234SquareWavePin_ModeClock:
creg &= ~_BV(DS3234_INTCN); // clear INTCN to enable SQW
break;
case DS3234SquareWavePin_ModeAlarmOne:
creg |= _BV(DS3234_A1IE);
break;
case DS3234SquareWavePin_ModeAlarmTwo:
creg |= _BV(DS3234_A2IE);
break;
case DS3234SquareWavePin_ModeAlarmBoth:
creg |= _BV(DS3234_A1IE) | _BV(DS3234_A2IE);
break;
}
setReg(DS3234_REG_CONTROL, creg);
}
void SetSquareWavePinClockFrequency(DS3234SquareWaveClock freq)
{
uint8_t creg = getReg(DS3234_REG_CONTROL);
creg &= ~DS3234_RSMASK; // Set to 0
creg |= (freq & DS3234_RSMASK); // Set freq bits
setReg(DS3234_REG_CONTROL, creg);
}
void SetAlarmOne(const DS3234AlarmOne& alarm)
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_ALARMONE | DS3234_REG_WRITE_FLAG);
_spi.transfer(Uint8ToBcd(alarm.Second()) | ((alarm.ControlFlags() & 0x01) << 7));
_spi.transfer(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x02) << 6));
_spi.transfer(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x04) << 5)); // 24 hour mode only
uint8_t rtcDow = alarm.DayOf();
if (alarm.ControlFlags() == DS3234AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch)
{
rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow);
}
_spi.transfer(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x18) << 3));
UnselectChip();
_spi.endTransaction();
}
void SetAlarmTwo(const DS3234AlarmTwo& alarm)
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_ALARMTWO | DS3234_REG_WRITE_FLAG);
_spi.transfer(Uint8ToBcd(alarm.Minute()) | ((alarm.ControlFlags() & 0x01) << 7));
_spi.transfer(Uint8ToBcd(alarm.Hour()) | ((alarm.ControlFlags() & 0x02) << 6)); // 24 hour mode only
// convert our Day of Week to Rtc Day of Week if needed
uint8_t rtcDow = alarm.DayOf();
if (alarm.ControlFlags() == DS3234AlarmTwoControl_HoursMinutesDayOfWeekMatch)
{
rtcDow = RtcDateTime::ConvertDowToRtc(rtcDow);
}
_spi.transfer(Uint8ToBcd(rtcDow) | ((alarm.ControlFlags() & 0x0c) << 4));
UnselectChip();
_spi.endTransaction();
}
DS3234AlarmOne GetAlarmOne()
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_ALARMONE);
uint8_t raw = _spi.transfer(0);
uint8_t flags = (raw & 0x80) >> 7;
uint8_t second = BcdToUint8(raw & 0x7F);
raw = _spi.transfer(0);
flags |= (raw & 0x80) >> 6;
uint8_t minute = BcdToUint8(raw & 0x7F);
raw = _spi.transfer(0);
flags |= (raw & 0x80) >> 5;
uint8_t hour = BcdToBin24Hour(raw & 0x7f);
raw = _spi.transfer(0);
flags |= (raw & 0xc0) >> 3;
uint8_t dayOf = BcdToUint8(raw & 0x3f);
UnselectChip();
_spi.endTransaction();
if (flags == DS3234AlarmOneControl_HoursMinutesSecondsDayOfWeekMatch)
{
dayOf = RtcDateTime::ConvertRtcToDow(dayOf);
}
return DS3234AlarmOne(dayOf, hour, minute, second, (DS3234AlarmOneControl)flags);
}
DS3234AlarmTwo GetAlarmTwo()
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_ALARMTWO);
uint8_t raw = _spi.transfer(0);
uint8_t flags = (raw & 0x80) >> 7;
uint8_t minute = BcdToUint8(raw & 0x7F);
raw = _spi.transfer(0);
flags |= (raw & 0x80) >> 6;
uint8_t hour = BcdToBin24Hour(raw & 0x7f);
raw = _spi.transfer(0);
flags |= (raw & 0xc0) >> 4;
uint8_t dayOf = BcdToUint8(raw & 0x3f);
UnselectChip();
_spi.endTransaction();
if (flags == DS3234AlarmTwoControl_HoursMinutesDayOfWeekMatch)
{
dayOf = RtcDateTime::ConvertRtcToDow(dayOf);
}
return DS3234AlarmTwo(dayOf, hour, minute, (DS3234AlarmTwoControl)flags);
}
// Latch must be called after an alarm otherwise it will not
// trigger again
DS3234AlarmFlag LatchAlarmsTriggeredFlags()
{
uint8_t sreg = getReg(DS3234_REG_STATUS);
uint8_t alarmFlags = (sreg & DS3234_AIFMASK);
sreg &= ~DS3234_AIFMASK; // clear the flags
setReg(DS3234_REG_STATUS, sreg);
return (DS3234AlarmFlag)alarmFlags;
}
void SetTemperatureCompensationRate(DS3234TempCompensationRate rate)
{
uint8_t sreg = getReg(DS3234_REG_STATUS);
sreg &= ~DS3234_CRATEMASK;
sreg |= (rate << DS3234_CRATE0);
setReg(DS3234_REG_STATUS, sreg);
}
DS3234TempCompensationRate GetTemperatureCompensationRate()
{
uint8_t sreg = getReg(DS3234_REG_STATUS);
return (sreg & DS3234_CRATEMASK) >> DS3234_CRATE0;
}
void ForceTemperatureCompensationUpdate(bool block)
{
uint8_t creg = getReg(DS3234_REG_CONTROL);
creg |= _BV(DS3234_CONV); // Write CONV bit
setReg(DS3234_REG_CONTROL, creg);
while (block && (creg & _BV(DS3234_CONV)) != 0)
{
// Block until CONV is 0
creg = getReg(DS3234_REG_CONTROL);
}
}
int8_t GetAgingOffset()
{
return getReg(DS3234_REG_AGING);
}
void SetAgingOffset(int8_t value)
{
setReg(DS3234_REG_AGING, value);
}
void SetMemory(uint8_t memoryAddress, uint8_t value)
{
SetMemory(memoryAddress, &value, 1);
}
uint8_t GetMemory(uint8_t memoryAddress)
{
uint8_t value;
GetMemory(memoryAddress, &value, 1);
return value;
}
uint8_t SetMemory(uint8_t memoryAddress, const uint8_t* pValue, uint8_t countBytes)
{
uint8_t countWritten = 0;
setReg(DS3234_REG_RAM_ADDRESS, memoryAddress);
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_RAM_DATA | DS3234_REG_WRITE_FLAG);
while (countBytes > 0)
{
_spi.transfer(*pValue++);
countBytes--;
countWritten++;
}
UnselectChip();
_spi.endTransaction();
return countWritten;
}
uint8_t GetMemory(uint8_t memoryAddress, uint8_t* pValue, uint8_t countBytes)
{
// set address to read from
setReg(DS3234_REG_RAM_ADDRESS, memoryAddress);
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(DS3234_REG_RAM_DATA);
// read the data
uint8_t countRead = 0;
while (countBytes-- > 0)
{
*pValue++ = _spi.transfer(0);
countRead++;
}
UnselectChip();
_spi.endTransaction();
return countRead;
}
private:
T_SPI_METHOD& _spi;
uint8_t _csPin;
void SelectChip()
{
digitalWrite(_csPin, LOW);
}
void UnselectChip()
{
digitalWrite(_csPin, HIGH);
}
uint8_t getReg(uint8_t regAddress)
{
uint8_t regValue;
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(regAddress);
regValue = _spi.transfer(0);
UnselectChip();
_spi.endTransaction();
return regValue;
}
void setReg(uint8_t regAddress, uint8_t regValue)
{
_spi.beginTransaction(c_Ds3234SpiSettings);
SelectChip();
_spi.transfer(regAddress | DS3234_REG_WRITE_FLAG);
_spi.transfer(regValue);
UnselectChip();
_spi.endTransaction();
}
};

View File

@@ -0,0 +1,146 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include <Arduino.h>
#include "RtcUtility.h"
#include "RtcDateTime.h"
const uint8_t c_daysInMonth[] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };
template <typename T> T DaysSinceFirstOfYear2000(uint16_t year, uint8_t month, uint8_t dayOfMonth)
{
T days = dayOfMonth;
for (uint8_t indexMonth = 1; indexMonth < month; ++indexMonth)
{
days += pgm_read_byte(c_daysInMonth + indexMonth - 1);
}
if (month > 2 && year % 4 == 0)
{
days++;
}
return days + 365 * year + (year + 3) / 4 - 1;
}
template <typename T> T SecondsIn(T days, uint8_t hours, uint8_t minutes, uint8_t seconds)
{
return ((days * 24L + hours) * 60 + minutes) * 60 + seconds;
}
bool RtcDateTime::IsValid() const
{
// this just tests the most basic validity of the value ranges
// and valid leap years
// It does not check any time zone or daylight savings time
if ((_month > 0 && _month < 13) &&
(_dayOfMonth > 0 && _dayOfMonth < 32) &&
(_hour < 24) &&
(_minute < 60) &&
(_second < 60))
{
// days in a month tests
//
if (_month == 2)
{
if (_dayOfMonth > 29)
{
return false;
}
else if (_dayOfMonth > 28)
{
// leap day
// check year to make sure its a leap year
uint16_t year = Year();
if ((year % 4) != 0)
{
return false;
}
if ((year % 100) == 0 &&
(year % 400) != 0)
{
return false;
}
}
}
else if (_dayOfMonth == 31)
{
if ((((_month - 1) % 7) % 2) == 1)
{
return false;
}
}
return true;
}
return false;
}
uint8_t RtcDateTime::DayOfWeek() const
{
uint16_t days = DaysSinceFirstOfYear2000<uint16_t>(_yearFrom2000, _month, _dayOfMonth);
return (days + 6) % 7; // Jan 1, 2000 is a Saturday, i.e. returns 6
}
// 32-bit time; as seconds since 1/1/2000
uint32_t RtcDateTime::TotalSeconds() const
{
uint16_t days = DaysSinceFirstOfYear2000<uint16_t>(_yearFrom2000, _month, _dayOfMonth);
return SecondsIn<uint32_t>(days, _hour, _minute, _second);
}
// 64-bit time; as seconds since 1/1/2000
uint64_t RtcDateTime::TotalSeconds64() const
{
uint32_t days = DaysSinceFirstOfYear2000<uint32_t>(_yearFrom2000, _month, _dayOfMonth);
return SecondsIn<uint64_t>(days, _hour, _minute, _second);
}
// total days since 1/1/2000
uint16_t RtcDateTime::TotalDays() const
{
return DaysSinceFirstOfYear2000<uint16_t>(_yearFrom2000, _month, _dayOfMonth);
}
// return the next day that falls on the given day of week
// if this day is that day of week, it will return this day
RtcDateTime RtcDateTime::NextDayOfWeek(uint8_t dayOfWeek) const
{
uint8_t currentDayOfWeek = DayOfWeek();
RtcDateTime result;
int8_t delta = (dayOfWeek - currentDayOfWeek);
// only want time in the future
if (delta < 0)
{
delta += 7;
}
result = *this + (delta * c_DayAsSeconds);
return result;
}

View File

@@ -0,0 +1,705 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
// ESP32 complains if not included
#if defined(ARDUINO_ARCH_ESP32)
#include <inttypes.h>
#endif
#include "RtcTimeZone.h"
#include "RtcLocaleEnUs.h"
#include "RtcLocaleEn.h"
enum DayOfWeek
{
DayOfWeek_Sunday = 0,
DayOfWeek_Monday,
DayOfWeek_Tuesday,
DayOfWeek_Wednesday,
DayOfWeek_Thursday,
DayOfWeek_Friday,
DayOfWeek_Saturday,
};
const uint16_t c_OriginYear = 2000;
const uint32_t c_UnixEpoch32 = 946684800; // Unix origin year is 1970
const uint32_t c_NtpEpoch32FromUnixEpoch32 = 2208988800; // Ntp origin year is 1900
const uint32_t c_NtpEpoch32 = c_UnixEpoch32 + c_NtpEpoch32FromUnixEpoch32;
extern const uint8_t c_daysInMonth[] PROGMEM;
const uint32_t c_MinuteAsSeconds = 60;
const uint32_t c_HourAsSeconds = 60 * c_MinuteAsSeconds;
const uint32_t c_DayAsSeconds = 24 * c_HourAsSeconds;
const uint32_t c_WeekAsSeconds = 7 * c_DayAsSeconds;
class RtcDateTime
{
public:
explicit RtcDateTime(uint32_t secondsFrom2000 = 0)
{
_initWithSecondsFrom2000<uint32_t>(secondsFrom2000);
}
RtcDateTime(uint16_t year,
uint8_t month,
uint8_t dayOfMonth,
uint8_t hour,
uint8_t minute,
uint8_t second) :
_yearFrom2000((year >= c_OriginYear) ? year - c_OriginYear : year),
_month(month),
_dayOfMonth(dayOfMonth),
_hour(hour),
_minute(minute),
_second(second)
{
}
// RtcDateTime compileDateTime(__DATE__, __TIME__);
// sample input: date = "Dec 06 2009", time = "12:34:56"
RtcDateTime(const char* date, const char* time) :
RtcDateTime(0)
{
// __DATE__ is always in english
InitWithDateTimeFormatString<RtcLocaleEnUs>(F("MMM DD YYYY"), date);
InitWithDateTimeFormatString<RtcLocaleEnUs>(F("hh:mm:ss"), time);
}
bool IsValid() const;
uint16_t Year() const
{
return c_OriginYear + _yearFrom2000;
}
uint8_t Month() const
{
return _month;
}
uint8_t Day() const
{
return _dayOfMonth;
}
uint8_t Hour() const
{
return _hour;
}
uint8_t Minute() const
{
return _minute;
}
uint8_t Second() const
{
return _second;
}
// 0 = Sunday, 1 = Monday, ... 6 = Saturday
uint8_t DayOfWeek() const;
// 32-bit time; as seconds since 1/1/2000
uint32_t TotalSeconds() const;
// 64-bit time; as seconds since 1/1/2000
uint64_t TotalSeconds64() const;
// total days since 1/1/2000
uint16_t TotalDays() const;
// return the next day that falls on the given day of week
// if this day is that day of week, it will return this day
RtcDateTime NextDayOfWeek(uint8_t dayOfWeek) const;
/*
causes mbiguous overload for 'operator+'
making explicit doesn't solve it
// add unsigned seconds
void operator += (uint32_t seconds)
{
*this = *this + seconds;
}
RtcDateTime operator + (uint32_t seconds) const
{
return RtcDateTime(TotalSeconds() + seconds);
}
*/
// add signed seconds
void operator += (int32_t seconds)
{
*this = *this + seconds;
}
RtcDateTime operator + (int32_t seconds) const
{
uint32_t totalSeconds = TotalSeconds();
// never allowed to go before year 2000
if (seconds < 0 && abs(seconds) > totalSeconds)
{
totalSeconds = 0;
}
else
{
totalSeconds += seconds;
}
return RtcDateTime(totalSeconds);
}
// remove seconds
void operator -= (uint32_t seconds)
{
*this = *this - seconds;
}
RtcDateTime operator - (uint32_t seconds) const
{
return RtcDateTime(TotalSeconds() - seconds);
}
bool operator == (const RtcDateTime& right)
{
return (this->TotalSeconds() == right.TotalSeconds());
}
bool operator != (const RtcDateTime& right)
{
return (this->TotalSeconds() != right.TotalSeconds());
}
bool operator <= (const RtcDateTime& right)
{
return (this->TotalSeconds() <= right.TotalSeconds());
}
bool operator >= (const RtcDateTime& right)
{
return (this->TotalSeconds() >= right.TotalSeconds());
}
bool operator < (const RtcDateTime& right)
{
return (this->TotalSeconds() < right.TotalSeconds());
}
bool operator > (const RtcDateTime& right)
{
return (this->TotalSeconds() > right.TotalSeconds());
}
// Epoch32 support
[[deprecated("Use Unix32Time() instead.")]]
uint32_t Epoch32Time() const
{
return TotalSeconds() + c_UnixEpoch32;
}
[[deprecated("Use InitWithUnix32Time() instead.")]]
void InitWithEpoch32Time(uint32_t secondsSince1970)
{
_initWithSecondsFrom2000<uint32_t>(secondsSince1970 - c_UnixEpoch32);
}
// Epoch64 support
[[deprecated("Use Unix64Time() instead.")]]
uint64_t Epoch64Time() const
{
return TotalSeconds64() + c_UnixEpoch32;
}
[[deprecated("Use InitWithUnix64Time() instead.")]]
void InitWithEpoch64Time(uint64_t secondsSince1970)
{
_initWithSecondsFrom2000<uint64_t>(secondsSince1970 - c_UnixEpoch32);
}
// Unix32 support
uint32_t Unix32Time() const
{
return TotalSeconds() + c_UnixEpoch32;
}
void InitWithUnix32Time(uint32_t secondsSince1970)
{
_initWithSecondsFrom2000<uint32_t>(secondsSince1970 - c_UnixEpoch32);
}
// Unix64 support
uint64_t Unix64Time() const
{
return TotalSeconds64() + c_UnixEpoch32;
}
void InitWithUnix64Time(uint64_t secondsSince1970)
{
_initWithSecondsFrom2000<uint64_t>(secondsSince1970 - c_UnixEpoch32);
}
// Ntp32 support
uint32_t Ntp32Time() const
{
return TotalSeconds() + c_NtpEpoch32;
}
void InitWithNtp32Time(uint32_t secondsSince1900)
{
_initWithSecondsFrom2000<uint32_t>(secondsSince1900 - c_NtpEpoch32);
}
// Ntp64 support
uint64_t Ntp64Time() const
{
return TotalSeconds64() + c_NtpEpoch32;
}
void InitWithNtp64Time(uint64_t secondsSince1900)
{
_initWithSecondsFrom2000<uint64_t>(secondsSince1900 - c_NtpEpoch32);
}
[[deprecated("Use InitWithDateTimeFormatString()")]]
void InitWithIso8601(const char* date)
{
// sample input: date = "Sat, 06 Dec 2009 12:34:56 GMT"
InitWithDateTimeFormatString<RtcLocaleEnUs>(F("*, DD MMM YYYY hh:mm:ss zzz"), date);
}
//
// https://www.w3.org/TR/NOTE-datetime
// https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings
// https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations#:~:text=List%20of%20time%20zone%20abbreviations%20%20%20,%20%20UTC%2B08%3A00%20%2051%20more%20rows%20
//
// * - ignore until next char
// ! = ignore until not next char
//
// YY - two digit year, assumes 2000 +
// YYYY - four digit year
//
// M - full month name, arbitrary length until next char
// MM - two digit month
// MMM - abreviated month name, 3 chars
//
// DD - two digit day of month
//
// hh - hour
// mm - minute
// ss - seconds
// sssss - seconds with decimal (12.34)
//
// z - +hh:mm or Z -
// using this will adjust the time to UTC from the time zone
// using this will adjust the time to UTC from the time zone
// present in the string,
// without it, it will ignore the timezone and return the local
// zzz - time zone abbreviation
// using this will adjust the time to UTC from the time zone
// present in the string,
// without it, it will ignore the timezone and return the local
//
// return - index last converted of datetime
template <typename T_LOCALE = RtcLocaleEnUs> size_t InitWithDateTimeFormatString(
const char* format,
const char* datetime)
{
const char specifiers[] = "*!YMDhmsz";
const char* scan = format;
const char* convert = datetime;
int32_t timezoneMinutes = 0;
// while chars in format and datetime
while (*scan != '\0' && *datetime != '\0')
{
// find next token
size_t iStart = strcspn(scan, specifiers);
scan += iStart;
convert += iStart;
if (*scan != '\0')
{
// find the end of the token
size_t iEnd = 1;
while (scan[iEnd] == *scan)
{
iEnd++;
}
size_t count = iEnd;
size_t countConverted = 0;
// handy debug tracing
//
//Serial.print(scan[iStart]);
//Serial.print(">");
//Serial.print(convert);
//Serial.print("< ");
//Serial.print(count);
//Serial.println();
switch (scan[iStart])
{
case '*':
{
// increment through convert until the matching char
// from scan after the * token
const char* skip = convert;
while (*skip != '\0' && *skip != scan[iEnd])
{
skip++;
}
// include skipping extra matching char
countConverted = skip - convert + 1;
count++;
// handy debug tracing
//
//Serial.print("*>");
//Serial.print(scan + count);
//Serial.print("<->");
//Serial.print(convert + countConverted);
//Serial.print("< ");
//Serial.print(count);
//Serial.print("-");
//Serial.print(countConverted);
//Serial.println();
}
break;
case '!':
{
// increment through convert until the matching char
// from scan after the * token is not present
const char* skip = convert;
while (*skip != '\0' && *skip == scan[iEnd])
{
skip++;
}
countConverted = skip - convert;
count++;
// handy debug tracing
//
//Serial.print("!>");
//Serial.print(scan + count);
//Serial.print("<->");
//Serial.print(convert + countConverted);
//Serial.print("< ");
//Serial.print(count);
//Serial.print("-");
//Serial.print(countConverted);
//Serial.println();
}
break;
case 'Y':
if (count >= 4)
{
// only care about last three digits
size_t offset = count - 3;
scan += offset;
convert += offset;
count = 3;
}
countConverted = CharsToNumber<uint8_t>(convert, &_yearFrom2000, count);
break;
case 'M':
if (*convert >= '0' && *convert <= '9')
{
if (count > 2)
{
return convert - datetime;
}
countConverted = CharsToNumber<uint8_t>(convert, &_month, count);
}
else
{
if (count > 3)
{
return convert - datetime;
}
else if (count == 1)
{
const char* temp = convert;
// increment temp until matching char after M
while (*temp != scan[iEnd])
{
temp++;
}
size_t monthCount = temp - convert;
if (monthCount < 3)
{
return convert - datetime;
}
_month = T_LOCALE::CharsToMonth(convert, monthCount);
countConverted = monthCount;
}
else
{
_month = T_LOCALE::CharsToMonth(convert, count);
countConverted = count;
}
}
break;
case 'D':
countConverted = CharsToNumber<uint8_t>(convert, &_dayOfMonth, count);
break;
case 'h':
countConverted = CharsToNumber<uint8_t>(convert, &_hour, count);
break;
case 'm':
countConverted = CharsToNumber<uint8_t>(convert, &_minute, count);
break;
case 's':
countConverted = CharsToNumber<uint8_t>(convert, &_second, count);
break;
case 'z':
if (count == 1)
{
const char* temp = convert;
// +hh:mm or Z formated timezone
// adjusting to local time
if (*temp == '+' || *temp == '-')
{
uint8_t hours;
uint8_t minutes;
int32_t timezoneSign = (*temp == '+') ? 1 : -1;
temp++;
temp += CharsToNumber<uint8_t>(temp, &hours, 2);
temp++; // :
temp += CharsToNumber<uint8_t>(temp, &minutes, 2);
timezoneMinutes = (static_cast<int32_t>(hours) * 60 + minutes) * timezoneSign;
countConverted = temp - datetime;
}
else if (*temp == 'Z' || *temp == 'z')
{
// nothing to adjust,
// zulu time is what we want
countConverted = 1;
}
else
{
return convert - datetime;
}
}
else
{
// zzz - abbreviation timezone format
// adjust from local time
countConverted = T_LOCALE::TimeZoneMinutesFromAbbreviation(&timezoneMinutes, convert);
}
break;
}
if (countConverted)
{
scan += count;
convert += countConverted;
}
else
{
return convert - datetime;
}
}
}
// adjust our time by the timezone to get GMT/Z
// as by default RtcDateTime is GMT
//
*this -= timezoneMinutes * 60;
return convert - datetime;
}
// Version of above but supporting PROGMEM for the format,
// specifically the F("") use for format
template <typename T_LOCALE = RtcLocaleEnUs> size_t InitWithDateTimeFormatString(
const __FlashStringHelper* format,
const char* datetime)
{
char ramFormat[32];
strncpy_P(ramFormat,
reinterpret_cast<const char*>(format),
countof(ramFormat));
return InitWithDateTimeFormatString<T_LOCALE>(ramFormat, datetime);
}
// convert our Day of Week to Rtc Day of Week
// RTC Hardware Day of Week is 1-7, 1 = Monday
static uint8_t ConvertDowToRtc(uint8_t dow)
{
if (dow == 0)
{
dow = 7;
}
return dow;
}
// convert Rtc Day of Week to our Day of Week
static uint8_t ConvertRtcToDow(uint8_t rtcDow)
{
return (rtcDow % 7);
}
// returns the number days in the month
// month (1-12)
static uint8_t DaysInMonth(uint16_t year, uint8_t month)
{
uint8_t zMonth = 0;
// cap and convert to zero based
if (month != 0)
{
if (month > 11)
{
zMonth = 11;
}
else
{
zMonth = month - 1;
}
}
uint8_t days = pgm_read_byte(c_daysInMonth + zMonth);
// check february for leap years
if (month == 2 && IsLeapYear(year))
{
days++;
}
return days;
}
static bool IsLeapYear(uint16_t year)
{
return ((year % 4) == 0);
}
protected:
uint8_t _yearFrom2000;
uint8_t _month;
uint8_t _dayOfMonth;
uint8_t _hour;
uint8_t _minute;
uint8_t _second;
template <typename T> void _initWithSecondsFrom2000(T secondsFrom2000)
{
_second = secondsFrom2000 % 60;
T timeFrom2000 = secondsFrom2000 / 60;
_minute = timeFrom2000 % 60;
timeFrom2000 /= 60;
_hour = timeFrom2000 % 24;
T days = timeFrom2000 / 24;
T leapDays;
for (_yearFrom2000 = 0;; ++_yearFrom2000)
{
leapDays = (_yearFrom2000 % 4 == 0) ? 1 : 0;
if (days < 365U + leapDays)
break;
days -= 365 + leapDays;
}
for (_month = 1;; ++_month)
{
uint8_t daysPerMonth = pgm_read_byte(c_daysInMonth + _month - 1);
if (leapDays && _month == 2)
daysPerMonth++;
if (days < daysPerMonth)
break;
days -= daysPerMonth;
}
_dayOfMonth = days + 1;
}
// CharsToNumber - convert a series of chars to a number of the given type
//
// str - the pointer to string to process
// result - the value converted from the string
// count - the number of numerals to stop processing with
// excludes leading non-numeral chars
// return - the number to increment str for more processing,
// 0 for failure
template <typename T_NUMBER> size_t CharsToNumber(const char* str, T_NUMBER* result, size_t count)
{
const char* scan = str;
bool converted = false;
size_t left = count;
*result = 0;
// skip leading 0 and non numericals
while (left && '\0' != *scan && ('0' >= *scan || '9' < *scan))
{
// only decrement left with numerals
if (left && '0' == *scan)
{
converted = true;
left--;
}
scan++;
}
// calculate number until we hit non-numeral char
while (left && '\0' != *scan && '0' <= *scan && *scan <= '9')
{
converted = true;
*result *= 10;
*result += *scan - '0';
left--;
scan++;
}
// we ignore decimal numbers but we need to scan past them
//
if (left && '.' == *scan)
{
// skip decimal
left--;
scan++;
// continue to discard numerals
while (left && '\0' != *scan && '0' <= *scan && *scan <= '9')
{
left--;
scan++;
}
}
return converted ? scan - str : 0;
}
};

View File

@@ -0,0 +1,509 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include <Arduino.h>
#include "RtcUtility.h"
#include "RtcTimeZone.h"
#include "RtcLocaleEn.h"
// due to how PROGMEM works, strings must be declared standalone
//
const char stringACDT[] PROGMEM = "ACDT";
const char stringACST[] PROGMEM = "ACST";
const char stringACT[] PROGMEM = "ACT";
const char stringACWST[] PROGMEM = "ACWST";
const char stringADT[] PROGMEM = "ADT";
const char stringAEDT[] PROGMEM = "AEDT";
const char stringAEST[] PROGMEM = "AEST";
const char stringAET[] PROGMEM = "AET";
const char stringAFT[] PROGMEM = "AFT";
const char stringAKDT[] PROGMEM = "AKDT";
const char stringAKST[] PROGMEM = "AKST";
const char stringALMT[] PROGMEM = "ALMT";
const char stringAMST[] PROGMEM = "AMST";
const char stringAMT[] PROGMEM = "AMT";
const char stringANAT[] PROGMEM = "ANAT";
const char stringAQTT[] PROGMEM = "AQTT";
const char stringART[] PROGMEM = "ART";
const char stringAST[] PROGMEM = "AST";
const char stringAWST[] PROGMEM = "AWST";
const char stringAZOST[] PROGMEM = "AZOST";
const char stringAZOT[] PROGMEM = "AZOT";
const char stringAZT[] PROGMEM = "AZT";
const char stringBNT[] PROGMEM = "BNT";
const char stringBIOT[] PROGMEM = "BIOT";
const char stringBIT[] PROGMEM = "BIT";
const char stringBOT[] PROGMEM = "BOT";
const char stringBRST[] PROGMEM = "BRST";
const char stringBRT[] PROGMEM = "BRT";
const char stringBST[] PROGMEM = "BST";
const char stringBTT[] PROGMEM = "BTT";
const char stringCAT[] PROGMEM = "CAT";
const char stringCCT[] PROGMEM = "CCT";
const char stringCDT[] PROGMEM = "CDT";
const char stringCEST[] PROGMEM = "CEST";
const char stringCET[] PROGMEM = "CET";
const char stringCHADT[] PROGMEM = "CHADT";
const char stringCHAST[] PROGMEM = "CHAST";
const char stringCHOT[] PROGMEM = "CHOT";
const char stringCHOST[] PROGMEM = "CHOST";
const char stringCHST[] PROGMEM = "CHST";
const char stringCHUT[] PROGMEM = "CHUT";
const char stringCIST[] PROGMEM = "CIST";
const char stringCKT[] PROGMEM = "CKT";
const char stringCLST[] PROGMEM = "CLST";
const char stringCLT[] PROGMEM = "CLT";
const char stringCOST[] PROGMEM = "COST";
const char stringCOT[] PROGMEM = "COT";
const char stringCST[] PROGMEM = "CST";
const char stringCT[] PROGMEM = "CT";
const char stringCVT[] PROGMEM = "CVT";
const char stringCWST[] PROGMEM = "CWST";
const char stringCXT[] PROGMEM = "CXT";
const char stringDAVT[] PROGMEM = "DAVT";
const char stringDDUT[] PROGMEM = "DDUT";
const char stringDFT[] PROGMEM = "DFT";
const char stringEASST[] PROGMEM = "EASST";
const char stringEAST[] PROGMEM = "EAST";
const char stringEAT[] PROGMEM = "EAT";
const char stringECT[] PROGMEM = "ECT";
const char stringEDT[] PROGMEM = "EDT";
const char stringEEST[] PROGMEM = "EEST";
const char stringEET[] PROGMEM = "EET";
const char stringEGST[] PROGMEM = "EGST";
const char stringEGT[] PROGMEM = "EGT";
const char stringEST[] PROGMEM = "EST";
const char stringET[] PROGMEM = "ET";
const char stringFET[] PROGMEM = "FET";
const char stringFJT[] PROGMEM = "FJT";
const char stringFKST[] PROGMEM = "FKST";
const char stringFKT[] PROGMEM = "FKT";
const char stringFNT[] PROGMEM = "FNT";
const char stringGALT[] PROGMEM = "GALT";
const char stringGAMT[] PROGMEM = "GAMT";
const char stringGET[] PROGMEM = "GET";
const char stringGFT[] PROGMEM = "GFT";
const char stringGILT[] PROGMEM = "GILT";
const char stringGIT[] PROGMEM = "GIT";
const char stringGMT[] PROGMEM = "GMT";
const char stringGST[] PROGMEM = "GST";
const char stringGYT[] PROGMEM = "GYT";
const char stringHDT[] PROGMEM = "HDT";
const char stringHAEC[] PROGMEM = "HAEC";
const char stringHST[] PROGMEM = "HST";
const char stringHKT[] PROGMEM = "HKT";
const char stringHMT[] PROGMEM = "HMT";
const char stringHOVST[] PROGMEM = "HOVST";
const char stringHOVT[] PROGMEM = "HOVT";
const char stringICT[] PROGMEM = "ICT";
const char stringIDLW[] PROGMEM = "IDLW";
const char stringIDT[] PROGMEM = "IDT";
const char stringIOT[] PROGMEM = "IOT";
const char stringIRDT[] PROGMEM = "IRDT";
const char stringIRKT[] PROGMEM = "IRKT";
const char stringIRST[] PROGMEM = "IRST";
const char stringIST[] PROGMEM = "IST";
const char stringJST[] PROGMEM = "JST";
const char stringKALT[] PROGMEM = "KALT";
const char stringKGT[] PROGMEM = "KGT";
const char stringKOST[] PROGMEM = "KOST";
const char stringKRAT[] PROGMEM = "KRAT";
const char stringKST[] PROGMEM = "KST";
const char stringLHST[] PROGMEM = "LHST";
const char stringLINT[] PROGMEM = "LINT";
const char stringMAGT[] PROGMEM = "MAGT";
const char stringMART[] PROGMEM = "MART";
const char stringMAWT[] PROGMEM = "MAWT";
const char stringMDT[] PROGMEM = "MDT";
const char stringMET[] PROGMEM = "MET";
const char stringMEST[] PROGMEM = "MEST";
const char stringMHT[] PROGMEM = "MHT";
const char stringMIST[] PROGMEM = "MIST";
const char stringMIT[] PROGMEM = "MIT";
const char stringMMT[] PROGMEM = "MMT";
const char stringMSK[] PROGMEM = "MSK";
const char stringMST[] PROGMEM = "MST";
const char stringMUT[] PROGMEM = "MUT";
const char stringMVT[] PROGMEM = "MVT";
const char stringMYT[] PROGMEM = "MYT";
const char stringNCT[] PROGMEM = "NCT";
const char stringNDT[] PROGMEM = "NDT";
const char stringNFT[] PROGMEM = "NFT";
const char stringNOVT[] PROGMEM = "NOVT";
const char stringNPT[] PROGMEM = "NPT";
const char stringNST[] PROGMEM = "NST";
const char stringNT[] PROGMEM = "NT";
const char stringNUT[] PROGMEM = "NUT";
const char stringNZDT[] PROGMEM = "NZDT";
const char stringNZST[] PROGMEM = "NZST";
const char stringOMST[] PROGMEM = "OMST";
const char stringORAT[] PROGMEM = "ORAT";
const char stringPDT[] PROGMEM = "PDT";
const char stringPET[] PROGMEM = "PET";
const char stringPETT[] PROGMEM = "PETT";
const char stringPGT[] PROGMEM = "PGT";
const char stringPHOT[] PROGMEM = "PHOT";
const char stringPHT[] PROGMEM = "PHT";
const char stringPHST[] PROGMEM = "PHST";
const char stringPKT[] PROGMEM = "PKT";
const char stringPMDT[] PROGMEM = "PMDT";
const char stringPMST[] PROGMEM = "PMST";
const char stringPONT[] PROGMEM = "PONT";
const char stringPST[] PROGMEM = "PST";
const char stringPWT[] PROGMEM = "PWT";
const char stringPYST[] PROGMEM = "PYST";
const char stringPYT[] PROGMEM = "PYT";
const char stringRET[] PROGMEM = "RET";
const char stringROTT[] PROGMEM = "ROTT";
const char stringSAKT[] PROGMEM = "SAKT";
const char stringSAMT[] PROGMEM = "SAMT";
const char stringSAST[] PROGMEM = "SAST";
const char stringSBT[] PROGMEM = "SBT";
const char stringSCT[] PROGMEM = "SCT";
const char stringSDT[] PROGMEM = "SDT";
const char stringSGT[] PROGMEM = "SGT";
const char stringSLST[] PROGMEM = "SLST";
const char stringSRET[] PROGMEM = "SRET";
const char stringSRT[] PROGMEM = "SRT";
const char stringSST[] PROGMEM = "SST";
const char stringSYOT[] PROGMEM = "SYOT";
const char stringTAHT[] PROGMEM = "TAHT";
const char stringTHA[] PROGMEM = "THA";
const char stringTFT[] PROGMEM = "TFT";
const char stringTJT[] PROGMEM = "TJT";
const char stringTKT[] PROGMEM = "TKT";
const char stringTLT[] PROGMEM = "TLT";
const char stringTMT[] PROGMEM = "TMT";
const char stringTRT[] PROGMEM = "TRT";
const char stringTOT[] PROGMEM = "TOT";
const char stringTVT[] PROGMEM = "TVT";
const char stringULAST[] PROGMEM = "ULAST";
const char stringULAT[] PROGMEM = "ULAT";
const char stringUTC[] PROGMEM = "UTC";
const char stringUYST[] PROGMEM = "UYST";
const char stringUYT[] PROGMEM = "UYT";
const char stringUZT[] PROGMEM = "UZT";
const char stringVET[] PROGMEM = "VET";
const char stringVLAT[] PROGMEM = "VLAT";
const char stringVOLT[] PROGMEM = "VOLT";
const char stringVOST[] PROGMEM = "VOST";
const char stringVUT[] PROGMEM = "VUT";
const char stringWAKT[] PROGMEM = "WAKT";
const char stringWAST[] PROGMEM = "WAST";
const char stringWAT[] PROGMEM = "WAT";
const char stringWEST[] PROGMEM = "WEST";
const char stringWET[] PROGMEM = "WET";
const char stringWIB[] PROGMEM = "WIB";
const char stringWIT[] PROGMEM = "WIT";
const char stringWITA[] PROGMEM = "WITA";
const char stringWGST[] PROGMEM = "WGST";
const char stringWGT[] PROGMEM = "WGT";
const char stringWST[] PROGMEM = "WST";
const char stringYAKT[] PROGMEM = "YAKT";
const char stringYEKT[] PROGMEM = "YEKT";
// collection in alphabetic order of abbreviation
// duplicates commented out randomly (why are there duplicates in a standard?!)
// IF YOU MODIFY THIS to your locale, please goto github and add an issue
//
const RtcTimeZone c_tzEn[] PROGMEM =
{
{ stringACDT, 10 * 60 + 30 }, // Australian Central Daylight Saving Time
{ stringACST, 9 * 60 + 30 }, // Australian Central Standard Time
{ stringACT, -5 * 60 }, // Acre Time
// { stringACT, 8 * 60 + 0 }, // ASEAN Common Time (proposed)
{ stringACWST, 8 * 60 + 45 }, // Australian Central Western Standard Time (unofficial)
{ stringADT, -3 * 60 }, // Atlantic Daylight Time
{ stringAEDT, 11 * 60 }, // Australian Eastern Daylight Saving Time
{ stringAEST, 10 * 60 }, // Australian Eastern Standard Time
{ stringAET, 10 * 60 }, // Australian Eastern Time
{ stringAFT, 4 * 60 + 30 }, // Afghanistan Time
{ stringAKDT, -8 * 60 }, // Alaska Daylight Time
{ stringAKST, -9 * 60 }, // Alaska Standard Time
{ stringALMT, 6 * 60 }, // Alma-Ata Time[1]
{ stringAMST, -3 * 60 }, // Amazon Summer Time (Brazil)[2]
{ stringAMT, -4 * 60 }, // Amazon Time (Brazil)[3]
// { stringAMT, 4 * 60 }, // Armenia Time
{ stringANAT, 12 * 60 }, // Anadyr Time[4]
{ stringAQTT, 5 * 60 }, // Aqtobe Time[5]
{ stringART, -3 * 60 }, // Argentina Time
// { stringAST, 3 * 60 }, // Arabia Standard Time
{ stringAST, -4 * 60 }, // Atlantic Standard Time
{ stringAWST, 8 * 60 }, // Australian Western Standard Time
{ stringAZOST, 0 }, // Azores Summer Time
{ stringAZOT, -1 * 60 }, // Azores Standard Time
{ stringAZT, 4 * 60 }, // Azerbaijan Time
{ stringBNT, 8 * 60 }, // Brunei Time
{ stringBIOT, 6 * 60 }, // British Indian Ocean Time
{ stringBIT, -12 * 60 }, // Baker Island Time
{ stringBOT, -4 * 60 }, // Bolivia Time
{ stringBRST, -2 * 60 }, // Brasília Summer Time
{ stringBRT, -3 * 60 }, // Brasília Time
// { stringBST, 6 * 60 }, // Bangladesh Standard Time
// { stringBST, 11 * 60 }, // Bougainville Standard Time[6]
{ stringBST, 1 * 60 }, // British Summer Time (British Standard Time from Mar 1968 to Oct 1971)
{ stringBTT, 6 * 60 }, // Bhutan Time
{ stringCAT, 2 * 60 }, // Central Africa Time
{ stringCCT, 6 * 60 + 30 }, // Cocos Islands Time
{ stringCDT, -5 * 60 }, // Central Daylight Time (North America)
// { stringCDT, -4 * 60 }, // Cuba Daylight Time[7]
{ stringCEST, 2 * 60 }, // Central European Summer Time
{ stringCET, 1 * 60 }, // Central European Time
{ stringCHADT, 13 * 60 + 45 }, // Chatham Daylight Time
{ stringCHAST, 12 * 60 + 45 }, // Chatham Standard Time
{ stringCHOT, 8 * 60 }, // Choibalsan Standard Time
{ stringCHOST, 9 * 60 }, // Choibalsan Summer Time
{ stringCHST, 10 * 60 }, // Chamorro Standard Time
{ stringCHUT, 10 * 60 }, // Chuuk Time
{ stringCIST, -8 * 60 }, // Clipperton Island Standard Time
{ stringCKT, -10 * 60 }, // Cook Island Time
{ stringCLST, -3 * 60 }, // Chile Summer Time
{ stringCLT, -4 * 60 }, // Chile Standard Time
{ stringCOST, -4 * 60 }, // Colombia Summer Time
{ stringCOT, -5 * 60 }, // Colombia Time
{ stringCST, -6 * 60 }, // Central Standard Time (North America)
// { stringCST, 8 * 60 }, // China Standard Time
// { stringCST, -5 * 60 }, // Cuba Standard Time
{ stringCT, -6 * 60 }, // Central Time
{ stringCVT, -1 * 60 }, // Cape Verde Time
{ stringCWST, 8 * 60 + 45 }, // Central Western Standard Time (Australia) unofficial
{ stringCXT, 7 * 60 }, // Christmas Island Time
{ stringDAVT, 7 * 60 }, // Davis Time
{ stringDDUT, 10 * 60 }, // Dumont d'Urville Time
{ stringDFT, 1 * 60 }, // AIX-specific equivalent of Central European Time[NB 1]
{ stringEASST, -5 * 60 }, // Easter Island Summer Time
{ stringEAST, -6 * 60 }, // Easter Island Standard Time
{ stringEAT, 3 * 60 }, // East Africa Time
{ stringECT, -4 * 60 }, // Eastern Caribbean Time (does not recognise DST)
// { stringECT, -5 * 60 }, // Ecuador Time
{ stringEDT, -4 * 60 }, // Eastern Daylight Time (North America)
{ stringEEST, 3 * 60 }, // Eastern European Summer Time
{ stringEET, 2 * 60 }, // Eastern European Time
{ stringEGST, 0 }, // Eastern Greenland Summer Time
{ stringEGT, -1 * 60 }, // Eastern Greenland Time
{ stringEST, -5 * 60 }, // Eastern Standard Time (North America)
{ stringET, -5 * 60 }, // Eastern Time (North America)
{ stringFET, 3 * 60 }, // Further-eastern European Time
{ stringFJT, 12 * 60 }, // Fiji Time
{ stringFKST, -3 * 60 }, // Falkland Islands Summer Time
{ stringFKT, -4 * 60 }, // Falkland Islands Time
{ stringFNT, -2 * 60 }, // Fernando de Noronha Time
{ stringGALT, -6 * 60 }, // Galápagos Time
{ stringGAMT, -9 * 60 }, // Gambier Islands Time
{ stringGET, 4 * 60 }, // Georgia Standard Time
{ stringGFT, -3 * 60 }, // French Guiana Time
{ stringGILT, 12 * 60 }, // Gilbert Island Time
{ stringGIT, -9 * 60 }, // Gambier Island Time
{ stringGMT, 0 }, // Greenwich Mean Time
// { stringGST, -2 * 60 }, // South Georgia and the South Sandwich Islands Time
{ stringGST, 4 * 60 }, // Gulf Standard Time
{ stringGYT, -4 * 60 }, // Guyana Time
{ stringHDT, -9 * 60 }, // HawaiiAleutian Daylight Time
{ stringHAEC, 2 * 60 }, // Heure Avancée d'Europe Centrale French-language name for CEST
{ stringHST, -10 * 60 }, // HawaiiAleutian Standard Time
{ stringHKT, 8 * 60 }, // Hong Kong Time
{ stringHMT, 5 * 60 }, // Heard and McDonald Islands Time
{ stringHOVST, 8 * 60 }, // Hovd Summer Time (not used from 2017-present)
{ stringHOVT, 7 * 60 }, // Hovd Time
{ stringICT, 7 * 60 }, // Indochina Time
{ stringIDLW, -12 * 60 }, // International Date Line West time zone
{ stringIDT, 3 * 60 }, // Israel Daylight Time
{ stringIOT, 3 * 60 }, // Indian Ocean Time
{ stringIRDT, 4 * 60 + 30 }, // Iran Daylight Time
{ stringIRKT, 8 * 60 }, // Irkutsk Time
{ stringIRST, 3 * 60 + 30 }, // Iran Standard Time
// { stringIST, 5 * 60 + 30 }, // Indian Standard Time
// { stringIST, 1 * 60 }, // Irish Standard Time[8]
{ stringIST, 2 * 60 }, // Israel Standard Time
{ stringJST, 9 * 60 }, // Japan Standard Time
{ stringKALT, 2 * 60 }, // Kaliningrad Time
{ stringKGT, 6 * 60 }, // Kyrgyzstan Time
{ stringKOST, 11 * 60 }, // Kosrae Time
{ stringKRAT, 7 * 60 }, // Krasnoyarsk Time
{ stringKST, 9 * 60 }, // Korea Standard Time
{ stringLHST, 10 * 60 + 30 }, // Lord Howe Standard Time
{ stringLHST, 11 * 60 }, // Lord Howe Summer Time
{ stringLINT, 14 * 60 }, // Line Islands Time
{ stringMAGT, 12 * 60 }, // Magadan Time
{ stringMART, -(9 * 60 + 30) }, // Marquesas Islands Time
{ stringMAWT, 5 * 60 }, // Mawson Station Time
{ stringMDT, -6 * 60 }, // Mountain Daylight Time (North America)
{ stringMET, 1 * 60 }, // Middle European Time (same zone as CET)
{ stringMEST, 2 * 60 }, // Middle European Summer Time (same zone as CEST)
{ stringMHT, 12 * 60 }, // Marshall Islands Time
{ stringMIST, 11 * 60 }, // Macquarie Island Station Time
{ stringMIT, -(9 * 60 + 30) }, // Marquesas Islands Time
{ stringMMT, 6 * 60 + 30 }, // Myanmar Standard Time
{ stringMSK, 3 * 60 }, // Moscow Time
{ stringMST, 8 * 60 }, // Malaysia Standard Time
{ stringMST, -7 * 60 }, // Mountain Standard Time (North America)
{ stringMUT, 4 * 60 }, // Mauritius Time
{ stringMVT, 5 * 60 }, // Maldives Time
{ stringMYT, 8 * 60 }, // Malaysia Time
{ stringNCT, 11 * 60 }, // New Caledonia Time
{ stringNDT, -(2 * 60 + 30) }, // Newfoundland Daylight Time
{ stringNFT, 11 * 60 }, // Norfolk Island Time
{ stringNOVT, 7 * 60 }, // Novosibirsk Time [9]
{ stringNPT, 5 * 60 + 45 }, // Nepal Time
{ stringNST, -(3 * 60 + 30) }, // Newfoundland Standard Time
{ stringNT, -(3 * 60 + 30) }, // Newfoundland Time
{ stringNUT, -11 * 60 }, // Niue Time
{ stringNZDT, 13 * 60 }, // New Zealand Daylight Time
{ stringNZST, 12 * 60 }, // New Zealand Standard Time
{ stringOMST, 6 * 60 }, // Omsk Time
{ stringORAT, 5 * 60 }, // Oral Time
{ stringPDT, -7 * 60 }, // Pacific Daylight Time (North America)
{ stringPET, -5 * 60 }, // Peru Time
{ stringPETT, 12 * 60 }, // Kamchatka Time
{ stringPGT, 10 * 60 }, // Papua New Guinea Time
{ stringPHOT, 13 * 60 }, // Phoenix Island Time
{ stringPHT, 8 * 60 }, // Philippine Time
{ stringPHST, 8 * 60 }, // Philippine Standard Time
{ stringPKT, 5 * 60 }, // Pakistan Standard Time
{ stringPMDT, -2 * 60 }, // Saint Pierre and Miquelon Daylight Time
{ stringPMST, -3 * 60 }, // Saint Pierre and Miquelon Standard Time
{ stringPONT, 11 * 60 }, // Pohnpei Standard Time
{ stringPST, -8 * 60 }, // Pacific Standard Time (North America)
{ stringPWT, 9 * 60 }, // Palau Time[10]
{ stringPYST, -3 * 60 }, // Paraguay Summer Time[11]
{ stringPYT, -4 * 60 }, // Paraguay Time[12]
{ stringRET, 4 * 60 }, // Réunion Time
{ stringROTT, -3 * 60 }, // Rothera Research Station Time
{ stringSAKT, 11 * 60 }, // Sakhalin Island Time
{ stringSAMT, 4 * 60 }, // Samara Time
{ stringSAST, 2 * 60 }, // South African Standard Time
{ stringSBT, 11 * 60 }, // Solomon Islands Time
{ stringSCT, 4 * 60 }, // Seychelles Time
{ stringSDT, -10 * 60 }, // Samoa Daylight Time
{ stringSGT, 8 * 60 }, // Singapore Time
{ stringSLST, 5 * 60 + 30 }, // Sri Lanka Standard Time
{ stringSRET, 11 * 60 }, // Srednekolymsk Time
{ stringSRT, -3 * 60 }, // Suriname Time
// { stringSST, -11 * 60 }, // Samoa Standard Time
{ stringSST, 8 * 60 }, // Singapore Standard Time
{ stringSYOT, 3 * 60 }, // Showa Station Time
{ stringTAHT, -10 * 60 }, // Tahiti Time
{ stringTHA, 7 * 60 }, // Thailand Standard Time
{ stringTFT, 5 * 60 }, // French Southern and Antarctic Time[13]
{ stringTJT, 5 * 60 }, // Tajikistan Time
{ stringTKT, 13 * 60 }, // Tokelau Time
{ stringTLT, 9 * 60 }, // Timor Leste Time
{ stringTMT, 5 * 60 }, // Turkmenistan Time
{ stringTRT, 3 * 60 }, // Turkey Time
{ stringTOT, 13 * 60 }, // Tonga Time
{ stringTVT, 12 * 60 }, // Tuvalu Time
{ stringULAST, 9 * 60 }, // Ulaanbaatar Summer Time
{ stringULAT, 8 * 60 }, // Ulaanbaatar Standard Time
{ stringUTC, 0 }, // Coordinated Universal Time
{ stringUYST, -2 * 60 }, // Uruguay Summer Time
{ stringUYT, -3 * 60 }, // Uruguay Standard Time
{ stringUZT, 5 * 60 }, // Uzbekistan Time
{ stringVET, -4 * 60 }, // Venezuelan Standard Time
{ stringVLAT, 10 * 60 }, // Vladivostok Time
{ stringVOLT, 3 * 60 }, // Volgograd Time
{ stringVOST, 6 * 60 }, // Vostok Station Time
{ stringVUT, 11 * 60 }, // Vanuatu Time
{ stringWAKT, 12 * 60 }, // Wake Island Time
{ stringWAST, 2 * 60 }, // West Africa Summer Time
{ stringWAT, 1 * 60 }, // West Africa Time
{ stringWEST, 1 * 60 }, // Western European Summer Time
{ stringWET, 0}, // Western European Time
{ stringWIB, 7 * 60 }, // Western Indonesian Time
{ stringWIT, 9 * 60 }, // Eastern Indonesian Time
{ stringWITA, 8 * 60 }, // Central Indonesia Time
{ stringWGST, -2 * 60 }, // West Greenland Summer Time[14]
{ stringWGT, -3 * 60 }, // West Greenland Time[15]
{ stringWST, 8 * 60 }, // Western Standard Time
{ stringYAKT, 9 * 60 }, // Yakutsk Time
{ stringYEKT, 5 * 60 } // Yekaterinburg Time
};
uint8_t RtcLocaleEn::CharsToMonth(const char* monthChars, size_t count)
{
uint8_t month = 0;
// works for both standard abbreviations and full month names
//
if (count >= 3)
{
switch (tolower(monthChars[0]))
{
case 'j':
if (tolower(monthChars[1]) == 'a')
month = 1;
else if (tolower(monthChars[2]) == 'n')
month = 6;
else
month = 7;
break;
case 'f':
month = 2;
break;
case 'a':
month = tolower(monthChars[1]) == 'p' ? 4 : 8;
break;
case 'm':
month = tolower(monthChars[2]) == 'r' ? 3 : 5;
break;
case 's':
month = 9;
break;
case 'o':
month = 10;
break;
case 'n':
month = 11;
break;
case 'd':
month = 12;
break;
}
}
return month;
}
size_t RtcLocaleEn::TimeZoneMinutesFromAbbreviation(int32_t* minutes,
const char* abbr)
{
RtcTimeZone entry;
*minutes = 0;
size_t result = RtcTimeZone::BinarySearchProgmemTable(&entry,
abbr,
c_tzEn,
countof(c_tzEn));
if (result)
{
// found
*minutes = entry.offset;
}
return result;
}

View File

@@ -0,0 +1,41 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
class RtcLocaleEn
{
public:
static uint8_t CharsToMonth(const char* monthChars, size_t count);
// TimeZoneMinutesFromAbbreviation
// searches known table for the abbreviation and provides the minutes offset
//
// *minutes - [out] variable filled with the minutes offset
// returns - the number of chars parsed, usually 3 or 4. 0 if not found
static size_t TimeZoneMinutesFromAbbreviation(int32_t* minutes, const char* abbr);
};

View File

@@ -0,0 +1,136 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include <Arduino.h>
#include "RtcUtility.h"
#include "RtcTimeZone.h"
#include "RtcLocaleEnUs.h"
// due to how PROGMEM works, strings must be declared standalone
//
const char stringADT[] PROGMEM = "ADT";
const char stringAKDT[] PROGMEM = "AKDT";
const char stringAKST[] PROGMEM = "AKST";
const char stringAST[] PROGMEM = "AST";
const char stringCDT[] PROGMEM = "CDT";
const char stringCST[] PROGMEM = "CST";
const char stringEDT[] PROGMEM = "EDT";
const char stringEST[] PROGMEM = "EST";
const char stringGMT[] PROGMEM = "GMT";
const char stringHDT[] PROGMEM = "HDT";
const char stringHST[] PROGMEM = "HST";
const char stringMDT[] PROGMEM = "MDT";
const char stringMST[] PROGMEM = "MST";
const char stringPDT[] PROGMEM = "PDT";
const char stringPST[] PROGMEM = "PST";
const char stringUTC[] PROGMEM = "UTC";
// collection in alphabetic order of abbreviation
//
const RtcTimeZone c_tzEnUs[] PROGMEM =
{
{ stringADT, -3 * 60 }, // Atlantic Daylight Time
{ stringAKDT, -8 * 60 }, // Alaska Daylight Time
{ stringAKST, -9 * 60 }, // Alaska Standard Time
{ stringAST, -4 * 60 }, // Atlantic Standard Time
{ stringCDT, -5 * 60 }, // Central Daylight Time
{ stringCST, -6 * 60 }, // Central Standard Time
{ stringEDT, -4 * 60 }, // Eastern Daylight Time
{ stringEST, -5 * 60 }, // Eastern Standard Time
{ stringGMT, 0 }, // Greenwich Mean Time
{ stringHDT, -9 * 60 }, // HawaiiAleutian Daylight Time
{ stringHST, -10 * 60 }, // HawaiiAleutian Standard Time
{ stringMDT, -6 * 60 }, // Mountain Daylight Time
{ stringMST, -7 * 60 }, // Mountain Standard Time
{ stringPDT, -7 * 60 }, // Pacific Daylight Time
{ stringPST, -8 * 60 }, // Pacific Standard Time
{ stringUTC, 0 }, // Cordinated Universal Time
};
uint8_t RtcLocaleEnUs::CharsToMonth(const char* monthChars, size_t count)
{
uint8_t month = 0;
// works for both standard abbreviations and full month names
//
if (count >= 3)
{
switch (tolower(monthChars[0]))
{
case 'j':
if (tolower(monthChars[1]) == 'a')
month = 1;
else if (tolower(monthChars[2]) == 'n')
month = 6;
else
month = 7;
break;
case 'f':
month = 2;
break;
case 'a':
month = tolower(monthChars[1]) == 'p' ? 4 : 8;
break;
case 'm':
month = tolower(monthChars[2]) == 'r' ? 3 : 5;
break;
case 's':
month = 9;
break;
case 'o':
month = 10;
break;
case 'n':
month = 11;
break;
case 'd':
month = 12;
break;
}
}
return month;
}
size_t RtcLocaleEnUs::TimeZoneMinutesFromAbbreviation(int32_t* minutes,
const char* abbr)
{
RtcTimeZone entry;
*minutes = 0;
size_t result = RtcTimeZone::BinarySearchProgmemTable(&entry,
abbr,
c_tzEnUs,
countof(c_tzEnUs));
if (result)
{
// found
*minutes = entry.offset;
}
return result;
}

View File

@@ -0,0 +1,41 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
class RtcLocaleEnUs
{
public:
static uint8_t CharsToMonth(const char* monthChars, size_t count);
// TimeZoneMinutesFromAbbreviation
// searches known table for the abbreviation and provides the minutes offset
//
// *minutes - [out] variable filled with the minutes offset
// returns - the number of chars parsed, usually 3 or 4. 0 if not found
static size_t TimeZoneMinutesFromAbbreviation(int32_t* minutes, const char* abbr);
};

View File

@@ -0,0 +1,424 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#include <Arduino.h>
#include "RtcUtility.h"
#include "RtcDateTime.h"
//I2C Slave Address
const uint8_t PCF8563_ADDRESS = 0x51;
//PCF8563 Register Addresses
const uint8_t PCF8563_REG_CONTROL = 0x00;
const uint8_t PCF8563_REG_STATUS = 0x01;
const uint8_t PCF8563_REG_TIMEDATE = 0x02;
const uint8_t PCF8563_REG_INTEGRITY = 0x02;
const uint8_t PCF8563_REG_ALARM = 0x09;
const uint8_t PCF8563_REG_CLKOUT_CONTROL = 0x0D;
const uint8_t PCF8563_REG_TIMER_CONTROL = 0x0E;
const uint8_t PCF8563_REG_TIMER = 0x0F;
//PCF8563 Register Data Size if not just 1
const size_t PCF8563_REG_TIMEDATE_SIZE = 7;
const size_t PCF8563_REG_ALARM_SIZE = 4;
// PCF8563 Valid Register Bits (PCF8563_REG_INTEGRITY)
const uint8_t PCF8563_INTEGRITY_VALID = 7;
// PCF8563 Control Register Bits (PCF8563_REG_CONTROL)
const uint8_t PCF8563_CONTROL_CLOCK = 5;
// PCF8563 Status Register Bits (PCF8563_REG_STATUS)
const uint8_t PCF8563_STATUS_TIE = 0; // timer interrupt enable
const uint8_t PCF8563_STATUS_AIE = 1; // alarm interrupt enable
const uint8_t PCF8563_STATUS_TF = 2; // timer flag
const uint8_t PCF8563_STATUS_AF = 3; // alarm flag
const uint8_t PCF8563_STATUS_TITP = 4; // timer interrupt period config
enum PCF8563SquareWavePinMode
{
PCF8563SquareWavePinMode_None = 0b00000000,
PCF8563SquareWavePinMode_32kHz = 0b10000000,
PCF8563SquareWavePinMode_1kHz = 0b10000001,
PCF8563SquareWavePinMode_32Hz = 0b10000010,
PCF8563SquareWavePinMode_1Hz = 0b10000011
};
// defines the timer period for the timer and
// when to trigger, minutes means at the top of the minute
// and period of 60 seconds
//
enum PCF8563TimerMode
{
PCF8563TimerMode_None = 0b00000000,
PCF8563TimerMode_4096thOfASecond = 0b10000000,
PCF8563TimerMode_64thOfASecond = 0b10000001,
PCF8563TimerMode_Seconds = 0b10000010,
PCF8563TimerMode_Minutes = 0b10000011 // at the top of and by
};
enum PCF8563AlarmControlFlags
{
PCF8563AlarmControl_MinuteMatch = 0x01,
PCF8563AlarmControl_HourMatch = 0x02,
PCF8563AlarmControl_DayOfMonthMatch = 0x04,
PCF8563AlarmControl_DayOfWeekMatch = 0x08
};
class PCF8563Alarm
{
public:
PCF8563Alarm(uint8_t dayOfMonth,
uint8_t hour,
uint8_t minute,
uint8_t dayOfWeek,
uint8_t controlFlags) :
_flags(controlFlags),
_dayOfMonth(dayOfMonth),
_hour(hour),
_minute(minute),
_dayOfWeek(dayOfWeek)
{
}
uint8_t DayOfMonth() const
{
return _dayOfMonth;
}
uint8_t Hour() const
{
return _hour;
}
uint8_t Minute() const
{
return _minute;
}
uint8_t DayOfWeek() const
{
return _dayOfWeek;
}
uint8_t ControlFlags() const
{
return _flags;
}
bool operator == (const PCF8563Alarm& other) const
{
return (_dayOfMonth == other._dayOfMonth &&
_hour == other._hour &&
_minute == other._minute &&
_dayOfWeek == other._dayOfWeek &&
_flags == other._flags);
}
bool operator != (const PCF8563Alarm& other) const
{
return !(*this == other);
}
protected:
uint8_t _flags;
uint8_t _dayOfMonth;
uint8_t _hour;
uint8_t _minute;
uint8_t _dayOfWeek;
};
template<class T_WIRE_METHOD> class RtcPCF8563
{
public:
RtcPCF8563(T_WIRE_METHOD& wire) :
_wire(wire),
_lastError(Rtc_Wire_Error_None)
{
}
void Begin()
{
_wire.begin();
}
void Begin(int sda, int scl)
{
_wire.begin(sda, scl);
}
uint8_t LastError()
{
return _lastError;
}
bool IsDateTimeValid()
{
uint8_t status = getReg(PCF8563_REG_INTEGRITY);
return (!(status & _BV(PCF8563_INTEGRITY_VALID)) && (_lastError == Rtc_Wire_Error_None));
}
bool GetIsRunning()
{
uint8_t creg = getReg(PCF8563_REG_CONTROL);
return (!(creg & _BV(PCF8563_CONTROL_CLOCK)) && (_lastError == Rtc_Wire_Error_None));
}
void SetIsRunning(bool isRunning)
{
uint8_t creg = getReg(PCF8563_REG_CONTROL);
if (isRunning)
{
creg &= ~_BV(PCF8563_CONTROL_CLOCK);
}
else
{
creg |= _BV(PCF8563_CONTROL_CLOCK);
}
setReg(PCF8563_REG_CONTROL, creg);
}
void SetDateTime(const RtcDateTime& dt)
{
// the invalid flag is part of seconds,
// so is automatically cleared when it is set
// set the date time
_wire.beginTransmission(PCF8563_ADDRESS);
_wire.write(PCF8563_REG_TIMEDATE);
_wire.write(Uint8ToBcd(dt.Second()));
_wire.write(Uint8ToBcd(dt.Minute()));
_wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only
_wire.write(Uint8ToBcd(dt.Day()));
// RTC Hardware Day of Week is the same as RtcDateTime
_wire.write(dt.DayOfWeek());
uint8_t year = dt.Year() - 2000;
uint8_t centuryFlag = 0;
if (year >= 100)
{
year -= 100;
centuryFlag = _BV(7);
}
_wire.write(Uint8ToBcd(dt.Month()) | centuryFlag);
_wire.write(Uint8ToBcd(year));
_lastError = _wire.endTransmission();
}
RtcDateTime GetDateTime()
{
_wire.beginTransmission(PCF8563_ADDRESS);
_wire.write(PCF8563_REG_TIMEDATE);
_lastError = _wire.endTransmission();
if (_lastError != Rtc_Wire_Error_None)
{
return RtcDateTime(0);
}
size_t bytesRead = _wire.requestFrom(PCF8563_ADDRESS, PCF8563_REG_TIMEDATE_SIZE);
if (PCF8563_REG_TIMEDATE_SIZE != bytesRead)
{
_lastError = Rtc_Wire_Error_Unspecific;
return RtcDateTime(0);
}
uint8_t second = BcdToUint8(_wire.read() & 0x7F);
uint8_t minute = BcdToUint8(_wire.read());
uint8_t hour = BcdToBin24Hour(_wire.read());
uint8_t dayOfMonth = BcdToUint8(_wire.read());
_wire.read(); // throwing away day of week as we calculate it
uint8_t monthRaw = _wire.read();
uint16_t year = BcdToUint8(_wire.read()) + 2000;
if (monthRaw & _BV(7)) // century wrap flag
{
year += 100;
}
uint8_t month = BcdToUint8(monthRaw & 0x7f);
return RtcDateTime(year, month, dayOfMonth, hour, minute, second);
}
void SetSquareWavePin(PCF8563SquareWavePinMode pinMode)
{
setReg(PCF8563_REG_CLKOUT_CONTROL, pinMode);
}
void SetAlarm(const PCF8563Alarm& alarm)
{
_wire.beginTransmission(PCF8563_ADDRESS);
_wire.write(PCF8563_REG_ALARM);
uint8_t matchFlag = (alarm.ControlFlags() & PCF8563AlarmControl_MinuteMatch) ? 0x00 : 0x80;
_wire.write(Uint8ToBcd(alarm.Minute()) | matchFlag);
matchFlag = (alarm.ControlFlags() & PCF8563AlarmControl_HourMatch) ? 0x00 : 0x80;
_wire.write(Uint8ToBcd(alarm.Hour()) | matchFlag); // 24 hour mode only
matchFlag = (alarm.ControlFlags() & PCF8563AlarmControl_DayOfMonthMatch) ? 0x00 : 0x80;
_wire.write(Uint8ToBcd(alarm.DayOfMonth()) | matchFlag);
matchFlag = (alarm.ControlFlags() & PCF8563AlarmControl_DayOfWeekMatch) ? 0x00 : 0x80;
_wire.write(Uint8ToBcd(alarm.DayOfWeek()) | matchFlag);
_lastError = _wire.endTransmission();
if (_lastError == Rtc_Wire_Error_None)
{
// enable alarm
uint8_t sreg = getReg(PCF8563_REG_STATUS);
sreg &= ~_BV(PCF8563_STATUS_AF);
sreg |= _BV(PCF8563_STATUS_AIE);
setReg(PCF8563_REG_STATUS, sreg);
}
}
void StopAlarm()
{
uint8_t sreg = getReg(PCF8563_REG_STATUS);
sreg &= ~(_BV(PCF8563_STATUS_AIE) | _BV(PCF8563_STATUS_AF));
setReg(PCF8563_REG_STATUS, sreg);
}
void SetTimer(PCF8563TimerMode mode, uint8_t time)
{
_wire.beginTransmission(PCF8563_ADDRESS);
_wire.write(PCF8563_REG_TIMER_CONTROL);
_wire.write(mode);
_wire.write(time);
_lastError = _wire.endTransmission();
if (_lastError == Rtc_Wire_Error_None)
{
// enable timer
uint8_t sreg = getReg(PCF8563_REG_STATUS);
sreg &= ~_BV(PCF8563_STATUS_TF);
sreg |= _BV(PCF8563_STATUS_TIE) | _BV(PCF8563_STATUS_TITP);
setReg(PCF8563_REG_STATUS, sreg);
}
}
void StopTimer()
{
uint8_t sreg = getReg(PCF8563_REG_STATUS);
sreg &= ~_BV(PCF8563_STATUS_TF);
sreg &= ~(_BV(PCF8563_STATUS_TIE) | _BV(PCF8563_STATUS_TITP));
setReg(PCF8563_REG_STATUS, sreg);
if (_lastError == Rtc_Wire_Error_None)
{
setReg(PCF8563_REG_TIMER_CONTROL, PCF8563TimerMode_None);
}
}
// Latch must be called after an alarm otherwise it will not
// trigger again?
bool LatchAlarmTriggeredFlag()
{
uint8_t sreg = getReg(PCF8563_REG_STATUS);
bool triggered = false;
if (sreg & _BV(PCF8563_STATUS_AF))
{
triggered = true;
sreg &= ~_BV(PCF8563_STATUS_AF);
setReg(PCF8563_REG_STATUS, sreg);
}
return triggered;
}
bool LatchTimerTriggeredFlag()
{
uint8_t sreg = getReg(PCF8563_REG_STATUS);
bool triggered = false;
if (sreg & _BV(PCF8563_STATUS_TF))
{
triggered = true;
sreg &= ~_BV(PCF8563_STATUS_TF);
setReg(PCF8563_REG_STATUS, sreg);
}
return triggered;
}
bool GetAlarmTriggered()
{
uint8_t sreg = getReg(PCF8563_REG_STATUS);
return (sreg & _BV(PCF8563_STATUS_AF));
}
bool GetTimerTriggered()
{
uint8_t sreg = getReg(PCF8563_REG_STATUS);
return (sreg & _BV(PCF8563_STATUS_TF));
}
private:
T_WIRE_METHOD& _wire;
uint8_t _lastError;
uint8_t getReg(uint8_t regAddress)
{
_wire.beginTransmission(PCF8563_ADDRESS);
_wire.write(regAddress);
_lastError = _wire.endTransmission();
if (_lastError != Rtc_Wire_Error_None)
{
return 0;
}
// control register
size_t bytesRead = _wire.requestFrom(PCF8563_ADDRESS, (uint8_t)1);
if (1 != bytesRead)
{
_lastError = Rtc_Wire_Error_Unspecific;
return 0;
}
uint8_t regValue = _wire.read();
return regValue;
}
void setReg(uint8_t regAddress, uint8_t regValue)
{
_wire.beginTransmission(PCF8563_ADDRESS);
_wire.write(regAddress);
_wire.write(regValue);
_lastError = _wire.endTransmission();
}
};

View File

@@ -0,0 +1,174 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
class RtcTemperature
{
public:
// Constructor
// a) Merge RTC registers into signed scaled temperature (x256),
// then bind to RTC resolution.
// | r11h | DP | r12h |
// Bit: 15 14 13 12 11 10 9 8 . 7 6 5 4 3 2 1 0 -1 -2
// s i i i i i i i . f f 0 0 0 0 0 0
//
// b) Rescale to (x4) by right-shifting (6) bits
// | | DP | |
// Bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 . 1 0 -1 -2
// s s s s s s s i i i i i i i f f 0 0
RtcTemperature(int8_t highByteDegreesC, uint8_t lowByteDegreesC)
{
int16_t scaledDegC = ((highByteDegreesC << 8) | (lowByteDegreesC & 0xC0)) >> 6;
_centiDegC = scaledDegC * 100 / 4;
}
RtcTemperature(int16_t centiDegC = 0) :
_centiDegC(centiDegC)
{
}
// Float temperature Celsius
float AsFloatDegC()
{
return (float)_centiDegC / 100.0f;
}
// Float temperature Fahrenheit
float AsFloatDegF()
{
return AsFloatDegC() * 1.8f + 32.0f;
}
// centi degrees (1/100th of a degree),
int16_t AsCentiDegC()
{
return _centiDegC;
}
void Print(Stream& target, uint8_t decimals = 2, char decimal = '.')
{
int16_t decimalDivisor = 1;
int16_t integerPart;
int16_t decimalPart;
{
int16_t rounded = abs(_centiDegC);
// round up as needed
if (decimals == 0)
{
rounded += 50;
}
else if (decimals == 1)
{
rounded += 5;
decimalDivisor = 10;
}
integerPart = rounded / 100;
decimalPart = (rounded % 100) / decimalDivisor;
}
// test for zero before printing negative sign to not print-0.00
if (_centiDegC < 0 && (integerPart != 0 || decimalPart != 0))
{
target.print('-');
}
// print integer part
target.print(integerPart);
// print decimal part
if (decimals != 0)
{
target.print(decimal);
if (decimalPart != 0)
{
target.print(decimalPart);
}
else
{
// append zeros as requested
while (decimals > 0)
{
target.print('0');
decimals--;
}
}
}
}
bool operator==(const RtcTemperature& other) const
{
return (_centiDegC == other._centiDegC);
};
bool operator>(const RtcTemperature& other) const
{
return (_centiDegC > other._centiDegC);
};
bool operator<(const RtcTemperature& other) const
{
return (_centiDegC < other._centiDegC);
};
bool operator>=(const RtcTemperature& other) const
{
return (_centiDegC >= other._centiDegC);
};
bool operator<=(const RtcTemperature& other) const
{
return (_centiDegC <= other._centiDegC);
};
bool operator!=(const RtcTemperature& other) const
{
return (_centiDegC != other._centiDegC);
};
RtcTemperature operator-(const RtcTemperature& right)
{
RtcTemperature result;
result._centiDegC = (_centiDegC - right._centiDegC);
return result;
}
RtcTemperature operator+(const RtcTemperature& right)
{
RtcTemperature result;
result._centiDegC = (_centiDegC + right._centiDegC);
return result;
}
protected:
int16_t _centiDegC; // 1/100th of a degree temperature (100 x degC)
};

View File

@@ -0,0 +1,113 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
struct RtcTimeZone
{
const char* abbr; // progmem
int32_t offset;
// searches the given table for the abbr match
// result - the found entry
// abbrSearch - the abbreviation to search for
// table - the PROGMEM table of RtcTimeZone in alphabetical order
// count - the number of entries in the table
// returns: 0 for not found, strlen of abbr if found
//
static size_t BinarySearchProgmemTable(RtcTimeZone* result,
const char* abbrSearch,
const RtcTimeZone* table,
size_t countTable)
{
size_t indexFront = 0;
size_t indexEnd = countTable;
size_t index = countTable / 2;
// binary search
//
while (indexFront < indexEnd)
{
RtcTimeZone entry;
// copy the entry from progmem
// string members still reside in PROGMEM though
memcpy_P(&entry, table + index, sizeof(RtcTimeZone));
auto match = strncmp_P(abbrSearch, entry.abbr, 4);
if (0 == match)
{
// found
*result = entry;
return strlen_P(entry.abbr);
}
else if (match < 0)
{
indexEnd = index;
}
else
{
indexFront = index + 1;
}
// pick one between the front and end
index = (indexEnd - indexFront) / 2 + indexFront;
}
return 0;
}
// searches the given table for the abbr match
// result - the found entry
// abbrSearch - the abbreviation to search for
// table - the PROGMEM table of RtcTimeZone to search
// count - the number of entries in the table
// returns: 0 for not found, strlen of abbr if found
//
static size_t LinearSearchProgmemTable(RtcTimeZone* result,
const char* abbrSearch,
const RtcTimeZone* table,
size_t countTable)
{
for (size_t index = 0; index < countTable; index++)
{
RtcTimeZone entry;
// copy the entry from progmem
// string members still reside in PROGMEM though
memcpy_P(&entry, table + index, sizeof(RtcTimeZone));
if (0 == strncmp_P(abbrSearch, entry.abbr, 4))
{
*result = entry;
return strlen_P(entry.abbr);
}
}
return 0;
}
};

View File

@@ -0,0 +1,59 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include <Arduino.h>
#include "RtcUtility.h"
uint8_t BcdToUint8(uint8_t val)
{
return val - 6 * (val >> 4);
}
uint8_t Uint8ToBcd(uint8_t val)
{
return val + 6 * (val / 10);
}
uint8_t BcdToBin24Hour(uint8_t bcdHour)
{
uint8_t hour;
if (bcdHour & 0x40)
{
// 12 hour mode, convert to 24
bool isPm = ((bcdHour & 0x20) != 0);
hour = BcdToUint8(bcdHour & 0x1f);
if (isPm)
{
hour += 12;
}
}
else
{
hour = BcdToUint8(bcdHour);
}
return hour;
}

View File

@@ -0,0 +1,87 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
// ESP32 complains if not included
#if defined(ARDUINO_ARCH_ESP32)
#include <inttypes.h>
#endif
#if !defined(countof)
#define countof(a) (sizeof(a) / sizeof(a[0]))
#endif
// Arduino has no standard for attributing methods used for ISRs
// even though some platforms require it, so to simplify the problem
// for end users, this provides a standard ISR_ATTR
#if !defined(ISR_ATTR)
#if defined(ARDUINO_ARCH_ESP8266)
#define ISR_ATTR ICACHE_RAM_ATTR
#elif defined(ARDUINO_ARCH_ESP32)
#define ISR_ATTR ICACHE_RAM_ATTR
#else
#define ISR_ATTR
#endif
#endif // !defined(ISR_ATTR)
// some platforms do not come with STL or properly defined one, specifically functional
// if you see...
// undefined reference to `std::__throw_bad_function_call()'
// ...then you can either add the platform symbol to the list so RTC_NO_STL gets defined or
// go to boards.txt and enable c++ by adding (teensy31.build.flags.libs=-lstdc++) and set to "smallest code" option in Arduino
//
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR) || defined(STM32L432xx) || defined(STM32L476xx) || defined(ARDUINO_ARCH_SAM)
#define RTC_NO_STL 1
#endif
// While WIRE has return codes, there is no standard definition of what they are
// within any headers; they are only documented on the website here
// https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/
// So we define our own "standard" for this RTC library that match those
//
enum Rtc_Wire_Error
{
Rtc_Wire_Error_None = 0,
Rtc_Wire_Error_TxBufferOverflow,
Rtc_Wire_Error_NoAddressableDevice,
Rtc_Wire_Error_UnsupportedRequest,
Rtc_Wire_Error_Unspecific,
Rtc_Wire_Error_CommunicationTimeout
};
// for some reason, the DUE board support does not define this, even though other non AVR archs do
#ifndef _BV
#define _BV(b) (1UL << (b))
#endif
extern uint8_t BcdToUint8(uint8_t val);
extern uint8_t Uint8ToBcd(uint8_t val);
extern uint8_t BcdToBin24Hour(uint8_t bcdHour);

View File

@@ -0,0 +1,127 @@
/*-------------------------------------------------------------------------
RTC library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/Rtc)
-------------------------------------------------------------------------
This file is part of the Makuna/Rtc library.
Rtc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Rtc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with Rtc. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include <Arduino.h>
#pragma once
//ThreeWire command Read/Write flag
const uint8_t THREEWIRE_READFLAG = 0x01;
class ThreeWire
{
public:
ThreeWire(uint8_t ioPin, uint8_t clkPin, uint8_t cePin) :
_ioPin(ioPin),
_clkPin(clkPin),
_cePin(cePin)
{
}
void begin() {
resetPins();
}
void end() {
resetPins();
}
void beginTransmission(uint8_t command) {
digitalWrite(_cePin, LOW); // default, not enabled
pinMode(_cePin, OUTPUT);
digitalWrite(_clkPin, LOW); // default, clock low
pinMode(_clkPin, OUTPUT);
pinMode(_ioPin, OUTPUT);
digitalWrite(_cePin, HIGH); // start the session
delayMicroseconds(4); // tCC = 4us
write(command, (command & THREEWIRE_READFLAG) == THREEWIRE_READFLAG);
}
void endTransmission() {
digitalWrite(_cePin, LOW);
delayMicroseconds(4); // tCWH = 4us
}
void write(uint8_t value, bool isDataRequestCommand = false) {
for (uint8_t bit = 0; bit < 8; bit++) {
digitalWrite(_ioPin, value & 0x01);
delayMicroseconds(1); // tDC = 200ns
// clock up, data is read by DS1302
digitalWrite(_clkPin, HIGH);
delayMicroseconds(1); // tCH = 1000ns, tCDH = 800ns
// for the last bit before a read
// Set IO line for input before the clock down
if (bit == 7 && isDataRequestCommand) {
pinMode(_ioPin, INPUT);
}
digitalWrite(_clkPin, LOW);
delayMicroseconds(1); // tCL=1000ns, tCDD=800ns
value >>= 1;
}
}
uint8_t read() {
uint8_t value = 0;
for (uint8_t bit = 0; bit < 8; bit++) {
// first bit is present on io pin, so only clock the other
// bits
value |= (digitalRead(_ioPin) << bit);
// Clock up, prepare for next
digitalWrite(_clkPin, HIGH);
delayMicroseconds(1);
// Clock down, value is ready after some time.
digitalWrite(_clkPin, LOW);
delayMicroseconds(1); // tCL=1000ns, tCDD=800ns
}
return value;
}
private:
const uint8_t _ioPin;
const uint8_t _clkPin;
const uint8_t _cePin;
void resetPins() {
// just making sure they are in a default low power use state
// as required state is set when transmissions are started
// three wire devices have internal pull downs so they will be low
pinMode(_clkPin, INPUT);
pinMode(_ioPin, INPUT);
pinMode(_cePin, INPUT);
}
};