Skip to content

Commit 0c73181

Browse files
author
Jack Christensen
committed
v0.9
Added function to set alarm polarity. Added function to set MFP level.
1 parent 509ef05 commit 0c73181

File tree

5 files changed

+328
-27
lines changed

5 files changed

+328
-27
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*----------------------------------------------------------------------*
2+
* Power Outage Logger using Microchip MCP79412 RTC. *
3+
* Assumes the RTC is running and set to UTC. *
4+
* A maximum of 7 outages (power down/up times) can be logged in the *
5+
* RTC's SRAM. *
6+
* *
7+
* Jack Christensen 23Aug2012 *
8+
* *
9+
* This work is licensed under the Creative Commons Attribution- *
10+
* ShareAlike 3.0 Unported License. To view a copy of this license, *
11+
* visit http://creativecommons.org/licenses/by-sa/3.0/ or send a *
12+
* letter to Creative Commons, 171 Second Street, Suite 300, *
13+
* San Francisco, California, 94105, USA. *
14+
*----------------------------------------------------------------------*/
15+
16+
#include <MCP79412RTC.h> //http://github.com/JChristensen/MCP79412RTC
17+
#include <Streaming.h> //http://arduiniana.org/libraries/streaming/
18+
#include <Time.h> //http://www.arduino.cc/playground/Code/Time
19+
#include <Timezone.h> //http://github.com/JChristensen/Timezone
20+
#include <Wire.h> //http://arduino.cc/en/Reference/Wire (included with Arduino IDE)
21+
22+
#define FIRST_OUTAGE_ADDR 0x08 //address of first outage timestamps in RTC SRAM
23+
#define OUTAGE_LENGTH 8 //8 data bytes for each outage (start and end timestamps, both are time_t values)
24+
#define MAX_OUTAGES 7 //maximum number of outage timestamp pairs that can be stored in SRAM
25+
#define MAX_OUTAGE_ADDR FIRST_OUTAGE_ADDR + OUTAGE_LENGTH * (MAX_OUTAGES - 1) //last outage address
26+
#define APP_ID 1 //APP_ID and 4 bytes of the RTC ID are stored in sram to provide
27+
//a way to recognize that the logging data structure has been initialized
28+
#define RTC_ID_LO 0x00 //lower 4 bytes of RTC unique ID are stored at sram addr 0x00
29+
#define APP_ID_ADDR 0x04 //address of appID (1)
30+
#define NBR_OUTAGES_ADDR 0x05 //address containing number of outages currently stored in SRAM
31+
#define NEXT_OUTAGE_ADDR 0x06 //address containing pointer to next outage
32+
#define RFU_ADDR 0x07 //reserved for future use
33+
34+
//US Eastern Time Zone (New York, Detroit)
35+
TimeChangeRule myDST = {"EDT", Second, Sun, Mar, 2, -240}; //Daylight time = UTC - 4 hours
36+
TimeChangeRule mySTD = {"EST", First, Sun, Nov, 2, -300}; //Standard time = UTC - 5 hours
37+
Timezone myTZ(myDST, mySTD);
38+
TimeChangeRule *tcr; //pointer to the time change rule, used to get TZ abbrev
39+
time_t utc, local, lastUTC;
40+
41+
void setup() {
42+
Serial.begin(115200);
43+
44+
setSyncProvider(RTC.get); //the function to get the time from the RTC
45+
Serial << "RTC SYNC";
46+
if (timeStatus()!= timeSet) Serial << "FAIL";
47+
Serial << endl;
48+
49+
//logClear();
50+
logOutage();
51+
}
52+
53+
void loop()
54+
{
55+
//nothing here in loop() has anything to do with logging power outages,
56+
//we just print the time every second so that something is happening.
57+
utc = now();
58+
if (utc != lastUTC) {
59+
lastUTC = utc;
60+
local = myTZ.toLocal(utc, &tcr);
61+
Serial << endl;
62+
printTime(utc, "UTC");
63+
printTime(local, tcr -> abbrev);
64+
}
65+
}
66+
67+
//initialize the log data structure in the RTC SRAM if needed.
68+
//log a new outage if one occurred.
69+
//print out the outages logged.
70+
void logOutage(void)
71+
{
72+
union {
73+
uint8_t b[8];
74+
struct {
75+
uint32_t hi;
76+
uint32_t lo;
77+
};
78+
} uniqueID; //8-byte RTC "unique ID" with access to upper and lower halves
79+
80+
uint32_t loID; //lower half of the unique ID read from sram
81+
uint8_t appID; //app ID read from sram
82+
uint8_t nOutage; //number of outages stored in sram
83+
uint8_t nextOutage; //address of next outage timestamps in sram
84+
uint8_t outageAddr; //outage address in sram
85+
time_t powerDown, powerUp; //power outage timestamps
86+
87+
RTC.idRead(uniqueID.b); //get the RTC's ID
88+
loID = read32(RTC_ID_LO); //if already initialized, the lower half of the ID is stored at SRAM addr 0x00,
89+
appID = RTC.sramRead(APP_ID_ADDR); //and the app ID (1) is at addr 0x04.
90+
Serial << "RTC ID";
91+
for (uint8_t i=0; i<8; i++) {
92+
Serial << (uniqueID.b[i] < 16 ? " 0" : " ") << _HEX(uniqueID.b[i]);
93+
}
94+
95+
if ( loID != uniqueID.lo || appID != 1 ) { //logging initialized?
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
101+
Serial << "Logging initialized" << endl; //no, do it now
102+
}
103+
104+
//if an outage has occurred, record it
105+
if ( RTC.powerFail(&powerDown, &powerUp) ) {
106+
nOutage = RTC.sramRead(NBR_OUTAGES_ADDR);
107+
nextOutage = RTC.sramRead(NEXT_OUTAGE_ADDR);
108+
write32(nextOutage, powerDown);
109+
write32(nextOutage + 4, powerUp);
110+
nextOutage += OUTAGE_LENGTH;
111+
if (nextOutage > MAX_OUTAGE_ADDR) nextOutage = FIRST_OUTAGE_ADDR;
112+
RTC.sramWrite(NEXT_OUTAGE_ADDR, nextOutage);
113+
if (nOutage < MAX_OUTAGES) RTC.sramWrite(NBR_OUTAGES_ADDR, ++nOutage);
114+
}
115+
116+
//print out all the outages logged
117+
nOutage = RTC.sramRead(NBR_OUTAGES_ADDR);
118+
nextOutage = RTC.sramRead(NEXT_OUTAGE_ADDR);
119+
outageAddr = nextOutage - OUTAGE_LENGTH;
120+
if (outageAddr < FIRST_OUTAGE_ADDR) outageAddr = MAX_OUTAGE_ADDR;
121+
Serial << endl << endl << "Power outages logged: " << _DEC(nOutage) << endl;
122+
for (uint8_t i=nOutage; i>0; i--) {
123+
powerDown = read32(outageAddr);
124+
powerUp = read32(outageAddr + 4);
125+
Serial << endl << _DEC(i) << ": Power down ";
126+
printTime(myTZ.toLocal(powerDown, &tcr), tcr -> abbrev);
127+
Serial << _DEC(i) << ": Power up ";
128+
printTime(myTZ.toLocal(powerUp, &tcr), tcr -> abbrev);
129+
outageAddr -= OUTAGE_LENGTH;
130+
if (outageAddr < FIRST_OUTAGE_ADDR) outageAddr = MAX_OUTAGE_ADDR;
131+
}
132+
}
133+
134+
//destroy the logging data structure and log data
135+
void logClear(void)
136+
{
137+
for (uint8_t i=0; i<MAX_OUTAGE_ADDR + OUTAGE_LENGTH; i++) {
138+
RTC.sramWrite(i, 0);
139+
}
140+
}
141+
142+
//write a time_t or other uint32_t value to sram starting at addr
143+
void write32(uint8_t addr, uint32_t t)
144+
{
145+
union {
146+
uint8_t b[4];
147+
uint32_t t;
148+
} i;
149+
150+
i.t = t;
151+
RTC.sramWrite(addr, i.b, 4);
152+
}
153+
154+
//read a time_t or other uint32_t value from sram starting at addr
155+
time_t read32(uint8_t addr)
156+
{
157+
union {
158+
uint8_t b[4];
159+
time_t t;
160+
} i;
161+
162+
RTC.sramRead(addr, i.b, 4);
163+
return i.t;
164+
}
165+
166+
//Print time with time zone
167+
void printTime(time_t t, char *tz)
168+
{
169+
sPrintI00(hour(t));
170+
sPrintDigits(minute(t));
171+
sPrintDigits(second(t));
172+
Serial << ' ' << dayShortStr(weekday(t)) << ' ';
173+
sPrintI00(day(t));
174+
Serial << ' ' << monthShortStr(month(t)) << ' ' << year(t) << ' ' << tz << endl;
175+
}
176+
177+
//Print an integer in "00" format (with leading zero).
178+
//Input value assumed to be between 0 and 99.
179+
void sPrintI00(int val)
180+
{
181+
if (val < 10) Serial << '0';
182+
Serial << val;
183+
return;
184+
}
185+
186+
//Print an integer in ":00" format (with leading zero).
187+
//Input value assumed to be between 0 and 99.
188+
void sPrintDigits(int val)
189+
{
190+
Serial << ':';
191+
if(val < 10) Serial << '0';
192+
Serial << val;
193+
}

Examples/TimeRTC/TimeRTC.ino

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* TimeRTC.pde
3+
* Example code illustrating Time library with Real Time Clock.
4+
* This example is identical to the example provided with the Time Library,
5+
* only the #include statement has been changed to include the MCP79412RTC library.
6+
*/
7+
8+
#include <Time.h>
9+
#include <Wire.h>
10+
#include <MCP79412RTC.h> //http://github.com/JChristensen/MCP79412RTC
11+
12+
void setup() {
13+
Serial.begin(9600);
14+
setSyncProvider(RTC.get); // the function to get the time from the RTC
15+
if(timeStatus()!= timeSet)
16+
Serial.println("Unable to sync with the RTC");
17+
else
18+
Serial.println("RTC has set the system time");
19+
}
20+
21+
void loop()
22+
{
23+
digitalClockDisplay();
24+
delay(1000);
25+
}
26+
27+
void digitalClockDisplay(){
28+
// digital clock display of the time
29+
Serial.print(hour());
30+
printDigits(minute());
31+
printDigits(second());
32+
Serial.print(" ");
33+
Serial.print(day());
34+
Serial.print(" ");
35+
Serial.print(month());
36+
Serial.print(" ");
37+
Serial.print(year());
38+
Serial.println();
39+
}
40+
41+
void printDigits(int digits){
42+
// utility function for digital clock display: prints preceding colon and leading 0
43+
Serial.print(":");
44+
if(digits < 10)
45+
Serial.print('0');
46+
Serial.print(digits);
47+
}
48+

MCP79412RTC.cpp

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ void MCP79412RTC::enableAlarm(uint8_t alarmNumber, uint8_t alarmType)
453453
ramRead(CTRL_REG, &ctrl, 1);
454454
if (alarmType < ALM_DISABLE) {
455455
ramRead(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
456-
day = ( day & 0x07 ) | alarmType << 4; //reset interrupt flag, OR in the config bits
456+
day = ( day & 0x87 ) | alarmType << 4; //reset interrupt flag, OR in the config bits
457457
ramWrite(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
458458
ctrl |= _BV(ALM0 + alarmNumber); //enable the alarm
459459
}
@@ -473,16 +473,56 @@ boolean MCP79412RTC::alarm(uint8_t alarmNumber)
473473
uint8_t day; //alarm day register has config & flag bits
474474

475475
alarmNumber &= 0x01; //ensure a valid alarm number
476-
ramRead( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG) , &day, 1);
476+
ramRead( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
477477
if (day & _BV(ALMIF)) {
478478
day &= ~_BV(ALMIF); //turn off the alarm "interrupt" flag
479-
ramWrite( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG) , &day, 1);
479+
ramWrite( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
480480
return true;
481481
}
482482
else
483483
return false;
484484
}
485485

486+
/*----------------------------------------------------------------------*
487+
* Sets the logic level on the MFP when it's not being used as a *
488+
* square wave or alarm output. The default is HIGH. *
489+
*----------------------------------------------------------------------*/
490+
void MCP79412RTC::out(boolean level)
491+
{
492+
uint8_t ctrlReg;
493+
494+
ramRead(CTRL_REG, &ctrlReg, 1);
495+
if (level)
496+
ctrlReg |= _BV(OUT);
497+
else
498+
ctrlReg &= ~_BV(OUT);
499+
ramWrite(CTRL_REG, &ctrlReg, 1);
500+
}
501+
502+
/*----------------------------------------------------------------------*
503+
* Specifies the logic level on the Multi-Function Pin (MFP) when an *
504+
* alarm is triggered. The default is HIGH. When both alarms are *
505+
* active, the two are ORed together to determine the level of the MFP. *
506+
* With alarm polarity set to LOW (the default), this causes the MFP *
507+
* to go low only when BOTH alarms are triggered. With alarm polarity *
508+
* set to HIGH, the MFP will go high when EITHER alarm is triggered. *
509+
* *
510+
* Note that the state of the MFP is independent of the alarm *
511+
* "interrupt" flags, and the alarm() function will indicate when an *
512+
* alarm is triggered regardless of the polarity. *
513+
*----------------------------------------------------------------------*/
514+
void MCP79412RTC::alarmPolarity(boolean polarity)
515+
{
516+
uint8_t alm0Day;
517+
518+
ramRead(ALM0_DAY, &alm0Day, 1);
519+
if (polarity)
520+
alm0Day |= _BV(OUT);
521+
else
522+
alm0Day &= ~_BV(OUT);
523+
ramWrite(ALM0_DAY, &alm0Day, 1);
524+
}
525+
486526
/*----------------------------------------------------------------------*
487527
* Check to see if the RTC's oscillator is started (ST bit in seconds *
488528
* register). Returns true if started. *

MCP79412RTC.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ class MCP79412RTC
121121
void setAlarm(uint8_t alarmNumber, time_t alarmTime);
122122
void enableAlarm(uint8_t alarmNumber, uint8_t alarmType);
123123
boolean alarm(uint8_t alarmNumber);
124+
void out(boolean level);
125+
void alarmPolarity(boolean polarity);
124126

125127
boolean oscStarted(void);
126128

0 commit comments

Comments
 (0)