3
3
// Originally written by Robin Birtles and Chris Gerekos based on http://arduinix.com/Main/Code/ANX-6Tube-Clock-Crossfade.txt
4
4
// Refactored and expanded by Luke McKenzie (luke@theclockspot.com)
5
5
6
- // TODO: Alarm - display, set, sound, snooze, 24h silence
7
6
// TODO: Timer - display, set, run, sound, silence - make non-volatile?
8
7
// TODO: Cathode anti-poisoning
9
8
// TODO: implement other setup options
@@ -69,16 +68,19 @@ const char mainAdjFn = -1;
69
68
// const byte altAdjFn = -1;
70
69
71
70
const byte enableSoftAlarmSwitch = 1 ;
72
- // 1 = yes. Use if using the integrated beeper or another non-switched device (bell solenoid, etc).
71
+ // 1 = yes. Use if using the integrated piezo or another non-switched device (bell solenoid, etc).
73
72
// 0 = no. Use if the connected alarm device has its own switch (e.g. clock radio function switch).
74
73
// Alarm will be permanently on in software.
75
- const byte alarmRadio = 0 ;
76
- // 0 = no. Alarm output is connected to the onboard piezoelectric beeper or similar signal device.
77
- // When alarm and timer go off, it will output a beep pattern for alarmDur minutes.
78
- // 1 = yes. Alarm output is connected to a relay to switch other equipment (like a radio).
79
- // When alarm goes off, output will stay on for alarmDur minutes (120 is common).
74
+ const byte signalPin = 10 ;
75
+ const byte signalType = 0 ;
76
+ // What is the signal pin connected to?
77
+ // 0 = Piezo. When alarm and timer go off, it will output a beep pattern with tone() for signalDur minutes.
78
+ // 1 = Signal relay TODO. Same as above, but it will simply switch the pin, for e.g. a solenoid.
79
+ // 2 = Radio relay TODO.
80
+ // When alarm goes off, output will stay on for signalDur minutes (120 is common).
80
81
// When timer is running, output will stay on until timer runs down.
81
- const byte alarmDur = 1 ;
82
+ const word signalBeepDur = 500 ; // With signalType 0/1, beeps happen once per second; how long is each beep in ms?
83
+ const byte signalDur = 1 ; // When alarm goes off (and timer, with signalType 0/1), how many mins does signal run for?
82
84
83
85
const byte unoffDur = 10 ; // when display is dim/off, a press will light the tubes for this many seconds
84
86
@@ -161,7 +163,8 @@ bool fnSetValVel; //whether it supports velocity setting (if max-min > 30)
161
163
word fnSetValDate[3 ]; // holder for newly set date, so we can set it in 3 stages but set the RTC only once
162
164
163
165
// Volatile running values
164
- word soundRemain = 0 ; // alarm/timer sound timeout counter, seconds
166
+ word signalRemain = 0 ; // alarm/timer signal timeout counter, seconds
167
+ word signalPitch = 440 ; // which pitch to use - set by what started the signal going
165
168
word snoozeRemain = 0 ; // snooze timeout counter, seconds
166
169
word timerInitial = 0 ; // timer original setting, seconds - up to 18 hours (64,800 seconds - fits just inside a word)
167
170
word timerRemain = 0 ; // timer actual counter
@@ -285,29 +288,30 @@ void checkRot(){
285
288
286
289
// //////// Input handling and value setting //////////
287
290
288
- bool stoppingSound = false ; // Special stuff (snooze canceling) happens right after a press that silences the sound
291
+ bool stoppingSignal = false ; // Special stuff (snooze canceling) happens right after a press that silences the signal
289
292
void ctrlEvt (byte ctrl, byte evt){
290
293
// Handle control events (from checkBtn or checkRot), based on current fn and set state.
291
294
// evt: 1=press, 2=short hold, 3=long hold, 0=release.
292
295
// We only handle press evts for adj ctrls, as that's the only evt encoders generate.
293
296
// But we can handle short and long holds and releases for the sel ctrls (always buttons).
294
297
// TODO needs alt handling
295
298
296
- // Before all else, is it a press while the beeper is sounding ? Silence it
297
- if (soundRemain >0 && evt==1 ){
298
- stoppingSound = true ;
299
- soundRemain = 0 ;
300
- noTone (10 );
299
+ // Before all else, is it a press to stop the signal ? Silence it
300
+ if (signalRemain >0 && evt==1 ){
301
+ stoppingSignal = true ;
302
+ signalRemain = 0 ;
303
+ if (signalType== 0 ) noTone (signalPin );
301
304
// If we're displaying the clock (as alarm trigger does), start snooze. 0 will have no effect
302
305
if (fn==fnIsTime) snoozeRemain = readEEPROM (24 ,false )*60 ;
303
306
return ;
304
307
}
305
308
// After pressing to silence, short hold cancels a snooze; ignore other btn evts
306
- if (stoppingSound ){
307
- stoppingSound = false ;
309
+ if (stoppingSignal ){
310
+ stoppingSignal = false ;
308
311
if (evt==2 && snoozeRemain>0 ) {
309
312
snoozeRemain = 0 ;
310
- tone (10 , 3136 , 100 ); // G7
313
+ // A short beep at alarm pitch. Use signalBeepDur or 100ms, whichever is smaller
314
+ if (signalType==0 ) tone (signalPin, getHz (readEEPROM (39 ,false )), (signalBeepDur<100 ?signalBeepDur:100 ));
311
315
}
312
316
btnStop ();
313
317
return ;
@@ -620,7 +624,7 @@ void checkRTC(bool force){
620
624
if (pollLast-inputLast>120000 ) { fnSetPg = 0 ; fn = fnIsTime; force=true ; } // Time out after 2 mins
621
625
}
622
626
// Temporary-display mode timeout: if we're *not* in a permanent one (time, day counter, or running timer)
623
- else if (fn!=fnIsTime && fn!=fnIsCleaner && fn!=fnIsDayCount && !(fn==fnIsTimer && (timerRemain>0 || soundRemain >0 ))){
627
+ else if (fn!=fnIsTime && fn!=fnIsCleaner && fn!=fnIsDayCount && !(fn==fnIsTimer && (timerRemain>0 || signalRemain >0 ))){
624
628
if (pollLast>inputLast+5000 ) { fnSetPg = 0 ; fn = fnIsTime; force=true ; }
625
629
}
626
630
}
@@ -642,7 +646,7 @@ void checkRTC(bool force){
642
646
if (readEEPROM (23 ,false )==0 || // any day of the week
643
647
(readEEPROM (23 ,false )==1 && toddow>=readEEPROM (33 ,false ) && toddow<=readEEPROM (34 ,false )) || // weekday only
644
648
(readEEPROM (23 ,false )==2 && toddow<readEEPROM (33 ,false ) && toddow>readEEPROM (34 ,false ))) { // weekend only
645
- fnSetPg = 0 ; fn = fnIsTime; soundRemain = alarmDur *60 ;
649
+ fnSetPg = 0 ; fn = fnIsTime; signalPitch = getHz ( readEEPROM ( 39 , false )); signalRemain = signalDur *60 ;
646
650
} // end toddow check
647
651
} // end alarm trigger
648
652
// checkDigitCycle();
@@ -657,26 +661,26 @@ void checkRTC(bool force){
657
661
if (timerRemain>0 ) {
658
662
timerRemain--;
659
663
if (timerRemain<=0 ) { // timer has elasped
660
- if (readEEPROM (25 ,false )) { // interval timer: sound for 1sec and restart; don't change to timer fn
661
- soundRemain = 1 ; timerRemain = timerInitial;
664
+ signalPitch = getHz (readEEPROM (40 ,false ));
665
+ if (readEEPROM (25 ,false )) { // interval timer: a short signal and restart; don't change to timer fn
666
+ signalRemain = 1 ; timerRemain = timerInitial;
662
667
} else {
663
- fnSetPg = 0 ; fn = fnIsTimer; inputLast = pollLast; soundRemain = alarmDur *60 ;
668
+ fnSetPg = 0 ; fn = fnIsTimer; inputLast = pollLast; signalRemain = signalDur *60 ;
664
669
}
665
670
// TODO radio mode
666
671
} // end timer elapsed
667
672
}
668
- // If beeper has time on it, decrement and sound the beeper for 1/2 second
669
- if (soundRemain>0 ) {
670
- soundRemain--;
671
- // tone(10, 1568, 500); //G6
672
- tone (10 , 1760 , 500 ); // A6
673
- // tone(10, 1976, 500); //B6
674
- // tone(10, 2093, 500); //C7
675
- }
676
673
// If alarm snooze has time on it, decrement and trigger beeper if we reach zero (and alarm is still on)
677
674
if (snoozeRemain>0 ) {
678
675
snoozeRemain--;
679
- if (snoozeRemain<=0 && readEEPROM (2 ,false )) { fnSetPg = 0 ; fn = fnIsTime; soundRemain = alarmDur*60 ; }
676
+ if (snoozeRemain<=0 && readEEPROM (2 ,false )) {
677
+ fnSetPg = 0 ; fn = fnIsTime; signalPitch = getHz (readEEPROM (39 ,false )); signalRemain = signalDur*60 ;
678
+ }
679
+ }
680
+ // If signal has time on it, decrement and make a beep
681
+ if (signalRemain>0 ) {
682
+ signalRemain--;
683
+ if (signalType==0 ) tone (signalPin, signalPitch, signalBeepDur);
680
684
}
681
685
if (unoffRemain>0 ) {
682
686
unoffRemain--; // updateDisplay will naturally put it back to off state if applicable
@@ -776,6 +780,9 @@ void updateDisplay(){
776
780
if (fnSetVal>=60 ) editDisplay (fnSetVal/60 , 0 , 1 , false ); else blankDisplay (0 ,1 ); // hour only if present, else blank
777
781
editDisplay (fnSetVal%60 , 2 , 3 , (fnSetVal>=60 ?true :false )); // leading zero only if hour present
778
782
editDisplay (0 ,4 ,5 ,true ); // placeholder seconds
783
+ } else if (fnSetValMax==88 ) { // A piezo pitch. Play a short demo beep.
784
+ editDisplay (fnSetVal, 0 , 3 , false );
785
+ if (signalType==0 ) tone (signalPin, getHz (fnSetVal), (signalBeepDur<100 ?signalBeepDur:100 ));
779
786
} else editDisplay (fnSetVal, 0 , 3 , false ); // some other type of value
780
787
}
781
788
else if (fn >= fnOpts){ // options menu, but not setting a value
0 commit comments