Skip to content

Commit ed497ec

Browse files
committed
Rename "SETUP" to "options" and adapt to 4 tubes; add temporary cleaner mode (anti-poisoning); various debug stuff
1 parent 965b1d7 commit ed497ec

File tree

1 file changed

+135
-63
lines changed

1 file changed

+135
-63
lines changed

sixtube_lm/sixtube_lm.ino

Lines changed: 135 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,16 @@
2626

2727
////////// Configuration consts //////////
2828

29-
// available clock functions, and unique IDs (between 0 and 199)
29+
// available clock functions, and unique IDs (between 0 and 200)
3030
const byte fnIsTime = 0;
3131
const byte fnIsDate = 1;
3232
const byte fnIsAlarm = 2;
3333
const byte fnIsTimer = 3;
3434
const byte fnIsDayCount = 4;
3535
const byte fnIsTemp = 5;
36+
const byte fnIsCleaner = 6;
3637
// functions enabled in this clock, in their display order. Only fnIsTime is required
37-
const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsDayCount, fnIsTemp};
38+
const byte fnsEnabled[] = {fnIsTime, fnIsDate, fnIsDayCount, fnIsTemp, fnIsCleaner};
3839

3940
// These are the RLB board connections to Arduino analog input pins.
4041
// S1/PL13 = Reset
@@ -79,9 +80,11 @@ const byte alarmRadio = 0;
7980
// When timer is running, output will stay on until timer runs down.
8081
const byte alarmDur = 3;
8182

83+
const byte displaySize = 4; //4 or 6 - causes small differences in display of timer, etc
84+
8285
// How long (in ms) are the button hold durations?
8386
const word btnShortHold = 1000; //for setting the displayed feataure
84-
const word btnLongHold = 3000; //for for entering SETUP menu
87+
const word btnLongHold = 3000; //for for entering options menu
8588
const byte velThreshold = 100; //ms
8689
// When an adj up/down input (btn or rot) follows another in less than this time, value will change more (10 vs 1).
8790
// Recommend ~100 for rotaries. If you want to use this feature with buttons, extend to ~400.
@@ -93,10 +96,10 @@ const byte dayCountYearLoc = 3; //and 4 (word)
9396
const byte dayCountMonthLoc = 5; //byte
9497
const byte dayCountDateLoc = 6; //byte
9598

96-
//EEPROM locations and default values for SETUP options menu
97-
//Option number/fnSet are 1-index, so arrays are padded to be 1-index too, for coding convenience. TODO change this
99+
//EEPROM locations and default values for options menu
100+
//Option numbers are 1-index, so arrays are padded to be 1-index too, for coding convenience. TODO change this
98101
//Most vals (default, min, max) are 1-byte. In case of two-byte (max-min>255), high byte is loc, low byte is loc+1.
99-
//SETUP options are offset to loc 16, to reserve 16 bytes of space for other set values per above
102+
//options are offset to loc 16, to reserve 16 bytes of space for other set values per above
100103
// Option number: - 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
101104
const byte optsLoc[] = {0,16,17,18,19,20,21,22,23,24, 25,27, 28, 30,32,33,34, 35, 37}; //EEPROM locs
102105
const word optsDef[] = {0, 2, 1, 0, 0, 0, 0, 0, 0, 0,500, 0,1320, 360, 0, 1, 5, 480,1020};
@@ -119,9 +122,9 @@ byte btnCurHeld = 0; //Button hold thresholds: 0=none, 1=unused, 2=short, 3=long
119122
unsigned long inputLast = 0; //When a button was last pressed
120123
unsigned long inputLast2 = 0; //Second-to-last of above
121124

122-
const byte fnSetup = 255; //function //TODO pagify menu by using fnSetup 200-255 for 56 options, and fnSet for the pages
125+
const byte fnOpts = 201; //fn values from here to 255 correspond to options in the options menu
123126
byte fn = fnIsTime; //currently displayed fn, as above
124-
byte fnSet = 0; //whether this function is currently being set, and which option/page it's on
127+
byte fnSetPg = 0; //whether this function is currently being set, and which option/page it's on
125128
word fnSetVal; //the value currently being set, if any - unsigned int 0-65535
126129
word fnSetValMin; //min possible - unsigned int
127130
word fnSetValMax; //max possible - unsigned int
@@ -138,7 +141,7 @@ byte displayNext[6] = {15,15,15,15,15,15}; //Internal representation of display.
138141
////////// Main code control //////////
139142

140143
void setup(){
141-
Serial.begin(57600);
144+
Serial.begin(9600);
142145
Wire.begin();
143146
initOutputs();
144147
initInputs();
@@ -256,14 +259,22 @@ void ctrlEvt(byte ctrl, byte evt){
256259
//But we can handle short and long holds and releases for the sel ctrls (always buttons).
257260
//TODO needs alt handling
258261

259-
if(fn != fnSetup) { //normal fn running/setting (not in setup menu)
260-
261-
if(evt==3 && ctrl==mainSel) { //mainSel long hold: enter SETUP menu
262-
btnStop(); fn = fnSetup; startOpt(1); return;
262+
if(fn < fnOpts) { //normal fn running/setting (not in options menu)
263+
264+
if(evt==3 && ctrl==mainSel) { //mainSel long hold: enter options menu
265+
//Serial.println("running/setting: mainSel long hold: enter options menu"); Serial.println();
266+
btnStop();
267+
fn = fnOpts;
268+
clearSet(); //don't need updateDisplay() here because this calls updateRTC with force=true
269+
return;
263270
}
264271

265-
if(!fnSet) { //fn running
266-
if(evt==2 && ctrl==mainSel) { //sel hold: enter fnSet
272+
if(!fnSetPg) { //fn running
273+
//Serial.print("fn ");
274+
//Serial.print(fn,DEC);
275+
//Serial.println(" running");
276+
if(evt==2 && ctrl==mainSel) { //sel hold: enter setting mode
277+
//Serial.println(" mainSel hold: enter setting");
267278
switch(fn){
268279
case fnIsTime: //set mins
269280
startSet((tod.hour()*60)+tod.minute(),0,1439,1); break;
@@ -281,39 +292,45 @@ void ctrlEvt(byte ctrl, byte evt){
281292
break; //nothing - or is this where we do the calibration? TODO
282293
default: break;
283294
}
295+
//showSitch();
284296
return;
285297
}
286298
else if((ctrl==mainSel && evt==0) || ((ctrl==mainAdjUp || ctrl==mainAdjDn) && evt==1)) { //sel release or adj press - switch fn, depending on config
299+
//Serial.println(" sel release or adj press: switch fn");
287300
//-1 = nothing, -2 = cycle through functions, other = go to specific function (see fn)
288-
//we can't handle sel press here because, if attempting to enter fnSet, it would switch the fn first
301+
//we can't handle sel press here because, if attempting to enter setting mode, it would switch the fn first
289302
bool fnChgd = false;
290303
if(ctrl==mainSel && mainSelFn!=-1) {
304+
//Serial.println(" mainSel release: go to next fn in the cycle");
291305
fnChgd = true;
292306
if(mainSelFn==-2) fnScroll(1); //Go to next fn in the cycle
293307
else fn = mainSelFn;
294308
}
295309
else if((ctrl==mainAdjUp || ctrl==mainAdjDn) && mainAdjFn!=-1) {
310+
//Serial.println(" mainAdj press: go to prev/next fn in the cycle");
296311
fnChgd = true;
297312
if(mainAdjFn==-2) fnScroll(ctrl==mainAdjUp?1:-1); //Go to next or previous fn in the cycle
298313
else fn = mainAdjFn;
299314
}
300315
if(fnChgd){
301316
switch(fn){
302-
//"ticking" ones
303-
case fnIsTime: case fnIsDate: case fnIsTimer: case fnIsDayCount:
304-
checkRTC(true); break;
305317
//static ones
306318
case fnIsAlarm: case fnIsTemp:
307319
updateDisplay(); break;
308-
default: break;
320+
//"ticking" ones
321+
default: checkRTC(true); break;
309322
}
310323
}
311324
}
312325
} //end fn running
313326

314327
else { //fn setting
328+
//Serial.print("fn ");
329+
//Serial.print(fn,DEC);
330+
//Serial.println(" setting");
315331
if(evt==1) { //we respond only to press evts during fn setting
316-
if(ctrl==mainSel) { //mainSel push: go to next option or save and exit fnSet
332+
if(ctrl==mainSel) { //mainSel push: go to next option or save and exit setting mode
333+
//Serial.println(" mainSel push: go to next option or save and exit setting mode");
317334
btnStop(); //not waiting for mainSelHold, so can stop listening here
318335
//We will set ds3231 time parts directly
319336
//con: potential for very rare clock rollover while setting; pro: can set date separate from time
@@ -324,7 +341,7 @@ void ctrlEvt(byte ctrl, byte evt){
324341
ds3231.setSecond(0); //will reset to exactly 0? TODO confirm
325342
clearSet(); break;
326343
case fnIsDate: //save in RTC
327-
switch(fnSet){
344+
switch(fnSetPg){
328345
case 1: //save year, set month
329346
fnSetValDate[0]=fnSetVal;
330347
startSet(fnSetValDate[1],1,12,2); break;
@@ -344,7 +361,7 @@ void ctrlEvt(byte ctrl, byte evt){
344361
timerTime = fnSetVal;
345362
break;
346363
case fnIsDayCount: //set like date, save in eeprom like finishOpt
347-
switch(fnSet){
364+
switch(fnSetPg){
348365
case 1: //save year, set month
349366
writeEEPROM(dayCountYearLoc,fnSetVal,true);
350367
startSet(readEEPROM(dayCountMonthLoc,false),1,12,2); break;
@@ -364,42 +381,83 @@ void ctrlEvt(byte ctrl, byte evt){
364381
} //end mainSel push
365382
if(ctrl==mainAdjUp) doSet(inputLast-inputLast2<velThreshold ? 10 : 1);
366383
if(ctrl==mainAdjDn) doSet(inputLast-inputLast2<velThreshold ? -10 : -1);
384+
//showSitch();
367385
} //end if evt==1
368386
} //end fn setting
369-
387+
370388
} //end normal fn running/setting
371-
else { //setup menu setting - to/from EEPROM
389+
390+
else { //options menu setting - to/from EEPROM
391+
//Serial.print("opt ");
392+
//Serial.print(fn,DEC);
372393

373-
if(ctrl==mainSel) {
374-
if(evt==1) { //TODO could consider making it a release, so it doesn't go to the next option before leaving
375-
finishOpt();
376-
if(fnSet==sizeof(optsLoc)-1) { //that was the last one – rotate back around
377-
startOpt(1); return;
378-
} else {
379-
startOpt(fnSet+1); return;
380-
}
381-
}
382-
if(evt==2) { //exit setup
383-
btnStop(); fn = fnIsTime; clearSet(); /*debugEEPROM();*/ return; //exit setup
384-
//setCaches();
385-
}
394+
if(evt==2 && ctrl==mainSel) { //mainSel short hold: exit options menu
395+
//Serial.println(" mainSel short hold: exit options menu");
396+
btnStop();
397+
//if we're setting a value, writes setting val to EEPROM if needed
398+
if(fnSetPg) writeEEPROM(optsLoc[fnSetPg],fnSetVal,optsMax[fnSetPg]-optsMin[fnSetPg]>255?true:false);
399+
fn = fnIsTime;
400+
clearSet();
401+
return;
402+
//showSitch();
386403
}
387-
if(ctrl==mainAdjUp && evt==1) doSet(inputLast-inputLast2<velThreshold ? 10 : 1);
388-
if(ctrl==mainAdjDn && evt==1) doSet(inputLast-inputLast2<velThreshold ? -10 : -1);
389404

390-
} //end setup menu setting
405+
if(!fnSetPg){ //viewing option number
406+
//Serial.println(" viewing option number");
407+
if(ctrl==mainSel && evt==0) { //mainSel release: enter option value setting
408+
//Serial.println(" mainSel release: enter option value setting");
409+
byte n = fn-fnOpts+1; //For a given options menu option (1-index), read from EEPROM and call startSet
410+
startSet(readEEPROM(optsLoc[n],optsMax[n]-optsMin[n]>255?true:false),optsMin[n],optsMax[n],n);
411+
}
412+
if(ctrl==mainAdjUp && evt==1) fnOptScroll(1); //next one up or cycle to beginning
413+
if(ctrl==mainAdjDn && evt==1) fnOptScroll(-1); //next one down or cycle to end?
414+
updateDisplay();
415+
//showSitch();
416+
} //end viewing option number
417+
else { //setting option value
418+
//Serial.println(" setting option value");
419+
if(ctrl==mainSel && evt==0) { //mainSel release: save and exit option value setting
420+
//Writes setting val to EEPROM if needed
421+
writeEEPROM(optsLoc[fnSetPg],fnSetVal,optsMax[fnSetPg]-optsMin[fnSetPg]>255?true:false);
422+
clearSet();
423+
}
424+
if(ctrl==mainAdjUp && evt==1) doSet(inputLast-inputLast2<velThreshold ? 10 : 1);
425+
if(ctrl==mainAdjDn && evt==1) doSet(inputLast-inputLast2<velThreshold ? -10 : -1);
426+
updateDisplay();
427+
} //end setting option value
428+
} //end options menu setting
429+
//Serial.println(" ");
430+
391431
} //end ctrlEvt
392432

433+
void showSitch(){
434+
Serial.print("--- fn=");
435+
Serial.print(fn,DEC);
436+
Serial.print(", fnSetPg=");
437+
Serial.print(fnSetPg,DEC);
438+
Serial.print(", fnSetVal=");
439+
Serial.print(fnSetVal,DEC);
440+
Serial.print(" ---");
441+
Serial.println();
442+
Serial.println();
443+
}
444+
393445
void fnScroll(char dir){
394446
//Switch to the next (1) or previous (-1) fn in fnsEnabled
395447
byte pos;
396448
byte posLast = sizeof(fnsEnabled)-1;
397449
if(dir==1) for(pos=0; pos<=posLast; pos++) if(fnsEnabled[pos]==fn) { fn = (pos==posLast?0:fnsEnabled[pos+1]); break; }
398450
if(dir==-1) for(pos=posLast; pos>=0; pos--) if(fnsEnabled[pos]==fn) { fn = (pos==0?posLast:fnsEnabled[pos-1]); break; }
399451
}
452+
void fnOptScroll(char dir){
453+
//Switch to the next options fn between min (fnOpts) and max (fnOpts+sizeof(optsLoc)-1) (inclusive)
454+
byte posLast = fnOpts+sizeof(optsLoc)-1;
455+
if(dir==1) fn = (fn==posLast? fnOpts: fn+1);
456+
if(dir==-1) fn = (fn==fnOpts? posLast: fn-1);
457+
}
400458

401459
void startSet(word n, word m, word x, byte p){ //Enter set state at page p, and start setting a value
402-
fnSetVal=n; fnSetValMin=m; fnSetValMax=x; fnSetValVel=(x-m>30?1:0); fnSet=p;
460+
fnSetVal=n; fnSetValMin=m; fnSetValMax=x; fnSetValVel=(x-m>30?1:0); fnSetPg=p;
403461
updateDisplay();
404462
}
405463
void doSet(int delta){
@@ -414,7 +472,7 @@ void doSetHold(){
414472
//TODO integrate this with checkInputs?
415473
if(doSetHoldLast+250<millis()) {
416474
doSetHoldLast = millis();
417-
if(fnSet!=0 && ((mainAdjType==1 && (btnCur==mainAdjUp || btnCur==mainAdjDn)) || (altAdjType==1 && (btnCur==altAdjUp || btnCur==altAdjDn))) ){ //if we're setting, and this is an adj input for which the type is button
475+
if(fnSetPg!=0 && ((mainAdjType==1 && (btnCur==mainAdjUp || btnCur==mainAdjDn)) || (altAdjType==1 && (btnCur==altAdjUp || btnCur==altAdjDn))) ){ //if we're setting, and this is an adj input for which the type is button
418476
bool dir = (btnCur==mainAdjUp || btnCur==altAdjUp ? 1 : 0);
419477
//If short hold, or long hold but high velocity isn't supported, use low velocity (delta=1)
420478
if(btnCurHeld==2 || (btnCurHeld==3 && fnSetValVel==false)) doSet(dir?1:-1);
@@ -425,14 +483,7 @@ void doSetHold(){
425483
}
426484
void clearSet(){ //Exit set state
427485
startSet(0,0,0,0);
428-
checkRTC(true); //force an update to tod before updateDisplay()
429-
}
430-
431-
void startOpt(byte n){ //For a given setup menu option (1-index), reads from EEPROM and calls startSet
432-
startSet(readEEPROM(optsLoc[n],optsMax[n]-optsMin[n]>255?true:false),optsMin[n],optsMax[n],n);
433-
}
434-
void finishOpt(){ //Writes fnSet val to EEPROM if needed
435-
writeEEPROM(optsLoc[fnSet],fnSetVal,optsMax[fnSet]-optsMin[fnSet]>255?true:false);
486+
checkRTC(true); //force an update to tod and updateDisplay()
436487
}
437488

438489
//EEPROM values are exclusively bytes (0-255) or words (unsigned ints, 0-65535)
@@ -448,14 +499,14 @@ void initEEPROM(){
448499
ds3231.setHour(0);
449500
ds3231.setMinute(0);
450501
ds3231.setSecond(0);
451-
//Set the default values that aren't part of the SETUP menu
502+
//Set the default values that aren't part of the options menu
452503
//writeEEPROM(alarmTimeLoc,420,true); //7am - word
453504
//writeEEPROM(alarmOnLoc,enableSoftAlarmSwitch==0?1:0,false); //off, or on if no software switch spec'd
454505
writeEEPROM(dayCountYearLoc,2018,true);
455506
writeEEPROM(dayCountMonthLoc,1,false);
456507
writeEEPROM(dayCountDateLoc,1,false);
457-
//then the SETUP menu defaults
458-
for(byte i=1; i<=sizeof(optsLoc)-1; i++) writeEEPROM(optsLoc[i],optsDef[i],optsMax[i]-optsMin[i]>255?true:false); //setup menu
508+
//then the options menu defaults
509+
for(byte i=1; i<=sizeof(optsLoc)-1; i++) writeEEPROM(optsLoc[i],optsDef[i],optsMax[i]-optsMin[i]>255?true:false); //options menu
459510
}
460511
word readEEPROM(int loc, bool isWord){
461512
if(isWord) {
@@ -510,8 +561,18 @@ void checkRTC(bool force){
510561
//Checks for new time-of-day second; decrements timer; checks for timed events;
511562
//updates display for running time or date.
512563
//Check for timeouts based on millis
513-
if(fnSet && pollLast-inputLast>120000) { fnSet = 0; fn = fnIsTime; force=true; } //setting timeout
514-
else if(!fnSet && fn!=fnIsTime && fn!=fnIsDayCount && !(fn==fnIsTimer && (timerTime>0 || alarmSoundStart!=0)) && pollLast>inputLast+5000) { fnSet = 0; fn = fnIsTime; force=true; } //temporarily-displayed fn timeout back to fnIsTime
564+
565+
//Timeout to reset display
566+
if(pollLast > inputLast){ //don't bother if the last input (which may have called checkRTC) was more recent than poll
567+
//Option/setting timeout: if we're in the options menu, or we're setting a value
568+
if(fnSetPg || fn>=fnOpts){
569+
if(pollLast-inputLast>120000) { fnSetPg = 0; fn = fnIsTime; force=true; } //Time out after 2 mins
570+
}
571+
//Temporary-display mode timeout: if we're *not* in a permanent one (time, day counter, or running timer)
572+
else if(fn!=fnIsTime && fn!=fnIsCleaner && fn!=fnIsDayCount && !(fn==fnIsTimer && (timerTime>0 || alarmSoundStart!=0))){
573+
if(pollLast>inputLast+5000) { fnSetPg = 0; fn = fnIsTime; force=true; }
574+
}
575+
}
515576
//Update things based on RTC
516577
tod = rtc.now();
517578
//toddow = ds3231.getDoW();
@@ -524,7 +585,7 @@ void checkRTC(bool force){
524585
//trip minutely date at :30 TODO
525586
//trip digit cycle TODO
526587

527-
if(fnSet==0) updateDisplay();
588+
if(fnSetPg==0) updateDisplay();
528589

529590
} //end if force or new second
530591
} //end checkRTC()
@@ -534,17 +595,21 @@ void checkRTC(bool force){
534595
void updateDisplay(){
535596
//Run as needed to update display when the value being shown on it has changed
536597
//This formats the new value and puts it in displayNext[] for cycleDisplay() to pick up
537-
if(fnSet) { //setting
538-
//little tubes:
539-
if(fn==fnSetup) editDisplay(fnSet, 4, 5, false); //setup menu: current option key
540-
else blankDisplay(4, 5); //fn setting: blank
598+
if(fnSetPg) { //setting value
599+
// //little tubes:
600+
// if(fn==fnOpts) editDisplay(fnSetPg, 4, 5, false); //options menu: current option key
601+
// else //fn setting: blank
602+
blankDisplay(4, 5);
541603
//big tubes:
542604
if(fnSetValMax==1439) { //value is a time of day
543605
editDisplay(fnSetVal/60, 0, 1, EEPROM.read(optsLoc[4])); //hours with leading zero
544606
editDisplay(fnSetVal%60, 2, 3, true);
545607
} else editDisplay(fnSetVal, 0, 3, false); //some other type of value
546608
}
547-
else { //fn running
609+
else if(fn >= fnOpts){ //options menu, but not setting a value
610+
editDisplay(fn-fnOpts+1,0,1,0); //display option number on hour tubes
611+
blankDisplay(2,5);
612+
} else { //fn running
548613
switch(fn){
549614
case fnIsTime:
550615
byte hr; hr = tod.hour();
@@ -587,6 +652,13 @@ void updateDisplay(){
587652
editDisplay(abs(temp)/100,1,3,(temp<0?true:false)); //leading zeros if negative
588653
editDisplay(abs(temp)%100,4,5,true);
589654
break;
655+
case fnIsCleaner:
656+
editDisplay(tod.second(),0,0,true);
657+
editDisplay(tod.second(),1,1,true);
658+
editDisplay(tod.second(),2,2,true);
659+
editDisplay(tod.second(),3,3,true);
660+
editDisplay(tod.second(),4,4,true);
661+
editDisplay(tod.second(),5,5,true);
590662
default: break;
591663
}//end switch
592664
}
@@ -639,7 +711,7 @@ void initOutputs() {
639711

640712
void cycleDisplay(){
641713
bool dim = 0;//(opts[2]>0?true:false); //Under normal circumstances, dim constantly if the time is right
642-
if(fnSet>0) { //but if we're setting, dim for every other 500ms since we started setting
714+
if(fnSetPg>0) { //but if we're setting, dim for every other 500ms since we started setting
643715
if(setStartLast==0) setStartLast = millis();
644716
dim = 1-(((millis()-setStartLast)/500)%2);
645717
} else {

0 commit comments

Comments
 (0)