|
| 1 | +// A library for handling real-time clocks, dates, etc. |
| 2 | +// 2010-02-04 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php |
| 3 | + |
| 4 | +#include <Wire.h> |
| 5 | +#include <avr/pgmspace.h> |
| 6 | +#include "RTClib.h" |
| 7 | +#include <Arduino.h> |
| 8 | + |
| 9 | +#define DS1307_ADDRESS 0x68 |
| 10 | + |
| 11 | +#define PCF8563_ADDRESS 0x51 |
| 12 | +#define PCF8563_SEC_ADDR 0x02 |
| 13 | + |
| 14 | +#define SECONDS_PER_DAY 86400L |
| 15 | + |
| 16 | +//////////////////////////////////////////////////////////////////////////////// |
| 17 | +// utility code, some of this could be exposed in the DateTime API if needed |
| 18 | + |
| 19 | +static uint8_t daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 }; |
| 20 | + |
| 21 | +// number of days since 2000/01/01, valid for 2001..2099 |
| 22 | +static uint16_t date2days(uint16_t y, uint8_t m, uint8_t d) { |
| 23 | + if (y >= 2000) |
| 24 | + y -= 2000; |
| 25 | + uint16_t days = d; |
| 26 | + for (uint8_t i = 1; i < m; ++i) |
| 27 | + days += pgm_read_byte(daysInMonth + i - 1); |
| 28 | + if (m > 2 && y % 4 == 0) |
| 29 | + ++days; |
| 30 | + return days + 365 * y + (y + 3) / 4 - 1; |
| 31 | +} |
| 32 | + |
| 33 | +static long time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s) { |
| 34 | + return ((days * 24L + h) * 60 + m) * 60 + s; |
| 35 | +} |
| 36 | + |
| 37 | +//////////////////////////////////////////////////////////////////////////////// |
| 38 | +// DateTime implementation - ignores time zones and DST changes |
| 39 | +// NOTE: also ignores leap seconds, see http://en.wikipedia.org/wiki/Leap_second |
| 40 | + |
| 41 | +DateTime::DateTime (long t) { |
| 42 | + ss = t % 60; |
| 43 | + t /= 60; |
| 44 | + mm = t % 60; |
| 45 | + t /= 60; |
| 46 | + hh = t % 24; |
| 47 | + uint16_t days = t / 24; |
| 48 | + uint8_t leap; |
| 49 | + for (yOff = 0; ; ++yOff) { |
| 50 | + leap = yOff % 4 == 0; |
| 51 | + if (days < 365 + leap) |
| 52 | + break; |
| 53 | + days -= 365 + leap; |
| 54 | + } |
| 55 | + for (m = 1; ; ++m) { |
| 56 | + uint8_t daysPerMonth = pgm_read_byte(daysInMonth + m - 1); |
| 57 | + if (leap && m == 2) |
| 58 | + ++daysPerMonth; |
| 59 | + if (days < daysPerMonth) |
| 60 | + break; |
| 61 | + days -= daysPerMonth; |
| 62 | + } |
| 63 | + d = days + 1; |
| 64 | +} |
| 65 | + |
| 66 | +DateTime::DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) { |
| 67 | + if (year >= 2000) |
| 68 | + year -= 2000; |
| 69 | + yOff = year; |
| 70 | + m = month; |
| 71 | + d = day; |
| 72 | + hh = hour; |
| 73 | + mm = min; |
| 74 | + ss = sec; |
| 75 | +} |
| 76 | + |
| 77 | +static uint8_t conv2d(const char* p) { |
| 78 | + uint8_t v = 0; |
| 79 | + if ('0' <= *p && *p <= '9') |
| 80 | + v = *p - '0'; |
| 81 | + return 10 * v + *++p - '0'; |
| 82 | +} |
| 83 | + |
| 84 | +// A convenient constructor for using "the compiler's time": |
| 85 | +// DateTime now (__DATE__, __TIME__); |
| 86 | +// NOTE: using PSTR would further reduce the RAM footprint |
| 87 | +DateTime::DateTime (const char* date, const char* time) { |
| 88 | + // sample input: date = "Dec 26 2009", time = "12:34:56" |
| 89 | + yOff = conv2d(date + 9); |
| 90 | + // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec |
| 91 | + switch (date[0]) { |
| 92 | + case 'J': m = date[1] == 'a' ? 1 : m = date[2] == 'n' ? 6 : 7; break; |
| 93 | + case 'F': m = 2; break; |
| 94 | + case 'A': m = date[2] == 'r' ? 4 : 8; break; |
| 95 | + case 'M': m = date[2] == 'r' ? 3 : 5; break; |
| 96 | + case 'S': m = 9; break; |
| 97 | + case 'O': m = 10; break; |
| 98 | + case 'N': m = 11; break; |
| 99 | + case 'D': m = 12; break; |
| 100 | + } |
| 101 | + d = conv2d(date + 4); |
| 102 | + hh = conv2d(time); |
| 103 | + mm = conv2d(time + 3); |
| 104 | + ss = conv2d(time + 6); |
| 105 | +} |
| 106 | + |
| 107 | +uint8_t DateTime::dayOfWeek() const { |
| 108 | + uint16_t day = get() / SECONDS_PER_DAY; |
| 109 | + return (day + 6) % 7; // Jan 1, 2000 is a Saturday, i.e. returns 6 |
| 110 | +} |
| 111 | + |
| 112 | +long DateTime::get() const { |
| 113 | + uint16_t days = date2days(yOff, m, d); |
| 114 | + return time2long(days, hh, mm, ss); |
| 115 | +} |
| 116 | + |
| 117 | +//////////////////////////////////////////////////////////////////////////////// |
| 118 | +// RTC_DS1307 implementation |
| 119 | + |
| 120 | +void RTC_DS1307::adjust(const DateTime& dt) { |
| 121 | + Wire.beginTransmission(DS1307_ADDRESS); |
| 122 | + Wire.write((byte) 0); |
| 123 | + Wire.write(bin2bcd(dt.second())); |
| 124 | + Wire.write(bin2bcd(dt.minute())); |
| 125 | + Wire.write(bin2bcd(dt.hour())); |
| 126 | + Wire.write(bin2bcd(0)); |
| 127 | + Wire.write(bin2bcd(dt.day())); |
| 128 | + Wire.write(bin2bcd(dt.month())); |
| 129 | + Wire.write(bin2bcd(dt.year() - 2000)); |
| 130 | + Wire.write((byte) 0); |
| 131 | + Wire.endTransmission(); |
| 132 | +} |
| 133 | + |
| 134 | +DateTime RTC_DS1307::now() { |
| 135 | + Wire.beginTransmission(DS1307_ADDRESS); |
| 136 | + Wire.write((byte) 0); |
| 137 | + Wire.endTransmission(); |
| 138 | + |
| 139 | + Wire.requestFrom(DS1307_ADDRESS, 7); |
| 140 | + uint8_t ss = bcd2bin(Wire.read()); |
| 141 | + uint8_t mm = bcd2bin(Wire.read()); |
| 142 | + uint8_t hh = bcd2bin(Wire.read()); |
| 143 | + Wire.read(); |
| 144 | + uint8_t d = bcd2bin(Wire.read()); |
| 145 | + uint8_t m = bcd2bin(Wire.read()); |
| 146 | + uint16_t y = bcd2bin(Wire.read()) + 2000; |
| 147 | + |
| 148 | + return DateTime (y, m, d, hh, mm, ss); |
| 149 | +} |
| 150 | + |
| 151 | +/////////////////////////////////////////////////////////////////////////////// |
| 152 | +// RTC_PCF8563 implementation |
| 153 | +// contributed by @mariusster, see http://forum.jeelabs.net/comment/1902 |
| 154 | + |
| 155 | +void RTC_PCF8563::adjust(const DateTime& dt) { |
| 156 | + Wire.beginTransmission(PCF8563_ADDRESS); |
| 157 | + Wire.write((byte) 0); |
| 158 | + Wire.write((byte) 0x0); // control/status1 |
| 159 | + Wire.write((byte) 0x0); // control/status2 |
| 160 | + Wire.write(bin2bcd(dt.second())); // set seconds |
| 161 | + Wire.write(bin2bcd(dt.minute())); // set minutes |
| 162 | + Wire.write(bin2bcd(dt.hour())); // set hour |
| 163 | + Wire.write(bin2bcd(dt.day())); // set day |
| 164 | + Wire.write((byte) 0x01); // set weekday |
| 165 | + Wire.write(bin2bcd(dt.month())); // set month, century to 1 |
| 166 | + Wire.write(bin2bcd(dt.year() - 2000)); // set year to 00-99 |
| 167 | + Wire.write((byte) 0x80); // minute alarm value reset to 00 |
| 168 | + Wire.write((byte) 0x80); // hour alarm value reset to 00 |
| 169 | + Wire.write((byte) 0x80); // day alarm value reset to 00 |
| 170 | + Wire.write((byte) 0x80); // weekday alarm value reset to 00 |
| 171 | + Wire.write((byte) 0x0); // set freqout 0= 32768khz, 1= 1hz |
| 172 | + Wire.write((byte) 0x0); // timer off |
| 173 | + Wire.endTransmission(); |
| 174 | +} |
| 175 | + |
| 176 | +DateTime RTC_PCF8563::now() { |
| 177 | + Wire.beginTransmission(PCF8563_ADDRESS); |
| 178 | + Wire.write(PCF8563_SEC_ADDR); |
| 179 | + Wire.endTransmission(); |
| 180 | + |
| 181 | + Wire.requestFrom(PCF8563_ADDRESS, 7); |
| 182 | + uint8_t ss = bcd2bin(Wire.read() & 0x7F); |
| 183 | + uint8_t mm = bcd2bin(Wire.read() & 0x7F); |
| 184 | + uint8_t hh = bcd2bin(Wire.read() & 0x3F); |
| 185 | + uint8_t d = bcd2bin(Wire.read() & 0x3F); |
| 186 | + Wire.read(); |
| 187 | + uint8_t m = bcd2bin(Wire.read()& 0x1F); |
| 188 | + uint16_t y = bcd2bin(Wire.read()) + 2000; |
| 189 | + |
| 190 | + return DateTime (y, m, d, hh, mm, ss); |
| 191 | +} |
| 192 | + |
| 193 | +//////////////////////////////////////////////////////////////////////////////// |
| 194 | +// RTC_Millis implementation |
| 195 | + |
| 196 | +long RTC_Millis::offset = 0; |
| 197 | + |
| 198 | +void RTC_Millis::adjust(const DateTime& dt) { |
| 199 | + offset = dt.get() - millis() / 1000; |
| 200 | +} |
| 201 | + |
| 202 | +DateTime RTC_Millis::now() { |
| 203 | + return offset + millis() / 1000; |
| 204 | +} |
| 205 | + |
| 206 | +//////////////////////////////////////////////////////////////////////////////// |
0 commit comments