/*
2 ONE-TUBE NIXIE CLOCK for Arduino UNO - - - - - - - - - - - - L. Thomas -- August 2023
3 - Displays the time by repeatedly sequencing the digits each 2.5 seconds.
4 - Uses interrupt (pin 2) to detect each cycle of 60Hz AC power. This is used to
5 count seconds, and time the sequencing of each digit.
6 - Has leading zero blanking.
7 - Jumper (pin 6) can be cut to run the clock in 24-hour format.
8 - A short blink between digits separates them. This is needed for the eye to catch
9 double digits such as 11 or 22.
10 - Nixie digits 0-9 are displayed according to the hex value of pins 8-11.
11 - A hexadecimal value greater than 9 will cause none of the nixie digits to illuminate.
12 - Output pin 12 is used to turn the decimal point ON and OFF.
13 - The decimal point is turned on during display of hours digit(s).
14 - The clock is frozen at zero seconds when the RUN/SET switch is in SET position. This
15 allows the clock to start immediately at zero seconds when switched from SET to RUN.
16 - At start-up or reset, all ten digits are displayed & decimal point blinks as a test.
17 - At start-up or reset, the clock begins at 12:59 (requires setting to correct time).
18
19*/
20int ACint = 2; // pin 2 is the interrupt from the 60Hz AC line (INT0)
21int HRSBTN = 3; // pin 3 senses press of the Hours button (adds 1)
22int MINBTN = 4; // pin 4 senses press of the Minutes button (adds 1)
23int RUNSET = 5; // pin 5 senses if in the Set mode (low)
24int JUMPER = 6; // pin 6 senses if time should be displayed in 24-hour format (HIGH)
25int hexA = 8; // pin 8 is the LSD - used to display one of the nixie digits
26int hexB = 9; // pin 9 - used to display one of the nixie digits
27int hexC = 10; // pin 10 - used to display one of the nixie digits
28int hexD = 11; // pin 11 is the MSD - used to display one of the nixie digits
29int Dpoint = 12; // pin 12 turns the nixie decimal point on and off
30int TestPt = 13; // pin 13 has an on-board LED and is used as a visual test indicator
31//
32int timeH = 12; // Stores the HOURS value
33int timeM = 59; // Stores the MINUTES value
34int SUBsec = 0; // Accumulates AC cycles for each second
35int Seconds = 0; // Accumulate seconds up to 60
36int Dcounter = 0; // Runs from 0 to 149 to organize nixie display sequence timing
37int Flag = 0; // Set to 1 when interrupt occurs
38int Temp1 = 0; // Temporary register 1
39int Temp2 = 0; // Temporary register 2
40int INstatus = LOW; // Used to capture status of a digital input - HIGH or LOW
41int LASTs3 = HIGH; // Used to debounce Hours setting button
42int LASTs2 = HIGH; // Used to debounce Minutes setting button
43int Dstatus = 5; // Defines the current status (case number) for nixie display
44//
45// The setup routine runs once during start-up or when RESET is pressed.
46void setup()
47{ pinMode(HRSBTN, INPUT); // 3
48 pinMode(MINBTN, INPUT); // 4
49 pinMode(RUNSET, INPUT); // 5
50 pinMode(JUMPER, INPUT); // 6
51 pinMode(hexA, OUTPUT); // 8
52 pinMode(hexB, OUTPUT); // 9
53 pinMode(hexC, OUTPUT); // 10
54 pinMode(hexD, OUTPUT); // 11
55 pinMode(Dpoint, OUTPUT); // 12
56 pinMode(TestPt, OUTPUT); // 13 (LED)
57// **** Display all ten digits and flash the decimal point ****
58 digitalWrite(hexA, LOW);
59 digitalWrite(hexB, LOW);
60 digitalWrite(hexC, LOW);
61 digitalWrite(hexD, LOW); // This will display "0"
62 digitalWrite(Dpoint, HIGH); // Decimal point ON
63 delay(400); // Show each digit for 800ms
64 digitalWrite(Dpoint, LOW); //Decimal point OFF
65 delay(400);
66 digitalWrite(hexA, HIGH); //Display "1"
67 digitalWrite(Dpoint, HIGH); //Decimal point ON
68 delay(400);
69 digitalWrite(Dpoint, LOW); //Decimal point OFF
70 delay(400);
71 digitalWrite(hexA, LOW);
72 digitalWrite(hexB, HIGH); //Display "2"
73 digitalWrite(Dpoint, HIGH); //Decimal point ON
74 delay(400);
75 digitalWrite(Dpoint, LOW); //Decimal point OFF
76 delay(400);
77 digitalWrite(hexA, HIGH);
78 digitalWrite(hexB, HIGH); //Display "3"
79 digitalWrite(Dpoint, HIGH); //Decimal point ON
80 delay(400);
81 digitalWrite(Dpoint, LOW); //Decimal point OFF
82 delay(400);
83 digitalWrite(hexA, LOW);
84 digitalWrite(hexB, LOW);
85 digitalWrite(hexC, HIGH); //Display "4"
86 digitalWrite(Dpoint, HIGH); //Decimal point ON
87 delay(400);
88 digitalWrite(Dpoint, LOW); //Decimal point OFF
89 delay(400);
90 digitalWrite(hexA, HIGH); //Display "5"
91 digitalWrite(Dpoint, HIGH); //Decimal point ON
92 delay(400);
93 digitalWrite(Dpoint, LOW); //Decimal point OFF
94 delay(400);
95 digitalWrite(hexA, LOW);
96 digitalWrite(hexB, HIGH); //Display "6"
97 digitalWrite(Dpoint, HIGH); //Decimal point ON
98 delay(400);
99 digitalWrite(Dpoint, LOW); //Decimal point OFF
100 delay(400);
101 digitalWrite(hexA, HIGH);
102 digitalWrite(hexB, HIGH); //Display "7"
103 digitalWrite(Dpoint, HIGH); //Decimal point ON
104 delay(400);
105 digitalWrite(Dpoint, LOW); //Decimal point OFF
106 delay(400);
107 digitalWrite(hexA, LOW);
108 digitalWrite(hexB, LOW);
109 digitalWrite(hexC, LOW);
110 digitalWrite(hexD, HIGH); //Display "8"
111 digitalWrite(Dpoint, HIGH); //Decimal point ON
112 delay(400);
113 digitalWrite(Dpoint, LOW); //Decimal point OFF
114 delay(400);
115 digitalWrite(hexA, HIGH); //Display "9"
116 digitalWrite(Dpoint, HIGH); //Decimal point ON
117 delay(400);
118 digitalWrite(Dpoint, LOW); //Decimal point OFF
119 delay(400);
120 digitalWrite(hexB, HIGH); //All nixie digits now OFF
121 delay(800);
122 attachInterrupt(0, ClkUpdate, RISING); //Enable the interrupt.
123}
124//
125// **** MAIN LOOP ***************************************************************
126void loop ()
127{
128 INstatus = digitalRead(RUNSET); //Is the clock being set?
129 if (Seconds >= 60 && Flag == 1 && INstatus == HIGH)
130// Just update the display if not true.
131 {
132 Seconds = 0; //ELSE Seconds = 60 so adjust for a new minute
133 timeM = timeM + 1; //Add a minute to the time
134 if (timeM >= 60) //Do we also have a new hour?
135 {
136 timeM = 0; //Yes-Reset the minutes
137 timeH = timeH + 1; // and add an hour
138 INstatus = digitalRead(JUMPER); //Read the jumper
139 if (INstatus == LOW && timeH > 12) //Back to 1:00 (if 12-hour format)
140 timeH = 1; //Yes-reset to 1:00
141 if (timeH > 23) //Reset to 0:00 (if 24-hour format)
142 timeH = 0; //Yes-reset to 0:00
143 }}
144// Now update the display according to Case 1 through Case 5 ********************
145 if (Flag == 1) //Recent interrupt?
146// If not, nothing else, done with loop.
147 {
148 Flag = 0; //Yes-so reset Flag and update display
149 Dstatus = 5; //Start with nixie off
150 if (Dcounter < 96) //If true display Minutes-ones
151 Dstatus = 4; //Case 4
152 if (Dcounter < 77) //If true nixie off
153 Dstatus = 5; //Case 5
154 if (Dcounter < 74) //If true display Minutes-tens
155 Dstatus = 3; //Case 3
156 if (Dcounter < 55) //If true nixie off
157 Dstatus = 5; //Case 5
158 if (Dcounter < 41) //If true display Hours-ones + DP
159 Dstatus = 2; //Case 2
160 if (Dcounter < 22) //If true nixie off
161 Dstatus = 5; //Case 5
162 if (Dcounter < 19) //If true display Hours-tens + DP
163 Dstatus = 1; //Case 1
164//
165 switch (Dstatus)
166 {
167 case 1: //Displays Hours-tens digit
168 Temp1 = timeH % 10; //Temp1 now has the remainder (0,1,or 2)
169 Temp2 = (timeH - Temp1)/10; //Temp2 now has the Hours-tens value
170 if (Temp2 == 0) //Check for leading zero blanking
171 Temp2 = 10; //Yes-Set Temp2 to blank leading zero
172 break;
173 case 2: //Displays Hours-ones digit
174 Temp2 = timeH % 10; //Temp2 now has the Hours-one value
175 break;
176 case 3: //Displays Minutes-tens digit
177 Temp1 = timeM % 10; //Temp1 now has the remainder (0-9)
178 Temp2 = (timeM - Temp1)/10; //Temp2 now has the Minutes-tens value
179 break;
180 case 4: //Displays Minutes-ones digit
181 Temp2 = timeM % 10; //Temp2 now has the Minutes-one value
182 break;
183 case 5: //Turns the nixie off (including DP)
184 Temp2 = 10;
185 break;
186 }
187//
188 if (Dstatus == 1 || Dstatus == 2) //Should the DP be turned on?
189 {
190 digitalWrite(Dpoint, HIGH); //ON - Yes-Dstatus was a 1 or 2
191 }
192 else
193 {
194 digitalWrite(Dpoint, LOW); //OFF - No-Dstatus was not a 1 or 2
195 }
196//
197// **** Set outputs 8,9,10,11 & 12 according to Temp2 ***************************
198//
199 if (Temp2 > 7 && Temp2 < 10) //Is it 8 or 9?
200 {
201 digitalWrite(hexD, HIGH); //Yes-Turn on the "8" bit
202 Temp2 = (Temp2 - 8); //prepare for next test
203 }
204 else
205 {
206 digitalWrite(hexD, LOW); //No -Turn off the "8" bit
207 }
208 if (Temp2 > 3 && Temp2 < 8) //Is it 4,5,6, or 7?
209 {
210 digitalWrite(hexC, HIGH); //Yes-Turn on the "4" bit
211 Temp2 = (Temp2 - 4); //prepare for next test
212 }
213 else
214 {
215 digitalWrite(hexC, LOW); //No -Turn off the "4" bit
216 }
217 if (Temp2 > 1 && Temp2 < 4) //Is it 2 or 3?
218 {
219 digitalWrite(hexB, HIGH); //Yes-Turn on the "2" bit
220 Temp2 = (Temp2 - 2); //prepare for next test
221 }
222 else
223 {
224 digitalWrite(hexB, LOW); //No -Turn off the "2" bit
225 }
226 if (Temp2 == 1) //Is it now 1?
227 {
228 digitalWrite(hexA, HIGH); //Yes-Turn on the "1" bit
229 }
230 else
231 {
232 digitalWrite(hexA, LOW); //No -Turn off the "1" bit
233 }
234 if (Temp2 == 10) //Turn the nixie off and DP off
235 {
236 digitalWrite(hexA, LOW); //The nixie will be off if output value
237 digitalWrite(hexB, HIGH); // is 10 or greater
238 digitalWrite(hexC, LOW);
239 digitalWrite(hexD, HIGH); //Output value is now 10 (decimal)
240 digitalWrite(Dpoint, LOW); //The decimal point is now off
241 // De-bounce the Minutes and Hours setting buttons
242 INstatus = digitalRead(MINBTN); //Read the Minutes button status
243 if (INstatus != LASTs2) //Determine if status has changed
244 LASTs2 = HIGH; //NOT equal- it did change
245 INstatus = digitalRead(HRSBTN); //Now read the Hours button status
246 if (INstatus != LASTs3) //Determine if status has changed
247 LASTs3 = HIGH; //NOT equal- it did change
248 }}} //End of Loop here *************************
249//
250//
251//
252// Interrupt Service - Accumulates Seconds and Detects Clock Setting ************
253 void ClkUpdate (void)
254 {
255 SUBsec = SUBsec + 1; //Add 1 towards accumulating 1 second
256 INstatus = digitalRead(RUNSET); //Read the RUN/SET switch status
257 if (INstatus == LOW) //LOW if switch is in SET positon
258 {
259 SUBsec = 0; //RUN/SET switch is in the SET position
260 Seconds = 0; //This freezes the clock at zero seconds
261 // Now check the clock setting buttons:
262 INstatus = digitalRead(MINBTN); //Read the MINUTES button status
263 if (INstatus == LOW && INstatus != LASTs2) //Must be a NEW low status
264 {
265 timeM = timeM + 1; // Button was pressed - add a minute
266 LASTs2 = INstatus; // Save the button status to LASTs2
267 if (timeM > 59) // Roll over back to zero?
268 timeM = 0; // Yes
269 }
270 INstatus = digitalRead(HRSBTN); //Read the HOURS button status
271 if (INstatus == LOW && INstatus != LASTs3) //Must be a NEW low status
272 {
273 timeH = timeH + 1; // Button was pressed - add an hour
274 LASTs3 = INstatus; // Save the button status to LASTs3
275 INstatus = digitalRead(JUMPER); //Read the jumper
276 if (INstatus == LOW && timeH > 12) //Back to 1:00 (if 12-hour format)
277 timeH = 1; //Yes-reset to 1:00
278 if (timeH > 23) //Reset to 0:00 (if 24-hour format)
279 timeH = 0; //Yes-reset to 0:00
280 }}
281 if (SUBsec == 5) //Skips to here if not setting the clock
282 digitalWrite(TestPt, LOW); //Turn off the test LED (short blink)
283 if (SUBsec > 59) //Have we accumulated 1 second yet?
284 {
285 SUBsec = 0; //Yes-so reset for the next one
286 Seconds = Seconds + 1; // and increment the seconds
287 digitalWrite(TestPt, HIGH); // and turn on the test point & LED
288 }
289 Dcounter = Dcounter + 1; //Also increment the display counter
290 if (Dcounter > 149) //At the end of the display cycle?
291 Dcounter = 0; //Yes- so reset for next cycle
292 Flag = 1; //Now show that we have been here
293 }
294// End of Interrupt Service: ****************************************************