forked from GuruSR/SmallRTC
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSmallRTC.cpp
More file actions
230 lines (212 loc) · 9.64 KB
/
Copy pathSmallRTC.cpp
File metadata and controls
230 lines (212 loc) · 9.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#include "SmallRTC.h"
/* SmallRTC by GuruSR (https://www.github.com/GuruSR/SmallRTC)
* Originally forked from WatchyRTC with a variety of fixes and improvements.
*
* Version 1.0, January 2, 2022
* Version 1.1, January 4, 2022 : Correct Months from 1 to 12 to 0 to 11.
* Version 1.2, January 7, 2022 : Added atMinuteWake to enable minute based wakeup.
* Version 1.3, January 28, 2022 : Corrected atMinuteWake missing AlarmInterrupt & moved ClearAlarm around.
* Version 1.4, January 29, 2022 : Added Make & Break Time functions to MATCH TimeLib & time.h by reducing Month and Wday.
* Version 1.5, January 30, 2022 : Fixed atMinuteWake to require extra values for DS3231M to work properly.
* Version 1.6, February 17, 2022 : Added isOperating for the DS3231M to detect if the Oscillator has stopped.
* Version 1.7, March 15, 2022 : Added Status, Squarewave & Timer reset to init for PCF8563.
* Version 1.8, March 29, 2022 : Added support for 2 variations of PCF8563 battery location.
* Version 1.9, April 4, 2022 : Added support for DS3232RTC version 2.0 by customizing defines.
*
* This library offers an alternative to the WatchyRTC library, but also provides a 100% time.h and timelib.h
* compliant RTC library.
*
* This library is adapted to work with the Arduino ESP32 and any other project that has similar libraries.
*
* MIT License
*
* Copyright (c) 2022 GuruSR
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
RTC_DATA_ATTR uint8_t RTCType;
RTC_DATA_ATTR uint32_t ADC_PIN;
RTC_DATA_ATTR bool Operational;
SmallRTC::SmallRTC()
: rtc_ds(false) {}
void SmallRTC::init(){
uint8_t controlReg, mask;
RTCType = Unknown;
ADC_PIN = 0;
Wire.beginTransmission(RTC_DS_ADDR);
if(Wire.endTransmission() == 0) {
RTCType = DS3231; ADC_PIN = 33;
controlReg = rtc_ds.readRTC(0x0E);
mask = _BV(7);
if (controlReg & mask){
controlReg &= ~mask;
rtc_ds.writeRTC(0x0E, controlReg);
}
checkStatus();
rtc_ds.squareWave(DS3232RTC::SQWAVE_NONE); //disable square wave output
rtc_ds.alarm(DS3232RTC::ALARM_2);
rtc_ds.setAlarm(DS3232RTC::ALM2_EVERY_MINUTE, 0, 0, 0, 0); //alarm wakes up Watchy every minute
rtc_ds.alarmInterrupt(DS3232RTC::ALARM_2, true); //enable alarm interrupt
Operational = true;
checkStatus();
}else{
Wire.beginTransmission(RTC_PCF_ADDR);
if(Wire.endTransmission() == 0) {
RTCType = PCF8563; Operational = true; rtc_pcf.clearStatus(); Wire.beginTransmission(0xa3>>1); Wire.write(0x09); Wire.write(0x80); Wire.write(0x80); Wire.write(0x80); Wire.write(0x80); Wire.write(0x0); Wire.write(0x0); Wire.endTransmission();
if ((analogReadMilliVolts(34) / 500.0f) > 1) ADC_PIN = 34;
if ((analogReadMilliVolts(35) / 500.0f) > 1) ADC_PIN = 35;
} // Clear SquareWave & Timer
}
}
void SmallRTC::setDateTime(String datetime){
tmElements_t tm, tst;
tm.Year = CalendarYrToTm(_getValue(datetime, ':', 0).toInt()); //YYYY - 1970
tm.Month = _getValue(datetime, ':', 1).toInt();
tm.Day = _getValue(datetime, ':', 2).toInt();
tm.Hour = _getValue(datetime, ':', 3).toInt();
tm.Minute = _getValue(datetime, ':', 4).toInt();
tm.Second = _getValue(datetime, ':', 5).toInt();
time_t t = SmallRTC::MakeTime(tm); //make and break to calculate tm.Wday
SmallRTC::BreakTime(t, tm);
if (RTCType == DS3231){
tm.Wday++;
rtc_ds.write(tm);
rtc_ds.read(tst);
checkStatus();
}else if (RTCType == PCF8563){
//day, weekday, month, century(1=1900, 0=2000), year(0-99)
rtc_pcf.setDate(tm.Day, tm.Wday, tm.Month, 0, tmYearToY2k(tm.Year)); //DS3231 has Wday range of 1-7, but TimeLib & PCF8563 require day of week in 0-6 range.
//hr, min, sec
rtc_pcf.setTime(tm.Hour, tm.Minute, tm.Second);
tst.Year = y2kYearToTm(rtc_pcf.getYear());
tst.Month = rtc_pcf.getMonth() - 1;
tst.Day = rtc_pcf.getDay();
tst.Hour = rtc_pcf.getHour();
tst.Minute = rtc_pcf.getMinute();
}
if (Operational) Operational = (tm.Year == tst.Year && tm.Month == tst.Month && tm.Day == tst.Day && tm.Hour == tst.Hour && tm.Minute == tst.Minute);
}
void SmallRTC::read(tmElements_t &tm){
if (RTCType == DS3231){
rtc_ds.read(tm);
tm.Wday--;
tm.Month--;
checkStatus();
}else if (RTCType == PCF8563){
tm.Year = y2kYearToTm(rtc_pcf.getYear());
tm.Month = rtc_pcf.getMonth() - 1;
tm.Day = rtc_pcf.getDay();
tm.Wday = rtc_pcf.getWeekday();
tm.Hour = rtc_pcf.getHour();
tm.Minute = rtc_pcf.getMinute();
tm.Second = rtc_pcf.getSecond();
}
}
void SmallRTC::set(tmElements_t tm){
tmElements_t tst;
if(RTCType == DS3231){
tm.Wday++;
tm.Month++;
rtc_ds.write(tm);
rtc_ds.read(tst);
checkStatus();
}else if (RTCType == PCF8563){
time_t t = SmallRTC::MakeTime(tm); //make and break to calculate tm.Wday
SmallRTC::BreakTime(t, tm);
//day, weekday, month, century(1=1900, 0=2000), year(0-99)
rtc_pcf.setDate(tm.Day, tm.Wday, tm.Month + 1, 0, tmYearToY2k(tm.Year)); //DS3231 has Wday range of 1-7, but TimeLib & PCF8563 require day of week in 0-6 range.
//hr, min, sec
rtc_pcf.setTime(tm.Hour, tm.Minute, tm.Second);
tst.Year = y2kYearToTm(rtc_pcf.getYear());
tst.Month = rtc_pcf.getMonth() - 1;
tst.Day = rtc_pcf.getDay();
tst.Hour = rtc_pcf.getHour();
tst.Minute = rtc_pcf.getMinute();
}
if (Operational) Operational = (tm.Year == tst.Year && tm.Month == tst.Month && tm.Day == tst.Day && tm.Hour == tst.Hour && tm.Minute == tst.Minute);
}
void SmallRTC::resetWake(){
if (RTCType == DS3231){
rtc_ds.clearAlarm(DS3232RTC::ALARM_2); //resets the alarm flag in the RTC
checkStatus();
}else if (RTCType == PCF8563){
rtc_pcf.clearAlarm(); //resets the alarm flag in the RTC
}
}
void SmallRTC::nextMinuteWake(bool Enabled){
if (RTCType == DS3231){
rtc_ds.setAlarm(DS3232RTC::ALM2_EVERY_MINUTE, 0, 0, 0, 0); //alarm wakes up Watchy every minute
rtc_ds.clearAlarm(DS3232RTC::ALARM_2); //resets the alarm flag in the RTC
rtc_ds.alarmInterrupt(DS3232RTC::ALARM_2, Enabled); // Turn interrupt on or off based on Enabled.
checkStatus();
}else if (RTCType == PCF8563){
rtc_pcf.clearAlarm(); //resets the alarm flag in the RTC
if (Enabled) rtc_pcf.setAlarm(constrain((rtc_pcf.getMinute() + 1), 0, 59), 99, 99, 99); //set alarm to trigger 1 minute from now
else rtc_pcf.resetAlarm();
}
}
void SmallRTC::atMinuteWake(uint8_t Minute, uint8_t Hour, uint8_t DayOfWeek, bool Enabled){
if (RTCType == DS3231){
rtc_ds.setAlarm(DS3232RTC::ALM2_MATCH_MINUTES,constrain(Minute, 0, 59) , Hour, DayOfWeek);
rtc_ds.clearAlarm(DS3232RTC::ALARM_2); //resets the alarm flag in the RTC
rtc_ds.alarmInterrupt(DS3232RTC::ALARM_2, Enabled); // Turn interrupt on or off based on Enabled.
checkStatus();
}else if (RTCType == PCF8563){
rtc_pcf.clearAlarm(); //resets the alarm flag in the RTC
if (Enabled) rtc_pcf.setAlarm(constrain(Minute, 0, 59), 99, 99, 99);
else rtc_pcf.resetAlarm();
}
}
uint8_t SmallRTC::temperature(){
if(RTCType == DS3231) return rtc_ds.temperature();
return 255; //error
}
uint8_t SmallRTC::getType(){ return RTCType; }
uint32_t SmallRTC::getADCPin(){ return ADC_PIN; }
time_t SmallRTC::MakeTime(tmElements_t TM){
TM.Month++;
TM.Wday++;
return makeTime(TM);
}
void SmallRTC::BreakTime(time_t &T, tmElements_t &TM){
breakTime(T,TM);
TM.Month--;
TM.Wday--;
}
bool SmallRTC::isOperating() { return Operational; }
void SmallRTC::checkStatus() { if (Operational) Operational = !rtc_ds.oscStopped(true); }
float SmallRTC::getRTCBattery(bool Critical){
if (RTCType == PCF8563) return (Critical ? 3.45 : 3.58);
else if (RTCType == DS3231) return (Critical ? 3.69 : 3.75);
return 3.4;
}
String SmallRTC::_getValue(String data, char separator, int index)
{
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = data.length()-1;
for(int i=0; i<=maxIndex && found<=index; i++){
if(data.charAt(i)==separator || i==maxIndex){
found++;
strIndex[0] = strIndex[1]+1;
strIndex[1] = (i == maxIndex) ? i+1 : i;
}
}
return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}