Skip to content

Commit b9f38bf

Browse files
committed
Switch to DS3231 library (untested!)
1 parent aa4ee8e commit b9f38bf

File tree

1 file changed

+83
-39
lines changed

1 file changed

+83
-39
lines changed

sixtube_lm/sixtube_lm.ino

Lines changed: 83 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
// Originally written by Robin Birtles and Chris Gerekos based on http://arduinix.com/Main/Code/ANX-6Tube-Clock-Crossfade.txt
44
// Refactored and expanded by Luke McKenzie (luke@theclockspot.com)
55

6-
// TODO: Rotary encoders with velocity - test
6+
// Requires ooPinChangeInt.h
7+
// Requires AdaEncoder.h
8+
79
// TODO: Alarm - display, set, sound, snooze, 24h silence
810
// TODO: Timer - display, set, run, sound, silence
911
// TODO: Cathode anti-poisoning
@@ -26,7 +28,7 @@ RTC_DS1307 rtc;
2628
// S5/PL8 = A3
2729
// S6/PL9 = A2
2830
// S7/PL14 = A7
29-
// A6-A7 are analog-only pins that aren't as responsive and require a physical pullup resistor (1K to +5V).
31+
// A6-A7 are analog-only pins that aren't quite as responsive and require a physical pullup resistor (1K to +5V), and can't be used with rotary encoders because they don't support pin change interrupts.
3032

3133
// What input is associated with each control?
3234
const byte mainSel = A2; //main select button - must be equipped
@@ -38,7 +40,6 @@ const byte altAdjDn = 0; //A3;
3840

3941
// What type of adj controls are equipped?
4042
// 1 = momentary buttons. 2 = quadrature rotary encoder.
41-
// Currently using AdaEncoder library that uses pin change interrupts, not useful on A6/A7!
4243
const byte mainAdjType = 2;
4344
AdaEncoder mainRot = AdaEncoder('a',mainAdjUp,mainAdjDn);
4445
const byte altAdjType = 0; //if unquipped, set to 0
@@ -70,26 +71,25 @@ const byte velThreshold = 100; //ms
7071
// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1).
7172
// Recommend ~100 for rotaries. If you want to use this feature with buttons, extend to ~400.
7273

73-
7474
////////// Global consts and vars used in multiple sections //////////
7575

7676
// Hardware inputs
7777
byte btnCur = 0; //Momentary button currently in use - only one allowed at a time
7878
byte btnCurHeld = 0; //Button hold thresholds: 0=none, 1=unused, 2=short, 3=long, 4=set by btnStop()
79-
unsigned long inputLast = 0; //When a button was last pressed / knob was last turned
79+
unsigned long inputLast = 0; //When a button was last pressed
8080
unsigned long inputLast2 = 0; //Second-to-last of above
8181

8282
// Input handling and value setting
83-
const byte fnCt = 2; //number of functions in the clock
84-
byte fn = 0; //currently displayed function: 0=time, 1=date, 2=alarm, 3=timer, 255=SETUP menu
83+
const byte fnCt = 5; //number of functions in the clock
84+
byte fn = 0; //currently displayed fn: 0=time, 1=date, 2=alarm, 3=timer, 4=temp, 255=SETUP menu
8585
byte fnSet = 0; //whether this function is currently being set, and which option/page it's on
8686
word fnSetVal; //the value currently being set, if any - unsigned int 0-65535
8787
word fnSetValMin; //min possible - unsigned int
8888
word fnSetValMax; //max possible - unsigned int
8989
bool fnSetValVel; //whether it supports velocity setting (if max-min > 30)
9090
word fnSetValDate[3]; //holder for newly set date, so we can set it in 3 stages but set the RTC only once
91-
const byte alarmTimeLoc = 0; //EEPROM locs 0-1 (2 bytes) in minutes past midnight.
92-
const byte alarmOnLoc = 2; //EEPROM loc 2
91+
//const byte alarmTimeLoc = 0; //EEPROM locs 0-1 (2 bytes) in minutes past midnight.
92+
//const byte alarmOnLoc = 2; //EEPROM loc 2
9393
unsigned long alarmSoundStart = 0; //also used for timer expiry TODO what happens if they are concurrent?
9494
word snoozeTime = 0; //seconds
9595
word timerTime = 0; //seconds - up to just under 18 hours
@@ -101,6 +101,9 @@ const byte optsLoc[19] = {0, 3, 4, 5, 6, 7, 8, 9,10,11, 12,14, 15, 17,19,20,21
101101
const word optsDef[19] = {0, 2, 1, 0, 0, 0, 0, 0, 0, 0,500, 0,1320, 360, 0, 1, 5, 480,1020};
102102
const word optsMin[19] = {0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0};
103103
const word optsMax[19] = {0, 2, 2, 2, 1,50, 1, 6, 4,60,999, 2,1439,1439, 2, 6, 6,1439,1439};
104+
//Buffer for reading extra data from ds3231
105+
#define BUFF_MAX 128
106+
char buff[BUFF_MAX];
104107

105108
// Display formatting
106109
byte displayNext[6] = {15,15,15,15,15,15}; //Internal representation of display. Blank to start. Change this to change tubes.
@@ -111,8 +114,13 @@ byte displayNext[6] = {15,15,15,15,15,15}; //Internal representation of display.
111114
void setup(){
112115
Serial.begin(57600);
113116
Wire.begin();
114-
rtc.begin();
115-
if(!rtc.isrunning()) rtc.adjust(DateTime(2017,1,1,0,0,0)); //TODO test
117+
//rtc.begin();
118+
//if(!rtc.isrunning()) rtc.adjust(DateTime(2017,1,1,0,0,0)); //TODO test TODO ds3231 version?
119+
120+
//ds3231
121+
DS3231_init(DS3231_INTCN);
122+
memset(recv,0,BUFF_MAX); //TODO what does this do
123+
116124
initOutputs();
117125
initInputs();
118126
initEEPROM(readInput(mainSel)==LOW);
@@ -121,10 +129,12 @@ void setup(){
121129
}
122130

123131
unsigned long pollLast = 0;
132+
struct ts t; //ds3231
124133
void loop(){
134+
unsigned long now = millis();
125135
//Things done every 50ms - avoids overpolling(?) and switch bounce(?)
126-
if(pollLast<millis()+50) {
127-
pollLast=millis();
136+
if(pollLast<now+50) {
137+
pollLast=now;
128138
checkRTC(false); //if clock has ticked, decrement timer if running, and updateDisplay
129139
checkInputs(); //if inputs have changed, this will do things + updateDisplay as needed
130140
doSetHold(); //if inputs have been held, this will do more things + updateDisplay as needed
@@ -237,15 +247,23 @@ void ctrlEvt(byte ctrl, byte evt){
237247
btnStop(); fn = 255; startOpt(1); return;
238248
}
239249

240-
DateTime now = rtc.now();
250+
//DateTime now = rtc.now();
251+
//ds3231 - checkRTC should already have run?? TODO
241252

242253
if(!fnSet) { //fn running
243254
if(evt==2 && ctrl==mainSel) { //sel hold: enter fnSet
244255
switch(fn){
245-
case 0: startSet((now.hour()*60)+now.minute(),0,1439,1); break; //time: set mins
246-
case 1: fnSetValDate[1]=now.month(), fnSetValDate[2]=now.day(); startSet(now.year(),0,32767,1); break; //date: set year
247-
case 2: //alarm: set mins
256+
case 0: startSet((t.hour*60)+t.min,0,1439,1); break; //time: set mins
257+
case 1: fnSetValDate[1]=t.mon, fnSetValDate[2]=t.mday; startSet(t.year,0,32767,1); break; //date: set year
258+
case 2:
259+
DS3231_get_a1(&buff[0], 59); //TODO write a wrapper function for this
260+
startSet(buff,0,1439,1); //alarm: set mins
261+
break;
248262
case 3: //timer: set mins
263+
startSet(timerTime/60,0,719,1); //12 hours
264+
break;
265+
case 4: //temperature
266+
//nothing - or is this where we do the calibration? TODO
249267
default: break;
250268
}
251269
return;
@@ -266,9 +284,8 @@ void ctrlEvt(byte ctrl, byte evt){
266284
}
267285
if(fnChgd){
268286
switch(fn){
269-
case 0: case 1: checkRTC(true); break;
270-
case 2: //alarm: show
271-
case 3: //timer: show
287+
case 0: case 1: checkRTC(true); break; //time or date
288+
case 2: case 3: updateDisplay(); break; //alarm or timer
272289
default: break;
273290
}
274291
}
@@ -279,9 +296,12 @@ void ctrlEvt(byte ctrl, byte evt){
279296
if(evt==1) { //we respond only to press evts during fn setting
280297
if(ctrl==mainSel) { //mainSel push: go to next option or save and exit fnSet
281298
btnStop(); //not waiting for mainSelHold, so can stop listening here
299+
//In case we are setting the time or date, in an effort to not lose synchronization too badly, should we update t here?
282300
switch(fn){
283301
case 0: //time of day: save in RTC
284-
rtc.adjust(DateTime(now.year(),now.month(),now.day(),fnSetVal/60,fnSetVal%60,0));
302+
//rtc.adjust(DateTime(t.year,t.mon,t.mday,fnSetVal/60,fnSetVal%60,0));
303+
t.hour = fnSetVal/60; t.min = fnSetVal%60, t.sec = 0;
304+
DS3231_set(t);
285305
clearSet(); break;
286306
case 1: switch(fnSet){ //date: save in RTC - year can be 2-byte int
287307
case 1: //date: save year, set month
@@ -291,15 +311,19 @@ void ctrlEvt(byte ctrl, byte evt){
291311
fnSetValDate[1]=fnSetVal;
292312
startSet(fnSetValDate[2],1,daysInMonth(fnSetValDate[0],fnSetValDate[1]),3); break;
293313
case 3: //date: save in RTC
294-
rtc.adjust(DateTime(fnSetValDate[0],fnSetValDate[1],fnSetVal,now.hour(),now.minute(),now.second()));
295-
//TODO this rounds down the seconds and loses synchronization! find a way to set the date only
314+
//rtc.adjust(DateTime( fnSetValDate[0],fnSetValDate[1],fnSetVal,t.hour,t.min,t.sec));
315+
//TODO this rounds down the seconds and loses synchronization! find a way to set the date only.
316+
t.year = fnSetValDate[0]; t.mon = fnSetValDate[1]; t.mday = fnSetVal;
317+
DS3231_set(t);
296318
clearSet(); break;
297319
default: break;
298320
} break;
299321
case 2: //alarm
300-
//EEPROM set TODO
322+
do(
323+
uint8_t flags[5] = {0,0,0,1,1}; //what calendar component triggers the alarm, see datasheet
324+
DS3231_set_a1(0,fnSetVal%60,fnSetVal/60,0,flags);
301325
case 3: //timer
302-
//TODO
326+
timerTime = fnSetVal;
303327
default: break;
304328
} //end switch fn
305329
} //end mainSel push
@@ -439,26 +463,31 @@ void checkRTC(bool force){
439463
if(fnSet && pollLast-inputLast>120000) { fnSet = 0; fn = 0; force=true; } //abandon set
440464
else if(!fnSet && fn!=0 && !(fn==3 && (timerTime>0 || alarmSoundStart!=0)) && pollLast>inputLast+5000) { fnSet = 0; fn = 0; force=true; } //abandon fn
441465
//Update things based on RTC
442-
DateTime now = rtc.now();
443-
if(rtcSecLast != now.second() || force) {
444-
rtcSecLast = now.second(); //this was missing! TODO reintroduce
466+
DS3231_get(&t); //ds3231 //DateTime now = rtc.now();
467+
//replace now.year(), month, day, dayOfTheWeek, hour, minute, second
468+
//with t.year, mon, mday, wday, hour, min, sec
469+
//hoping t.mon continues to be 1-index and wday 0-index starting with Sunday
470+
//TODO what is inp2toi?
471+
472+
if(rtcSecLast != t.sec || force) {
473+
rtcSecLast = t.sec; //this was missing! TODO reintroduce
445474
//trip alarm TODO
446475
//decrement timer TODO
447476
//trip minutely date at :30 TODO
448477
//trip digit cycle TODO
449478
//finally display live time of day / date
450479
if(fnSet==0 && fn==0){ //time of day
451-
byte hr = now.hour();
480+
byte hr = t.hour;
452481
if(readEEPROM(optsLoc[1],false)==1) hr = (hr==0?12:(hr>12?hr-12:hr));
453482
editDisplay(hr, 0, 1, readEEPROM(optsLoc[4],false));
454-
editDisplay(now.minute(), 2, 3, true);
455-
if(EEPROM.read(optsLoc[3])==1) editDisplay(now.day(), 4, 5, EEPROM.read(optsLoc[4])); //date
456-
else editDisplay(now.second(), 4, 5, true); //seconds
483+
editDisplay(t.min, 2, 3, true);
484+
if(EEPROM.read(optsLoc[3])==1) editDisplay(t.mday, 4, 5, EEPROM.read(optsLoc[4])); //date
485+
else editDisplay(t.sec, 4, 5, true); //seconds
457486
} else if(fnSet==0 && fn==1){ //date
458-
editDisplay(EEPROM.read(optsLoc[2])==1?now.month():now.day(), 0, 1, EEPROM.read(optsLoc[4]));
459-
editDisplay(EEPROM.read(optsLoc[2])==1?now.day():now.month(), 2, 3, EEPROM.read(optsLoc[4]));
487+
editDisplay(EEPROM.read(optsLoc[2])==1?t.mon:t.mday, 0, 1, EEPROM.read(optsLoc[4]));
488+
editDisplay(EEPROM.read(optsLoc[2])==1?t.mday:t.mon, 2, 3, EEPROM.read(optsLoc[4]));
460489
blankDisplay(4, 4);
461-
editDisplay(now.dayOfTheWeek(), 5, 5, false);
490+
editDisplay(t.wday, 5, 5, false); //TODO is this 0=Sunday, 6=Saturday?
462491
}
463492
}
464493
}
@@ -481,11 +510,26 @@ void updateDisplay(){
481510
}
482511
else { //fn running
483512
switch(fn){
484-
//case 0: //time taken care of by checkRTC()
485-
//case 1: //date taken care of by checkRTC()
513+
//case 0 and 1: time/date display taken care of by checkRTC()
486514
case 2: //alarm
487-
case 3: //timer
488-
break;
515+
editDisplay(0,0,1,EEPROM.read(optsLoc[4])); //hrs
516+
editDisplay(0,2,3,true); //mins
517+
editDisplay(0,4,5,false); //status
518+
break;
519+
case 3: //timer - display time duration, not time of day with leading zeros
520+
//todo does checkRTC need to do this one too?
521+
if(false /*hrs > 0*/) editDisplay(1,0,1,false); else blankDisplay(0,1); //hrs if present
522+
editDisplay(5,2,3,(false/*hrs>0*/?true:false)); //mins - leading zero only if hrs present
523+
editDisplay(
524+
break;
525+
case 4: //thermometer
526+
float temp = DS3231_get_treg(); //TODO signed? decimal? what?
527+
if(temp>0) blankDisplay(0,0); else editDisplay(0,0,1,true); //0 in left tube if negative
528+
editDisplay(temp,1,3,false); //whole degrees
529+
editDisplay(temp%10,4,4,true); //tenths?
530+
blankDisplay(5,5);
531+
break;
532+
default: break;
489533
}
490534
}
491535
} //end updateDisplay()

0 commit comments

Comments
 (0)