1
+ // //////// Configuration consts //////////
2
+
3
+ // These are the RLB board connections to Arduino analog input pins.
4
+ // S1/PL13 = Reset
5
+ // S2/PL5 = A1
6
+ // S3/PL6 = A0
7
+ // S4/PL7 = A6
8
+ // S5/PL8 = A3
9
+ // S6/PL9 = A2
10
+ // S7/PL14 = A7
11
+ // A6-A7 are analog-only pins that aren't as responsive and require a physical pullup resistor (1K to +5V).
12
+
13
+ // What input is associated with each control?
14
+ const byte mainAdjA = A1; // main up/down buttons or rotary encoder - must be equipped
15
+ const byte mainAdjB = A0;
16
+ const byte mainAdjType = 2 ;
17
+
18
+ // //////// Function prototypes, global consts and vars //////////
19
+
20
+ // Hardware inputs
21
+ // unsigned long inputSampleLast = 0; //millis() of last time inputs (and RTC) were sampled
22
+ // const byte inputSampleDur = 50; //input sampling frequency (in ms) to avoid bounce
23
+ // unsigned long inputLast = 0; //When a button was last pressed / knob was last turned
24
+ // unsigned long inputLast2 = 0; //Second-to-last of above
25
+ // byte btnCurHeld = 0; //Button hold thresholds: 0=none, 1=unused, 2=short, 3=long, 4=set by btnStop()
26
+ bool mainRotLast[2 ] = {0 ,0 }; // last state of main rotary encoder inputs
27
+ void initInputs (); // Set pinModes for inputs; capture initial state of knobs (rotary encoders, if equipped).
28
+ void checkInputs (); // Run at sample rate. Calls checkBtn() for each eligible button and ctrlEvt() for each knob event.
29
+ bool readInput (byte pin); // Does analog or digital read depending on which type of pin it is.
30
+ // void checkBtn(byte btn); //Calls ctrlEvt() for each button event
31
+ // void btnStop(); //Stops further events from a button until it's released, to prevent unintended behavior after an event is handled.
32
+ void checkRot (byte rotA, byte rotB, bool last[], bool triggerEvent);
33
+
34
+ // Display formatting
35
+ byte displayNext[6 ] = {15 ,15 ,15 ,15 ,15 ,15 }; // Internal representation of display. Blank to start. Change this to change tubes.
36
+
37
+ // Hardware outputs
38
+ // This clock is 2x3 multiplexed: two tubes powered at a time.
39
+ // The anode channel determines which two tubes are powered,
40
+ // and the two SN74141 cathode driver chips determine which digits are lit.
41
+ // 4 pins out to each SN74141, representing a binary number with values [1,2,4,8]
42
+ byte binOutA[4 ] = {2 ,3 ,4 ,5 };
43
+ byte binOutB[4 ] = {6 ,7 ,8 ,9 };
44
+ // 3 pins out to anode channel switches
45
+ byte anodes[3 ] = {11 ,12 ,13 };
46
+ void initOutputs ();
47
+ float fadeMax = 5 .0f ;
48
+ float fadeStep = 1 .0f ;
49
+ int displayLast[6 ]={11 ,11 ,11 ,11 ,11 ,11 }; // What is currently being displayed. We slowly fade away from this.
50
+ float displayNextFade[6 ]={0 .0f ,0 .0f ,0 .0f ,0 .0f ,0 .0f ,0 .0f }; // Fading in displayNext values
51
+ float displayLastFade[6 ]={8 .0f ,8 .0f ,8 .0f ,8 .0f ,8 .0f ,8 .0f }; // Fading out displayLast values
52
+ unsigned long setStartLast = 0 ; // to control flashing
53
+ void cycleDisplay (); // Run on every "clock cycle" to keep multiplexing going.
54
+ void setCathodes (byte decValA, byte decValB);
55
+ void decToBin (bool binVal[], byte i);
56
+
57
+
58
+ // //////// Includes and main code control //////////
59
+ // #include <EEPROM.h>
60
+ // #include <Wire.h>
61
+ // #include <RTClib.h>
62
+ // RTC_DS1307 rtc;
63
+
64
+ void setup (){
65
+ // Serial.begin(57600);
66
+ // Wire.begin();
67
+ // rtc.begin();
68
+ // if(!rtc.isrunning()) rtc.adjust(DateTime(2017,1,1,0,0,0)); //TODO test
69
+ initOutputs ();
70
+ initInputs ();
71
+ // initEEPROM(readInput(mainSel)==LOW);
72
+ // debugEEPROM();
73
+ // setCaches();
74
+ }
75
+
76
+ void loop (){
77
+ // Things done every "clock cycle"
78
+ // checkRTC(); //if clock has ticked, decrement timer if running, and updateDisplay
79
+ checkInputs (); // if inputs have changed, this will do things + updateDisplay as needed
80
+ // doSetHold(); //if inputs have been held, this will do more things + updateDisplay as needed
81
+ cycleDisplay (); // keeps the display hardware multiplexing cycle going
82
+ }
83
+
84
+
85
+ // //////// Control inputs //////////
86
+ void initInputs (){
87
+ // TODO are there no "loose" pins left floating after this? per https://electronics.stackexchange.com/q/37696/151805
88
+ pinMode (A0, INPUT_PULLUP);
89
+ pinMode (A1, INPUT_PULLUP);
90
+ pinMode (A2, INPUT_PULLUP);
91
+ pinMode (A3, INPUT_PULLUP);
92
+ // 4 and 5 used for I2C
93
+ pinMode (A6, INPUT); digitalWrite (A6, HIGH);
94
+ pinMode (A7, INPUT); digitalWrite (A7, HIGH);
95
+ // If using rotary encoders, capture their initial state
96
+ if (mainAdjType==2 ) checkRot (mainAdjA,mainAdjB,mainRotLast,false );
97
+ // if(altAdjType==2) checkRot(altAdjA,altAdjB,altRotLast,false);
98
+ }
99
+
100
+ void checkInputs (){
101
+ // TODO can all this if/else business be defined at load instead of evaluated every sample?
102
+ // if(millis() >= inputSampleLast+inputSampleDur) { //time for a sample
103
+ // inputSampleLast = millis();
104
+ // potential issue: if user only means to rotate or push encoder but does both?
105
+ // checkBtn(mainSel); //main select
106
+ // if(mainAdjType==2)
107
+ checkRot (mainAdjA,mainAdjB,mainRotLast,true ); // main rotary encoder
108
+ // else {
109
+ // checkBtn(mainAdjA); checkBtn(mainAdjB);//main adj buttons
110
+ // }
111
+ // if(altSel!=0) checkBtn(altSel); //alt select (if equipped)
112
+ // if(altAdjType==2) checkRot(altAdj,altRotLast,true); //alt rotary encoder (if equipped)
113
+ // else
114
+ // if(altAdjType==1) { checkBtn(altAdjA); checkBtn(altAdjB); } //alt adj buttons
115
+ // } //end if time for a sample
116
+ }
117
+ bool readInput (byte pin){
118
+ if (pin==A6 || pin==A7) return analogRead (pin)<100 ?0 :1 ; // analog-only pins
119
+ else return digitalRead (pin);
120
+ }
121
+
122
+ word numMoves = 0 ;
123
+ void checkRot (byte rotA, byte rotB, bool last[], bool triggerEvent){
124
+ // Changes in rotary encoders.
125
+ // When an encoder has changed, will call ctrlEvt(ctrl,1)
126
+ // mimicking an up/down adj button press
127
+ // TODO do we need to watch the rotation direction w/ last2[] to accommodate for inputs changing faster than we sample?
128
+ // if(btnCur==0) { //only do if a button isn't pressed
129
+ bool newA = readInput (rotA);
130
+ bool newB = readInput (rotB);
131
+ // if(newA != last[0] && triggerEvent) ctrlEvt(newA==last[1] ? rotA : rotB, 1);
132
+ // If A changed, we'll pretend B didn't - see TODO above
133
+ // else if(newB != last[1] && triggerEvent) ctrlEvt(newB==last[0] ? rotB : rotA, 1);
134
+
135
+ if (newA!=last[0 ] || newB!=last[1 ]) {
136
+ numMoves++;
137
+ // Big tubes: show the number of moves we've had
138
+ editDisplay (numMoves,0 ,3 ,false );
139
+ // Small tubes: show the state of each of the encoder's pins
140
+ editDisplay (newA,4 ,4 ,false );
141
+ editDisplay (newB,5 ,5 ,false );
142
+ }
143
+
144
+ last[0 ] = newA;
145
+ last[1 ] = newB;
146
+ // }//end if button isn't pressed
147
+ }// end checkRot
148
+
149
+
150
+
151
+ // //////// Display data formatting //////////
152
+ // void updateDisplay(){
153
+ // //Run as needed to update display when the value being shown on it has changed
154
+ // //(Ticking time and date are an exception - see checkRTC() )
155
+ // //This formats the new value and puts it in displayNext[] for cycleDisplay() to pick up
156
+ // if(fnSet) { //setting
157
+ // //little tubes:
158
+ // if(fn==255) editDisplay(fnSet, 4, 5, false); //setup menu: current option key
159
+ // else blankDisplay(4, 5); //fn setting: blank
160
+ // //big tubes:
161
+ // if(fnSetValMax==1439) { //value is a time of day
162
+ // editDisplay(fnSetVal/60, 0, 1, EEPROM.read(optsLoc[4])); //hours with leading zero
163
+ // editDisplay(fnSetVal%60, 2, 3, true);
164
+ // } else editDisplay(fnSetVal, 0, 3, false); //some other type of value
165
+ // }
166
+ // else { //fn running
167
+ // switch(fn){
168
+ // //case 0: //time taken care of by checkRTC()
169
+ // //case 1: //date taken care of by checkRTC()
170
+ // case 2: //alarm
171
+ // case 3: //timer
172
+ // break;
173
+ // }
174
+ // }
175
+ // } //end updateDisplay()
176
+
177
+ void editDisplay (word n, byte posStart, byte posEnd, bool leadingZeros){
178
+ // Splits n into digits, sets them into displayNext in places posSt-posEnd (inclusive), with or without leading zeros
179
+ // If there are blank places (on the left of a non-leading-zero number), uses value 15 to blank tube
180
+ // If number has more places than posEnd-posStart, the higher places are truncated off (e.g. 10015 on 4 tubes --> 0015)
181
+ word place;
182
+ for (byte i=0 ; i<=posEnd-posStart; i++){
183
+ // place = int(pow(10,i)); TODO PROBLEM: int(pow(10,2))==99 and int(pow(10,3))==999. Why??????????
184
+ switch (i){
185
+ case 0 : place=1 ; break ;
186
+ case 1 : place=10 ; break ;
187
+ case 2 : place=100 ; break ;
188
+ case 3 : place=1000 ; break ;
189
+ default : break ;
190
+ }
191
+ displayNext[posEnd-i] = (i==0 &&n==0 ? 0 : (n>=place ? (n/place)%10 : (leadingZeros?0 :15 )));
192
+ }
193
+ } // end editDisplay()
194
+ void blankDisplay (byte posStart, byte posEnd){
195
+ for (byte i=posStart; i<=posEnd; i++) displayNext[i]=15 ;
196
+ } // end blankDisplay();
197
+
198
+
199
+ // //////// Hardware outputs //////////
200
+ void initOutputs () {
201
+ for (byte i=0 ; i<4 ; i++) { pinMode (binOutA[i],OUTPUT); pinMode (binOutB[i],OUTPUT); }
202
+ for (byte i=0 ; i<3 ; i++) { pinMode (anodes[i],OUTPUT); }
203
+ pinMode (10 , OUTPUT); // Alarm signal pin
204
+ }
205
+
206
+ void cycleDisplay (){
207
+ bool dim = 0 ;// (opts[2]>0?true:false); //Under normal circumstances, dim constantly if the time is right
208
+ // if(fnSet>0) { //but if we're setting, dim for every other 500ms since we started setting
209
+ // if(setStartLast==0) setStartLast = millis();
210
+ // dim = 1-(((millis()-setStartLast)/500)%2);
211
+ // } else {
212
+ // if(setStartLast>0) setStartLast=0;
213
+ // }
214
+
215
+ // Anode channel 0: tubes #2 (min x10) and #5 (sec x1)
216
+ setCathodes (displayLast[2 ],displayLast[5 ]); // Via d2b decoder chip, set cathodes to old digits
217
+ digitalWrite (anodes[0 ], HIGH); // Turn on tubes
218
+ delay (displayLastFade[0 ]/(dim?4 :1 )); // Display for fade-out cycles
219
+ setCathodes (displayNext[2 ],displayNext[5 ]); // Switch cathodes to new digits
220
+ delay (displayNextFade[0 ]/(dim?4 :1 )); // Display for fade-in cycles
221
+ digitalWrite (anodes[0 ], LOW); // Turn off tubes
222
+
223
+ if (dim) delay (fadeMax/1.5 );
224
+
225
+ // Anode channel 1: tubes #4 (sec x10) and #1 (hour x1)
226
+ setCathodes (displayLast[4 ],displayLast[1 ]);
227
+ digitalWrite (anodes[1 ], HIGH);
228
+ delay (displayLastFade[1 ]/(dim?4 :1 ));
229
+ setCathodes (displayNext[4 ],displayNext[1 ]);
230
+ delay (displayNextFade[1 ]/(dim?4 :1 ));
231
+ digitalWrite (anodes[1 ], LOW);
232
+
233
+ if (dim) delay (fadeMax/1.5 );
234
+
235
+ // Anode channel 2: tubes #0 (hour x10) and #3 (min x1)
236
+ setCathodes (displayLast[0 ],displayLast[3 ]);
237
+ digitalWrite (anodes[2 ], HIGH);
238
+ delay (displayLastFade[2 ]/(dim?4 :1 ));
239
+ setCathodes (displayNext[0 ],displayNext[3 ]);
240
+ delay (displayNextFade[2 ]/(dim?4 :1 ));
241
+ digitalWrite (anodes[2 ], LOW);
242
+
243
+ if (dim) delay (fadeMax*0.75 );
244
+
245
+ // Loop thru and update all the arrays, and fades.
246
+ for ( byte i = 0 ; i < 6 ; i ++ ) {
247
+ if ( displayNext[i] != displayLast[i] ) {
248
+ displayNextFade[i] += fadeStep;
249
+ displayLastFade[i] -= fadeStep;
250
+
251
+ if ( displayNextFade[i] >= fadeMax ){
252
+ displayNextFade[i] = 0 .0f ;
253
+ displayLastFade[i] = fadeMax;
254
+ displayLast[i] = displayNext[i];
255
+ }
256
+ }
257
+ }
258
+ } // end cycleDisplay()
259
+
260
+ void setCathodes (byte decValA, byte decValB){
261
+ bool binVal[4 ]; // 4-bit binary number with values [1,2,4,8]
262
+ decToBin (binVal,decValA); // have binary value of decVal set into binVal
263
+ for (byte i=0 ; i<4 ; i++) digitalWrite (binOutA[i],binVal[i]); // set bin inputs of SN74141
264
+ decToBin (binVal,decValB);
265
+ for (byte i=0 ; i<4 ; i++) digitalWrite (binOutB[i],binVal[i]); // set bin inputs of SN74141
266
+ } // end setCathodes()
267
+
268
+ void decToBin (bool binVal[], byte i){
269
+ // binVal is a reference (modify in place) of a binary number bool[4] with values [1,2,4,8]
270
+ if (i<0 || i>15 ) i=15 ; // default value, turns tubes off
271
+ binVal[3 ] = int (i/8 )%2 ;
272
+ binVal[2 ] = int (i/4 )%2 ;
273
+ binVal[1 ] = int (i/2 )%2 ;
274
+ binVal[0 ] = i%2 ;
275
+ } // end decToBin()
0 commit comments