Skip to content

Commit 530d489

Browse files
committed
Bugfixes and support for timer sel reset long hold
1 parent 2728ae4 commit 530d489

File tree

3 files changed

+49
-37
lines changed

3 files changed

+49
-37
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,23 @@ The alarm is always shown in 24h format so you can tell AM from PM.
4747

4848
This feature can count down (timer) or up (chrono) to 100 hours. When idle, it displays `0` (or `000000` if you have leading zeros enabled).
4949

50-
* To start and stop, use **Up**. While at `0`, this will start the chrono.
50+
* To start and stop, press **Up**. While at `0`, this will start the chrono.
5151
* While the chrono is running, **Down** will briefly display a lap time.
52-
* To set the timer, hold **Select** while at `0`. It will prompt for hours/minutes first, then seconds. For convenience, it will recall the last-used time. Once the timer is set, use **Up** to start it.
52+
* To set the timer, hold **Select** while at `0`. It will prompt for hours/minutes first, then seconds. For convenience, it will recall the last-used time. Once the timer is set, press **Up** to start it.
5353
* While the timer is running, **Down** will cycle through the runout options (what the timer will do when it runs out – clocks with beeper only):
5454
* 1 beep: simply stop, with a long signal (default)
5555
* 2 beeps: restart, with a short signal (makes a great interval timer!)
5656
* 3 beeps: start the chrono, with a long signal
5757
* 4 beeps: start the chrono, with a short signal
58+
* To reset the timer to the last-used time, press **Down** while stopped.
5859
* To reset to `0`, hold **Select**.
5960
* It will automatically reset if you switch to a different function while it’s stopped, if it’s left stopped for an hour, if the chrono reaches 100 hours, or if power is lost. However, you can switch functions while it’s running, and it will continue to run in the background.
6061
* When the timer signal sounds, press any button to silence it.
6162

6263
Some variations may apply, depending on your [hardware configuration](#hardware-configuration):
6364

64-
* If your clock has a switched relay and the timer/chrono is set to use it (in the [options menu](#options-menu)), it will switch on while the timer/chrono is running, like the “sleep” function on a clock radio.
65-
* If your clock does not have a beeper, the runout options are not available.
65+
* If your clock has a switched relay and the timer/chrono is set to use it (in the [options menu](#options-menu)), it will switch on while the timer is running, like the “sleep” function on a clock radio. The runout options will still work, but won’t signal.
66+
* If your clock does not have a beeper, the runout options cannot be set.
6667
* If your clock uses a rotary encoder for **Up/Down** rather than buttons, the controls are slightly different:
6768
* To start, use **Up**.
6869
* While the chrono is running, **Up** will briefly display a lap time.

arduino-nixie/arduino-nixie.ino

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ void ctrlEvt(byte ctrl, byte evt){
346346
case fnIsAlarm: //set mins
347347
startSet(readEEPROM(0,true),0,1439,1); break;
348348
case fnIsTimer: //set mins
349-
if(timerTime!=0 || timerState&1) { timerClear(); btnStop(); updateDisplay(); break; } //If the timer is nonzero or running, zero it out.
349+
if(timerTime!=0 || timerState&1) { quickBeep(57); timerClear(); updateDisplay(); break; } //If the timer is nonzero or running, zero it
350350
startSet(timerInitialMins,0,5999,1); break; //minutes
351351
//fnIsDayCount removed in favor of paginated calendar
352352
case fnIsTemp: //could do calibration here if so inclined
@@ -357,7 +357,7 @@ void ctrlEvt(byte ctrl, byte evt){
357357
}
358358
else if((ctrl==mainSel && evt==0) || ((ctrl==mainAdjUp || ctrl==mainAdjDn) && evt==1)) { //sel release or adj press
359359
//we can't handle sel press here because, if attempting to enter setting mode, it would switch the fn first
360-
if(ctrl==mainSel){
360+
if(ctrl==mainSel && !(fn==fnIsTimer && btnCurHeld==2)){ //sel release - except after a timer short hold (possibly to clear timer)
361361
if(fn==fnIsTimer && !(timerState&1)) timerClear(); //if timer is stopped, clear it
362362
fnScroll(1); //Go to next fn in the cycle
363363
fnPg = 0; //reset page counter in case we were in a paged display
@@ -379,8 +379,14 @@ void ctrlEvt(byte ctrl, byte evt){
379379
}
380380
} else { //mainAdjDown
381381
if(!(timerState&1)){ //stopped
382-
if(mainAdjType!=2 && false) { //button only - I think it's too easy to reset the timer like this
383-
timerClear();
382+
if(mainAdjType!=2) { //button
383+
quickBeep(57); timerClear();
384+
//same as //save timer secs
385+
timerTime = (timerInitialMins*60000)+(timerInitialSecs*1000); //set timer duration
386+
if(timerTime!=0){
387+
bitWrite(timerState,1,0); //set timer direction (bit 1) to down (0)
388+
//timerStart(); //we won't automatically start, we'll let the user do that
389+
}
384390
updateDisplay();
385391
}
386392
} else { //running
@@ -395,7 +401,8 @@ void ctrlEvt(byte ctrl, byte evt){
395401
} //end if fnIsTimer
396402
//if(fn==fnIsTime) TODO volume in I2C radio
397403
}
398-
}
404+
//else do nothing
405+
} //end sel release or adj press
399406
else if(altSel>0 && ctrl==altSel) { //alt sel press
400407
//if switched relay, and soft switch enabled, we'll switch power.
401408
if(enableSoftPowerSwitch && relayPin>=0 && relayMode==0) { switchPower(2); btnStop(); }
@@ -452,11 +459,12 @@ void ctrlEvt(byte ctrl, byte evt){
452459
if(fnPg==0){ //regular date display: save in RTC
453460
switch(fnSetPg){
454461
case 1: //save year, set month
455-
delay(300); //blink display to indicate save. Safe b/c we've btnStopped. See below for why
462+
delay(200); //blink display to indicate save. Safe b/c we've btnStopped. See below for why
463+
//TODO find another way to do this bc it could briefly screw up the ms() calibration by up to 200ms, the way cycleDisplay does
456464
fnSetValDate[0]=fnSetVal;
457465
startSet(fnSetValDate[1],1,12,2); break;
458466
case 2: //save month, set date
459-
delay(300); //blink display to indicate save. Needed if set month == date: without blink, nothing changes.
467+
delay(200); //blink display to indicate save. Needed if set month == date: without blink, nothing changes.
460468
fnSetValDate[1]=fnSetVal;
461469
startSet(fnSetValDate[2],1,daysInMonth(fnSetValDate[0],fnSetValDate[1]),3); break;
462470
case 3: //write year/month/date to RTC
@@ -472,12 +480,12 @@ void ctrlEvt(byte ctrl, byte evt){
472480
} else if(fnPg==fnDateCounter){ //set like date, save in eeprom like finishOpt
473481
switch(fnSetPg){
474482
case 1: //save month, set date
475-
delay(300); //blink display to indicate save. Safe b/c we've btnStopped.
483+
delay(200); //blink display to indicate save. Safe b/c we've btnStopped.
476484
//Needed if set month == date: without blink, nothing changes. Also just good feedback.
477485
writeEEPROM(5,fnSetVal,false);
478486
startSet(readEEPROM(6,false),1,daysInMonth(fnSetValDate[0],fnSetValDate[1]),2); break;
479487
case 2: //save date, set direction
480-
delay(300);
488+
delay(200);
481489
writeEEPROM(6,fnSetVal,false);
482490
startSet(readEEPROM(4,false),0,1,3); break;
483491
case 3: //save date
@@ -496,11 +504,12 @@ void ctrlEvt(byte ctrl, byte evt){
496504
clearSet(); break;
497505
case fnIsTimer: //timer - depends what page we're on
498506
switch(fnSetPg){
499-
case 1: //save mins, set secs
500-
delay(300); //blink display to indicate save. See fnIsDate for details
507+
case 1: //save timer mins, set timer secs
508+
delay(200); //blink display to indicate save. See fnIsDate for details
501509
timerInitialMins = fnSetVal; //minutes, up to 5999 (99m 59s)
502510
startSet(timerInitialSecs,0,59,2); break;
503-
case 2: //save secs
511+
case 2: //save timer secs
512+
delay(200);
504513
timerInitialSecs = fnSetVal;
505514
timerTime = (timerInitialMins*60000)+(timerInitialSecs*1000); //set timer duration
506515
if(timerTime!=0){
@@ -637,6 +646,7 @@ void switchPower(byte dir){
637646
if(timerState&1 && !((timerState>>1)&1) && readEEPROM(43,false)==1) {
638647
timerClear();
639648
updateDisplay();
649+
return;
640650
}
641651
//relayPin state is the reverse of the appliance state: LOW = device on, HIGH = device off
642652
// Serial.print(ms(),DEC);
@@ -1108,7 +1118,7 @@ void timerStart(){
11081118
// Serial.print(F("="));
11091119
// Serial.print(timerTime,DEC);
11101120
// Serial.println();
1111-
timerSwitchSleepRelay(1);
1121+
if(!((timerState>>1)&1)) timerSwitchSleepRelay(1); //possibly switch the relay, but only if counting down
11121122
quickBeep(69);
11131123
} //end timerStart()
11141124
void timerStop(){
@@ -1131,7 +1141,7 @@ void timerStop(){
11311141
// Serial.print(F("="));
11321142
// Serial.print(timerTime,DEC);
11331143
// Serial.println();
1134-
timerSwitchSleepRelay(0);
1144+
if(!((timerState>>1)&1)) timerSwitchSleepRelay(0); //possibly switch the relay, but only if counting down
11351145
quickBeep(64);
11361146
bitWrite(timerState,4,0); //set timer lap display (bit 4) to off (0)
11371147
updateDisplay(); //since cycleTimer won't do it
@@ -1150,13 +1160,13 @@ void timerLap(){
11501160
quickBeep(73);
11511161
}
11521162
void timerRunoutToggle(){
1153-
if(piezoPin>=0 && readEEPROM(43,false)==0){ //if piezo, and timer signal is using it
1163+
if(piezoPin>=0){ //if piezo equipped
11541164
//cycle thru runout options: 00 stop, 01 repeat, 10 chrono, 11 chrono short signal
11551165
timerState ^= (1<<2); //toggle runout repeat bit
11561166
if(!((timerState>>2)&1)) timerState ^= (1<<3); //if it's 0, toggle runout chrono bit
11571167
//do a quick signal to indicate the selection
11581168
signalPattern = ((timerState>>2)&3)+1; //convert 00/01/10/11 to 1/2/3/4
1159-
signalSource = fnIsTimer;
1169+
signalSource = fnIsTimer; //we'll get the piezo pitch even if timer isn't using it
11601170
signalStart(-1,0); //Play pulse using above pattern and source
11611171
}
11621172
}
@@ -1708,7 +1718,8 @@ void signalStart(byte sigFn, byte sigDur){ //make some noise! or switch on an ap
17081718
// or 0 for a single pulse as applicable (i.e. skipped in radio mode).
17091719
//Special case: if sigFn==fnIsAlarm, and sigDur>0, we'll use signalDur or switchDur as appropriate.
17101720
//If sigFn is given as -1 (255), we will use both the existing signalSource and signalPattern for purposes of configs and fibonacci.
1711-
signalStop(); //if there is a signal going per the current signalSource, stop it - can only have one signal at a time
1721+
if(!(sigFn==255 && signalSource==fnIsTimer)) signalStop(); // if there is a signal going per the current signalSource, stop it - can only have one signal at a time – except if this is a forced fnIsTimer signal (for signaling runout options) which is cool to overlap timer sleep
1722+
//except if this is a forced
17121723
if(sigFn!=255) signalSource = sigFn;
17131724
if(sigFn!=255) signalPattern = (
17141725
(signalSource==fnIsTime && readEEPROM(21,false)==2)? -1: //special case: the pips
@@ -1723,25 +1734,24 @@ void signalStart(byte sigFn, byte sigDur){ //make some noise! or switch on an ap
17231734
// Serial.print(F(", pattern="));
17241735
// Serial.print(signalPattern,DEC);
17251736
// Serial.println();
1726-
if(sigDur==0){ //single pulse
1727-
//not adding any time to signalRemain
1728-
signalPulseStartTime = ms();
1729-
signalPulseStep = 1;
1730-
//cycleSignal will pick up from here
1731-
}
1732-
else { //long-duration signal (alarm, sleep, etc)
1733-
if(getSignalOutput()==1 && relayPin>=0 && relayMode==0) { //switched relay: turn it on now
1737+
1738+
//Set up for a single pulse - no signalRemain
1739+
signalPulseStartTime = ms();
1740+
signalPulseStep = 1;
1741+
if(sigDur!=0){ //long-duration signal (alarm, sleep, etc)
1742+
//If switched relay, except if this is a forced fnIsTimer signal (for signaling runout options)
1743+
if(getSignalOutput()==1 && relayPin>=0 && relayMode==0 && !(sigFn==255 && signalSource==fnIsTimer)) { //turn it on now
17341744
signalRemain = (sigFn==fnIsAlarm? switchDur: sigDur); //For alarm signal, use switched relay duration (eg 2hr)
1745+
signalPulseStep = -1; //it will be perpetually waiting for the next pulse
17351746
digitalWrite(relayPin,LOW); //LOW = device on
1747+
updateLEDs(); //LEDs following signal or relay
17361748
//Serial.print(ms(),DEC); Serial.println(F(" Relay on, signalStart"));
17371749
} else { //start pulsing. If there is no beeper or pulsed relay, this will have no effect since cycleSignal will clear it
17381750
signalRemain = (sigFn==fnIsAlarm? signalDur: sigDur); //For alarm signal, use pulse signal duration (eg 2min)
1739-
signalPulseStartTime = ms();
1740-
signalPulseStep = 1;
17411751
}
17421752
}
1743-
updateLEDs(); //LEDs following signal or relay
1744-
}
1753+
//cycleSignal will pick up from here
1754+
} //end signalStart()
17451755
void signalStop(){ //stop current signal and clear out signal timer if applicable
17461756
//Serial.println(F("signalStop"));
17471757
signalRemain = 0; snoozeRemain = 0; signalPulseStep = 0;
@@ -1751,15 +1761,15 @@ void signalStop(){ //stop current signal and clear out signal timer if applicabl
17511761
//Serial.print(ms(),DEC); Serial.println(F(" Relay off, signalStop"));
17521762
updateLEDs(); //LEDs following relay
17531763
}
1754-
}
1764+
} //end signalStop()
17551765
void cycleSignal(){
17561766
//Called on every loop to control the signal.
17571767
//The signal is made up of a series of pulses of a given duration
17581768
word pulseDur = 1000; //Pulse interval (between starts) in ms, by default
17591769
//Each pulse is made up of steps such as triggering beeps or starting/stopping the pulse relay
17601770
//signalPulseStep keeps track of those steps, or -1 (255) if waiting for next pulse, or 0 if not signaling
17611771
if(signalPulseStep){ //if there's a pulse going
1762-
if(getSignalOutput()==0 && piezoPin>=0) { //beeper
1772+
if((getSignalOutput()==0 || (signalRemain==0 && signalSource==fnIsTimer)) && piezoPin>=0) { //beeper, or single pulse for fnIsTimer
17631773
//Since tone() handles the duration of each beep,
17641774
//we only need to use signalPulseStep to track beep starts; they'll stop on their own.
17651775
byte bc = 0; //this many beeps
@@ -1858,7 +1868,8 @@ byte getSignalPattern(){ //for current signal: chime, timer, or (default) alarm:
18581868
return readEEPROM((signalSource==fnIsTime?49:(signalSource==fnIsTimer?48:47)),false);
18591869
}
18601870
void quickBeep(int pitch){
1861-
//Separate from signal system
1871+
//This is separate from signal system
1872+
//F5 = 57
18621873
//C6 = 64
18631874
//F6 = 69
18641875
//G6 = 71

arduino-nixie/configs/v8c-6tube-top-relay.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const byte relayMode = 0; //If relay is equipped, what does it do?
4141
// 0 = switched mode: the relay will be switched to control an appliance like a radio or light fixture. If used with timer, it will switch on while timer is running (like a "sleep" function). If used with alarm, it will switch on when alarm trips; specify duration of this in switchDur.
4242
// 1 = pulsed mode: the relay will be pulsed, like the beeper is, to control an intermittent signaling device like a solenoid or indicator lamp. Specify pulse duration in relayPulse.
4343
const word signalDur = 180; //sec - when pulsed signal is going, pulses are sent once/sec for this period (e.g. 180 = 3min)
44-
const word switchDur = 7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr)
44+
const word switchDur = 30; //7200; //sec - when alarm triggers switched relay, it's switched on for this period (e.g. 7200 = 2hr)
4545
const word piezoPulse = 250; //ms - used with piezo via tone()
4646
const word relayPulse = 200; //ms - used with pulsed relay
4747

0 commit comments

Comments
 (0)