77// Assumes the RTC is running and set to UTC.
88// A maximum of 7 outages (power down/up times) can be logged in the
99// RTC's SRAM.
10+ // The logging data structure is automatically initialized if not
11+ // present. An initialization can be forced with a button switch
12+ // connected from pin 9 to ground. Hold the button down
13+ // while resetting the MCU to initialize the logging data.
1014//
1115// Jack Christensen 23Aug2012
1216
1317#include < MCP79412RTC.h> // https://github.com/JChristensen/MCP79412RTC
14- #include < Streaming.h> // http://arduiniana.org/libraries/streaming/
15- #include < TimeLib.h> // https://github.com/PaulStoffregen/Time
18+ #include < Streaming.h> // https://github.com/janelia-arduino/Streaming
1619#include < Timezone.h> // https://github.com/JChristensen/Timezone
1720
18- #define FIRST_OUTAGE_ADDR 0x08 // address of first outage timestamps in RTC SRAM
19- #define OUTAGE_LENGTH 8 // 8 data bytes for each outage (start and end timestamps, both are time_t values)
20- #define MAX_OUTAGES 7 // maximum number of outage timestamp pairs that can be stored in SRAM
21- #define MAX_OUTAGE_ADDR FIRST_OUTAGE_ADDR + OUTAGE_LENGTH * (MAX_OUTAGES - 1 ) // last outage address
22- #define APP_ID 1 // APP_ID and 4 bytes of the RTC ID are stored in sram to provide
23- // a way to recognize that the logging data structure has been initialized
24- #define RTC_ID_LO 0x00 // lower 4 bytes of RTC unique ID are stored at sram addr 0x00
25- #define APP_ID_ADDR 0x04 // address of appID (1)
26- #define NBR_OUTAGES_ADDR 0x05 // address containing number of outages currently stored in SRAM
27- #define NEXT_OUTAGE_ADDR 0x06 // address containing pointer to next outage
28- #define RFU_ADDR 0x07 // reserved for future use
29-
30- // US Eastern Time Zone (New York, Detroit)
31- TimeChangeRule myDST = {" EDT" , Second, Sun, Mar, 2 , -240 }; // Daylight time = UTC - 4 hours
32- TimeChangeRule mySTD = {" EST" , First, Sun, Nov, 2 , -300 }; // Standard time = UTC - 5 hours
33- Timezone myTZ (myDST, mySTD);
34- TimeChangeRule *tcr; // pointer to the time change rule, used to get TZ abbrev
35- time_t utc, local, lastUTC;
21+ MCP79412RTC myRTC;
3622
3723void setup ()
3824{
25+ constexpr uint8_t initButton {9 };
26+ pinMode (initButton, INPUT_PULLUP);
27+ myRTC.begin ();
3928 Serial.begin (115200 );
29+ Serial << F ( " \n " __FILE__ " " __DATE__ " " __TIME__ " \n " );
4030
41- setSyncProvider (RTC .get ); // the function to get the time from the RTC
31+ setSyncProvider (myRTC .get ); // the function to get the time from the RTC
4232 Serial << " RTC SYNC" ;
4333 if (timeStatus ()!= timeSet) Serial << " FAIL" ;
4434 Serial << endl;
4535
46- // logClear();
36+ if (! digitalRead (initButton)) logClear ();
4737 logOutage ();
38+ // myRTC.dumpSRAM();
4839}
4940
41+ // US Eastern Time Zone (New York, Detroit)
42+ TimeChangeRule myDST = {" EDT" , Second, Sun, Mar, 2 , -240 }; // Daylight time = UTC - 4 hours
43+ TimeChangeRule mySTD = {" EST" , First, Sun, Nov, 2 , -300 }; // Standard time = UTC - 5 hours
44+ Timezone myTZ (myDST, mySTD);
45+ TimeChangeRule *tcr; // pointer to the time change rule, used to get TZ abbrev
46+
5047void loop ()
5148{
5249 // nothing here in loop() has anything to do with logging power outages,
53- // we just print the time every second so that something is happening.
54- utc = now () ;
55- if ( utc != lastUTC)
56- {
50+ // we just print the time once a minute so that something is happening.
51+ static time_t lastUTC ;
52+ time_t utc = now ();
53+ if ( minute (utc) != minute (lastUTC)) {
5754 lastUTC = utc;
58- local = myTZ.toLocal (utc, &tcr);
55+ time_t local = myTZ.toLocal (utc, &tcr);
5956 Serial << endl;
6057 printTime (utc, " UTC" );
6158 printTime (local, tcr -> abbrev);
6259 }
6360}
6461
62+ // constants for logging
63+ constexpr uint8_t
64+ firstOutageAddr {0x08 }, // address of first outage timestamps in RTC SRAM
65+ outageLength {8 }, // 8 data bytes for each outage (start and end timestamps, both are time_t values)
66+ maxOutages {7 }, // maximum number of outage timestamp pairs that can be stored in SRAM
67+ lastOutageAddr {firstOutageAddr + outageLength * (maxOutages - 1 )}, // last outage address
68+ appID {1 }, // appID and 4 bytes of the RTC ID are stored in sram to provide
69+ // a way to recognize that the logging data structure has been initialized
70+ rtcID {0x00 }, // lower 4 bytes of RTC unique ID are stored at sram addr 0x00
71+ appAddr {0x04 }, // address of appID in sram
72+ nbrOutagesAddr {0x05 }, // address containing number of outages currently stored in SRAM
73+ nextOutageAddr {0x06 }, // address containing pointer to next outage
74+ rfuAddr {0x07 }; // reserved for future use
75+
6576// initialize the log data structure in the RTC SRAM if needed.
6677// log a new outage if one occurred.
6778// print out the outages logged.
68- void logOutage ()
69- {
79+ void logOutage () {
7080 union {
7181 uint8_t b[8 ];
7282 struct {
7383 uint32_t hi;
7484 uint32_t lo;
7585 };
76- } uniqueID; // 8-byte RTC "unique ID" with access to upper and lower halves
77-
78- uint32_t loID; // lower half of the unique ID read from sram
79- uint8_t appID; // app ID read from sram
80- uint8_t nOutage; // number of outages stored in sram
81- uint8_t nextOutage; // address of next outage timestamps in sram
82- uint8_t outageAddr; // outage address in sram
83- time_t powerDown, powerUp; // power outage timestamps
84-
85- RTC.idRead (uniqueID.b ); // get the RTC's ID
86- loID = read32 (RTC_ID_LO); // if already initialized, the lower half of the ID is stored at SRAM addr 0x00,
87- appID = RTC.sramRead (APP_ID_ADDR); // and the app ID (1) is at addr 0x04.
86+ } uniqueID; // 8-byte RTC "unique ID" with access to upper and lower halves
87+
88+ myRTC.idRead (uniqueID.b ); // get the RTC's ID
89+ uint32_t loID = read32 (rtcID); // if already initialized, the lower half of the ID is stored in SRAM,
90+ uint8_t app = myRTC.sramRead (appAddr); // and also the app ID
8891 Serial << " RTC ID" ;
89- for (uint8_t i=0 ; i<8 ; i++)
90- {
92+ for (uint8_t i=0 ; i<8 ; i++) {
9193 Serial << (uniqueID.b [i] < 16 ? " 0" : " " ) << _HEX (uniqueID.b [i]);
9294 }
95+ Serial << endl;
9396
94- if ( loID != uniqueID.lo || appID != 1 ) // logging initialized?
95- {
96- write32 (RTC_ID_LO, uniqueID.lo ); // least significant half of the RTC unique ID
97- RTC.sramWrite (APP_ID_ADDR, APP_ID); // app ID
98- RTC.sramWrite (NBR_OUTAGES_ADDR, 0 ); // number of outages
99- RTC.sramWrite (NEXT_OUTAGE_ADDR, FIRST_OUTAGE_ADDR); // next location for outage times
100- RTC.sramWrite (RFU_ADDR, 0 ); // reserved for future use
97+ if ( loID != uniqueID.lo || app != appID ) { // logging initialized?
98+ write32 (rtcID, uniqueID.lo ); // least significant half of the RTC unique ID
99+ myRTC.sramWrite (appAddr, appID); // app ID
100+ myRTC.sramWrite (nbrOutagesAddr, 0 ); // number of outages
101+ myRTC.sramWrite (nextOutageAddr, firstOutageAddr); // next location for outage times
102+ myRTC.sramWrite (rfuAddr, 0 ); // reserved for future use
101103 Serial << " Logging initialized" << endl; // no, do it now
102104 }
103105
104106 // if an outage has occurred, record it
105- if ( RTC.powerFail (&powerDown, &powerUp) )
106- {
107- nOutage = RTC.sramRead (NBR_OUTAGES_ADDR);
108- nextOutage = RTC.sramRead (NEXT_OUTAGE_ADDR);
107+ time_t powerDown, powerUp; // power outage timestamps
108+ uint8_t nOutage; // number of outages stored in sram
109+ uint8_t nextOutage; // address of next outage timestamps in sram
110+ if ( myRTC.powerFail (&powerDown, &powerUp) ) {
111+ nOutage = myRTC.sramRead (nbrOutagesAddr);
112+ nextOutage = myRTC.sramRead (nextOutageAddr);
109113 write32 (nextOutage, powerDown);
110114 write32 (nextOutage + 4 , powerUp);
111- nextOutage += OUTAGE_LENGTH ;
112- if (nextOutage > MAX_OUTAGE_ADDR ) nextOutage = FIRST_OUTAGE_ADDR ;
113- RTC .sramWrite (NEXT_OUTAGE_ADDR , nextOutage);
114- if (nOutage < MAX_OUTAGES) RTC .sramWrite (NBR_OUTAGES_ADDR , ++nOutage);
115+ nextOutage += outageLength ;
116+ if (nextOutage > lastOutageAddr ) nextOutage = firstOutageAddr ;
117+ myRTC .sramWrite (nextOutageAddr , nextOutage);
118+ if (nOutage < maxOutages) myRTC .sramWrite (nbrOutagesAddr , ++nOutage);
115119 }
116120
117121 // print out all the outages logged
118- nOutage = RTC.sramRead (NBR_OUTAGES_ADDR);
119- nextOutage = RTC.sramRead (NEXT_OUTAGE_ADDR);
120- outageAddr = nextOutage - OUTAGE_LENGTH;
121- if (outageAddr < FIRST_OUTAGE_ADDR) outageAddr = MAX_OUTAGE_ADDR;
122- Serial << endl << endl << " Power outages logged: " << _DEC (nOutage) << endl;
123- for (uint8_t i=nOutage; i>0 ; i--)
124- {
122+ nOutage = myRTC.sramRead (nbrOutagesAddr);
123+ nextOutage = myRTC.sramRead (nextOutageAddr);
124+ uint8_t outageAddr = nextOutage - outageLength;
125+ if (outageAddr < firstOutageAddr) outageAddr = lastOutageAddr;
126+ Serial << endl << " Power outages logged: " << _DEC (nOutage) << endl;
127+ for (uint8_t i=nOutage; i>0 ; i--) {
125128 powerDown = read32 (outageAddr);
126129 powerUp = read32 (outageAddr + 4 );
127130 Serial << endl << _DEC (i) << " : Power down " ;
128131 printTime (myTZ.toLocal (powerDown, &tcr), tcr -> abbrev);
129132 Serial << _DEC (i) << " : Power up " ;
130133 printTime (myTZ.toLocal (powerUp, &tcr), tcr -> abbrev);
131- outageAddr -= OUTAGE_LENGTH ;
132- if (outageAddr < FIRST_OUTAGE_ADDR ) outageAddr = MAX_OUTAGE_ADDR ;
134+ outageAddr -= outageLength ;
135+ if (outageAddr < firstOutageAddr ) outageAddr = lastOutageAddr ;
133136 }
134137}
135138
136139// initialize the logging data structure and log data
137140void logClear ()
138141{
139- for (uint8_t i=0 ; i<MAX_OUTAGE_ADDR + OUTAGE_LENGTH; i++)
140- {
141- RTC.sramWrite (i, 0 );
142+ for (uint8_t i=0 ; i<lastOutageAddr + outageLength; i++) {
143+ myRTC.sramWrite (i, 0 );
142144 }
143145}
144146
@@ -151,7 +153,7 @@ void write32(uint8_t addr, uint32_t t)
151153 } i;
152154
153155 i.t = t;
154- RTC .sramWrite (addr, i.b , 4 );
156+ myRTC .sramWrite (addr, i.b , 4 );
155157}
156158
157159// read a time_t or other uint32_t value from sram starting at addr
@@ -162,7 +164,7 @@ time_t read32(uint8_t addr)
162164 time_t t;
163165 } i;
164166
165- RTC .sramRead (addr, i.b , 4 );
167+ myRTC .sramRead (addr, i.b , 4 );
166168 return i.t ;
167169}
168170
0 commit comments