Skip to content

Commit 13c64ec

Browse files
committed
Display time of day with RTC, fn value setting for time and date w/RTC, hold adj set w/ velocity, .ino move, readme update
1 parent 66eb215 commit 13c64ec

File tree

2 files changed

+190
-64
lines changed

2 files changed

+190
-64
lines changed

README.md

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,32 @@ This is an alternate codebase that is very much in progress!
66

77
## Current instructions (WIP)
88

9-
* Normal running mode simply shows a number (42 at startup).
10-
* To set, hold S4 'til display flashes; push/hold S2/S3 to set the number up/down; push S4 to save.
9+
**As of this commit:**
1110

12-
* To enter the *Setup* menu, hold for 3 seconds.
11+
* Normal running mode shows time of day.
12+
* To set time, hold S6 'til display flashes; push/hold S2/S3 to set the time up/down (in 24h format), and push S6 save at top of minute (seconds will set to :00).
13+
14+
* To enter the **Setup** menu, hold S6 for 3 seconds.
1315
* Shows option number on small tubes, and current setting on big tubes.
14-
* Push to cycle through options (listed below); turn to set; hold to exit.
16+
* Push S6 to cycle through options (listed below); push S2/S3 to set up/down; pass all options or hold S6 to exit.
1517

1618
| Option | Possible Settings |
1719
| --- | --- |
18-
| 1. Dimming | 0 = Normal running mode will display at full brightness<br/>1 = Normal running mode will display dim (25% duty cycle) |
19-
| 2. Nothing | Any number from 1 to 7. Has no effect yet. |
20-
| 3. Nothing | Any number from 1 to 13. Has no effect yet. |
20+
| 1. Time format | 12- or 24-hour (time display only; setting is always done in 24h) |
21+
| 2. n/a | |
22+
| 3. n/a | |
23+
| 4. Leading zero in hour, date, and month? | 0 = no<br/>1 = yes |
24+
| 5. A number | (no effect) |
2125

2226
## Todos
2327

24-
* Add hold-advance for adj buttons
2528
* Add support for rotary encoders
2629
* Add velocity
2730
* Configurable run controls
2831
* Test
2932
* Flesh out menu
3033
* Make settings save in eeprom(?)
31-
* Reintroduce Time
32-
* Make settable - if setting is opened in latter half of minute, setting value should be min+1 to make sync easy
34+
* Make it loop
3335
* Date
3436
* Make settable
3537
* Alarm
@@ -54,18 +56,18 @@ _These instructions are for a clock equipped with a single knob/button (rotary e
5456

5557
* **Time**
5658
* Shows the current time of day. (Choose 12h or 24h format in Setup.)
57-
* To set, hold 'til display flashes; turn to set (in 24h format), and push to save at top of minute (seconds will set to :00).
59+
* To set time, hold 'til display flashes; turn to set (in 24h format), and push to save at top of minute (seconds will set to :00).
5860
* **Date**
5961
* Shows month, date, and weekday as 0=Sunday, 6=Saturday. (Choose month/date or date/month in Setup.)
60-
* To set, hold 'til display flashes; then set year, then month, then date.
62+
* To set date, hold 'til display flashes; then set year, then month, then date.
6163
* **Alarm**
6264
* Shows alarm time (in 24h format) and whether the alarm is on (1) or off (0).
6365
* To turn alarm on or off, push the knob.
6466
* To set alarm, hold 'til display flashes; turn to set (in 24h format), and push to save.
6567
* When alarm goes off, push to snooze, or hold to silence until tomorrow.
6668
* **Timer**
6769
* Shows a countdown timer (or 0min 00sec when idle).
68-
* To set, hold 'til display flashes; set time (in hrs, min, sec); and press to start. Hold to cancel.
70+
* To set timer, hold 'til display flashes; set time (in hrs, min, sec); and press to start. Hold to cancel.
6971
* When timer goes off, push to silence.
7072

7173
**Additional settings are available in the Setup menu.**

sixtube_lm.ino renamed to sixtube_lm/sixtube_lm.ino

Lines changed: 175 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
// S7/PL14 = A7
2121

2222
// What input is associated with each control?
23-
const byte mainSel = A6; //main select button - must be equipped //TODO test this with A6
23+
const byte mainSel = A2; //main select button - must be equipped //TODO test this with A6
2424
const byte mainAdj[2] = {A1,A0}; //main up/down buttons or rotary encoder - must be equipped
2525
const byte altSel = 0; //alt select button - if unequipped, set to 0
26-
const byte altAdj[2] = {A3,A2}; //alt up/down buttons or rotary encoder - if unequipped, need not define this
26+
const byte altAdj[2] = {A6,A3}; //alt up/down buttons or rotary encoder - if unequipped, need not define this
2727

2828
// What type of adj controls are equipped?
2929
// 1 = momentary buttons. 2 = quadrature rotary encoder.
@@ -55,36 +55,52 @@ const int btnLongHold = 3000; //for for entering SETUP menu
5555

5656

5757
////////// Major global vars/consts //////////
58-
byte fnCt = 4;
58+
int rtcLast[6] = {0,0,0,0,0,-1}; //y,m,d,h,i,s. -1 to force initial set
59+
byte fnCt = 4; //number of functions in the clock
5960
byte fn = 0; //currently displayed function: 0=time, 1=date, 2=alarm, 3=timer, 255=SETUP menu
6061
byte fnSet = 0; //whether this function is currently being set, and which option/page it's on
61-
byte setupOptsCt = 3; //1-index
62-
byte setupOpts[4] = {0,0,1,1}; //see ctrlEvt() switch(fnSet) for what these are/do
62+
//parameters of value currently being set, if any
63+
int fnSetVal; //the value currently being set, if any
64+
int fnSetValMin; //min possible
65+
int fnSetValMax; //max possible
66+
bool fnSetValVel; //whether it supports velocity setting
67+
//Values we could be setting:
68+
//Time (mins): 0–1439, supports velocity
69+
//Year: 2000–32767, supports velocity
70+
//Month: 1-12, no velocity
71+
//Date: 1-[max for this month], no velocity
72+
//or some other number
73+
byte setupOptsCt = 5; //number of options in the Setup menu, 1-index
74+
byte setupOpts[6] = {0,2,1,0,0,42}; //values of options – see "setup opts details" for what these do
6375

6476
byte displayNext[6] = {15,15,15,15,15,15}; //Blank tubes at start. When display should change, put it here
65-
int btnPresses = 0;
77+
//int btnPresses = 0;
6678

6779

6880
////////// Includes and main code control //////////
6981

7082
//#include <EEPROM.h>
71-
//#include <Wire.h>
72-
//#include "RTClib.h"
73-
//RTC_DS1307 RTC;
83+
#include <Wire.h>
84+
#include <RTClib.h>
85+
RTC_DS1307 rtc;
7486

7587
void setup(){
76-
Serial.begin(57600);
77-
//Wire.begin(); TODO
78-
//RTC.begin(); TODO
88+
//Serial.begin(57600);
89+
Wire.begin();
90+
rtc.begin();
91+
if(!rtc.isrunning()) rtc.adjust(DateTime(2017,1,1,0,0,0)); //TODO test
7992
initOutputs();
8093
initInputs();
94+
checkRTC();
8195
updateDisplay(); //initial fill of data
8296
}
8397

8498
void loop(){
8599
//Things done every "clock cycle"
86-
checkInputs(); //will do things if necessary and updateDisplay
87-
cycleDisplay(); //keeps the multiplexing cycle going
100+
checkRTC(); //if clock has ticked, decrement timer if running, and updateDisplay
101+
checkInputs(); //if inputs have changed, this will do things + updateDisplay as needed
102+
fnSetHoldCheck(); //if inputs have been held, this will do more things + updateDisplay as needed
103+
cycleDisplay(); //keeps the display hardware multiplexing cycle going
88104
}
89105

90106

@@ -177,13 +193,66 @@ bool readInput(byte pin){
177193
if(pin==A6 || pin==A7) return analogRead(pin)<100?0:1; //analog-only pins
178194
else return digitalRead(pin);
179195
}
180-
void btnStop(){ btnCurHeld = 4; }
196+
void btnStop(){
197+
//In some cases, when handling btn evt 1/2/3, we may call this
198+
//so following events 2/3/0 won't cause unintended behavior
199+
//(e.g. after a fn change, or going in or out of set)
200+
btnCurHeld = 4;
201+
}
181202

182203
void ctrlEvt(byte ctrl, byte evt){
183-
//Handle button (for now) events.
184-
//In some cases, when reacting to evt 1/2/3, we may want to call btnStop()
185-
//so following events 2/3/0 don't cause unintended behavior.
186-
if(fn==255) { //SETUP menu
204+
//Handle button-like events, based on current fn and set state.
205+
//Currently only main, not alt... TODO
206+
bool mainSelPush = (ctrl==mainSel && evt==1?1:0);
207+
bool mainSelHold = (ctrl==mainSel && evt==2?1:0);
208+
bool mainAdjUpPush = (ctrl==mainAdj[0] && evt==1?1:0);
209+
bool mainAdjDnPush = (ctrl==mainAdj[1] && evt==1?1:0);
210+
211+
if(fn!=255 && ctrl==mainSel && evt==3) { //joker mainSel long hold: enter SETUP menu
212+
btnStop(); fn=255; fnSet=1; updateDisplay(); return;
213+
}
214+
215+
if(fn!=255) { //normal fn running/setting
216+
if(!fnSet) { //fn running
217+
if(mainSelHold) { //enter fnSet
218+
//btnStop(); prevents joker
219+
switch(fn){
220+
case 0: setSet((rtcLast[3]*60)+rtcLast[4],0,1439,1,1); break; //time: set mins
221+
case 1: setSet(rtcLast[0],2000,32767,1,1); break; //date: set year
222+
case 2: //alarm: set mins
223+
case 3: //timer: set mins
224+
default: break;
225+
}
226+
return;
227+
}
228+
if(mainSelPush) { //mainSel press: do nothing TODO
229+
//btnPresses++; log("Incrementing counter to "); log(String(btnPresses)); log("\n"); updateDisplay(); return;
230+
return;
231+
}
232+
} //end fn running
233+
else { //fn setting
234+
if(mainAdjUpPush) fnSetDo(1);
235+
if(mainAdjDnPush) fnSetDo(-1);
236+
//change (adj hold will be handled by fnSetHoldCheck(), but here we'll need press velocity to treat encoder changes like very rapid button presses
237+
//no mainSelHold will happen here
238+
if(mainSelPush) { //go to next option or save and exit fnSet
239+
btnStop();
240+
switch(fn){
241+
case 0: setRTC(3,fnSetVal/60,fnSetVal%60,0); clearSet(); break; //time: save
242+
case 1: switch(fnSet){
243+
case 1: setRTC(0,fnSetVal,0,0); setSet(rtcLast[1],1,12,0,2); break; //date: save year, set month
244+
case 2: setRTC(1,fnSetVal,0,0); setSet(rtcLast[2],1,getDaysInMonth(rtcLast[0],rtcLast[1]),0,3); break; //date: save month, set date
245+
case 3: setRTC(2,fnSetVal,0,0); clearSet(); break;
246+
default: break;
247+
} break;
248+
case 2: //alarm
249+
case 3: //timer
250+
default: break;
251+
}
252+
}
253+
} //end fn setting
254+
} //end normal fn running/setting
255+
else { //setup menu
187256
if(ctrl==mainSel) {
188257
if(evt==1) { //mainSel press
189258
if(fnSet==setupOptsCt) { //that was the last one – exit
@@ -206,38 +275,37 @@ void ctrlEvt(byte ctrl, byte evt){
206275
btnStop(); //no more events from this press
207276
int delta = (ctrl==mainAdj[0]?1:-1);
208277
switch(fnSet){ //This is where we set the ranges for each option
278+
//setup opts details
209279
//setupOpts[0] exists but is just a pad to make it 1-index for programming convenience
210-
case 1: changeInRangeAr(setupOpts, 1, delta, 0, 1); break; //dim no/yes. Default: 0
211-
case 2: changeInRangeAr(setupOpts, 2, delta, 1, 7); break;
212-
case 3: changeInRangeAr(setupOpts, 3, delta, 1, 13); break;
280+
case 1: changeInRangeAr(setupOpts, 1, delta, 1, 2); break; //1=12hr, 2=24hr
281+
case 2: changeInRangeAr(setupOpts, 2, delta, 1, 2); break; //1=m/d/w, 2=d/m/w
282+
case 3: changeInRangeAr(setupOpts, 3, delta, 0, 2); break; //date during time: 0=no, 1=d->s, 2=date@:30
283+
case 4: changeInRangeAr(setupOpts, 4, delta, 0, 1); break; //leading zero no/yes
284+
case 5: changeInRangeAr(setupOpts, 5, delta, 1, 9999); break; //random number
213285
default: break;
214286
}
215287
updateDisplay(); return;
216-
} //end mainAdj
217-
} else { //not SETUP menu
218-
if(ctrl==mainSel && evt==3) { //mainSel long hold: enter SETUP
219-
btnStop(); //silence, you
220-
log("Entering SETUP per mainSel long hold\n");
221-
fn=255; fnSet=1; updateDisplay(); return;
222-
} else {
223-
if(fnSet) { //function setting
224-
//press: go to next option or save and exit fnSet
225-
switch(fn){
226-
case 0: //time
227-
case 1: //date
228-
case 2: //alarm
229-
case 3: //timer
230-
default: break;
231-
}
232-
} else { //normal function running
233-
//short hold: enter fnSet
234-
//press: increment counter
235-
if(ctrl==mainSel && evt==1) {btnPresses++; log("Incrementing counter to "); log(String(btnPresses)); log("\n"); updateDisplay(); return; }
236-
}
237-
}
288+
} //end mainAdj
289+
}//end setup
290+
}
291+
void setSet(int n, int m, int x, bool v, byte p){
292+
fnSetVal=n; fnSetValMin=m; fnSetValMax=x; fnSetValVel=v; fnSet=p;
293+
updateDisplay();
294+
}
295+
void clearSet(){ setSet(0,0,0,0,0); }
296+
297+
void setRTC(byte unit, int v1, int v2, int v3){
298+
//Use rtcLast as a sort of rtcNext
299+
if(unit==3) { //time
300+
rtcLast[3]=v1; rtcLast[4]=v2; rtcLast[5]=v3;
301+
} else { //date
302+
rtcLast[unit] = v1; //Set in the new value. But in case current date doesn't exist in new yr/mo, change it
303+
if(unit<2) { byte dIM = getDaysInMonth(rtcLast[0],rtcLast[1]); if(rtcLast[2]>dIM) rtcLast[2]==dIM; }
238304
}
239-
305+
rtc.adjust(DateTime(rtcLast[0],rtcLast[1],rtcLast[2],rtcLast[3],rtcLast[4],rtcLast[5]));
240306
}
307+
308+
//TODO replace these with the fnSet temporary value
241309
void changeInRangeAr(byte ar[], byte i, int delta, byte minVal, byte maxVal){
242310
ar[i] = changeInRange(ar[i], delta, minVal, maxVal);
243311
}
@@ -249,8 +317,52 @@ byte changeInRange(byte curVal, int delta, byte minVal, byte maxVal){
249317
if(delta<0) if(curVal-minVal<abs(delta)) return minVal; else return curVal+delta;
250318
}
251319

320+
void fnSetDo(int delta){
321+
//Does actual setting of fnSetVal, as told to by ctrlEvt or fnSetHoldCheck, within params of fnSetVal vars
322+
if(delta>0) if(fnSetValMax-fnSetVal<delta) fnSetVal=fnSetValMax; else fnSetVal=fnSetVal+delta;
323+
if(delta<0) if(fnSetVal-fnSetValMin<abs(delta)) fnSetVal=fnSetValMin; else fnSetVal=fnSetVal+delta;
324+
updateDisplay();
325+
}
326+
327+
unsigned long fnSetHoldLast;
328+
void fnSetHoldCheck(){
329+
//When we're setting via an adj button that's passed a hold point, fire off fnSet commands at intervals
330+
if(fnSetHoldLast+250<millis()) { //TODO is it a problem this won't sync up with the blinking?
331+
fnSetHoldLast = millis();
332+
if(fnSet!=0 && ((mainAdjType==1 && (btnCur==mainAdj[0] || btnCur==mainAdj[1])) || (altAdjType==1 && (btnCur==altAdj[0] || btnCur==altAdj[1]))) ){ //if we're setting, and this is an adj input for which the type is button
333+
bool dir = (btnCur==mainAdj[0] || btnCur==altAdj[0] ? 1 : 0);
334+
//If short hold, or long hold but high velocity isn't supported, use low velocity (delta=1)
335+
if(btnCurHeld==2 || (btnCurHeld==3 && fnSetValVel==false)) fnSetDo(dir?1:-1);
336+
//else if long hold, use high velocity (delta=10)
337+
else if(btnCurHeld==3) fnSetDo(dir?10:-10);
338+
}
339+
}
340+
}
341+
252342

253343
////////// Display data formatting //////////
344+
unsigned int rtcPollLast = 0; //maybe don't poll the RTC every loop? would that be good?
345+
void checkRTC(){
346+
//TODO why are the digits flashing before changing? I've got some blocking code happening? is it here?
347+
//If in fn time or timer, updates display when RTC time changes. Also decrements running timer.
348+
if(rtcPollLast<millis()+50) { //check every 1/20th of a second
349+
rtcPollLast=millis();
350+
DateTime now = rtc.now();
351+
if(rtcLast[5] != now.second()) {
352+
rtcLast[0] = now.year();
353+
rtcLast[1] = now.month();
354+
rtcLast[2] = now.day();
355+
rtcLast[3] = now.hour();
356+
rtcLast[4] = now.minute();
357+
rtcLast[5] = now.second();
358+
//TODO decrement timer if appropriate
359+
if(fn==0 || fn==3) { //clock or timer
360+
updateDisplay();
361+
}
362+
}
363+
}
364+
}
365+
254366
void updateDisplay(){
255367
//Only run as often as the data behind the display changes (clock tick, setting change, etc.)
256368
//This function takes that data and uses it to edit displayNext[] for cycleDisplay() to pick up
@@ -266,11 +378,19 @@ void updateDisplay(){
266378
case 3: //timer
267379
break;
268380
default: //clock
269-
//TODO currently we are just displaying the btnPresses count
270-
log("Per normal running mode, displaying btnPresses="); log(String(btnPresses)); log("\n");
271-
editDisplay(btnPresses, 0, 3, true); //big tubes: counter – pad with leading zeros
272-
blankDisplay(4,5); //small tubes blank
273-
break;
381+
if(!fnSet){
382+
byte hr = rtcLast[3];
383+
if(setupOpts[1]==1) hr = (hr==0?12:(hr>12?hr-12:hr));
384+
editDisplay(hr, 0, 1, setupOpts[4]);
385+
editDisplay(rtcLast[4], 2, 3, true);
386+
editDisplay(rtcLast[5], 4, 5, true);
387+
} else {
388+
//display clock setting
389+
editDisplay(fnSetVal/60, 0, 1, setupOpts[4]);
390+
editDisplay(fnSetVal%60, 2, 3, true);
391+
blankDisplay(4, 5);
392+
}
393+
break;
274394
} //end switch fn
275395
} //end updateDisplay()
276396
void editDisplay(int n, byte posStart, byte posEnd, bool leadingZeros){
@@ -327,7 +447,7 @@ float displayLastFade[6]={8.0f,8.0f,8.0f,8.0f,8.0f,8.0f}; //Fading out displayLa
327447
unsigned long setStartLast = 0; //to control flashing
328448

329449
void cycleDisplay(){
330-
bool dim = (setupOpts[1]>0?true:false); //Under normal circumstances, dim constantly if the time is right
450+
bool dim = 0;//(setupOpts[2]>0?true:false); //Under normal circumstances, dim constantly if the time is right
331451
if(fnSet>0) { //but if we're setting, dim for every other 500ms since we started setting
332452
if(setStartLast==0) setStartLast = millis();
333453
dim = 1-(((millis()-setStartLast)/500)%2);
@@ -397,6 +517,10 @@ void decToBin(bool binVal[], int i){
397517

398518

399519
////////// Misc global utility functions //////////
520+
const byte daysInMonth[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
521+
byte getDaysInMonth(int y, int m){
522+
if(m==2) return (y%4==0 && (y%100!=0 || y%400==0) ? 29 : 28); else return daysInMonth[m];
523+
}
400524
void log(String s){
401525
// while (!Serial) ;
402526
// Serial.print(s);

0 commit comments

Comments
 (0)