-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLED_Clock.ino
More file actions
426 lines (363 loc) · 15.2 KB
/
LED_Clock.ino
File metadata and controls
426 lines (363 loc) · 15.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
// Source: https://github.com/redxeth/led_clock
//
// NOTE: If have problems loading onto Teensy-- try higher quality USB cable!!!
// DIGIT 1 DIGIT 2 DIGIT 3 DIGIT 4
// o ---A--- ------- ------- -------
// | | | | | | | |
// F B | | o | | | |
// | | | | | | | |
// o |--G--| |-----| |-----| |-----|
// | | | | o | | | |
// E C | | | | | |
// | | | | | | | |
// ---D--- ------- ------- -------
#define DEBUGME 0 // whether to use serial port for debugging
#define NUM_NEG 3 // number of cathode pins
#define NUM_POS 15 // number of anode pins
#define LOOPDELAY 8000 // time in uS for each cycle, effectively the refresh rate
// Will use 125 fps (8000uS delay) which 'looks' pretty good.
// Making faster is possible but you start to have LEDs not fully turning off between cycles.
// see here some human eye fps info: http://www.100fps.com/how_many_frames_can_humans_see.htm
#define AM 0
#define PM 1
#define FASTFACTOR 1 // set to 1 for 'real time', set to 480 typically for debug (8x per second)
#define CYCLELED 13 // indicate which cycle we are in
#define MODE 0 // mode want to run:
// 0 = clock from starting time
// 1 = second/minute time counter
// pushbuttons
#define HH_BUTTON 22 // Teensy pin connected to HH button
#define MM_BUTTON 21 // Teensy pin connected to MM button
// Button stuff
int HHReading; // a reading off the HH set button (not yet used to set the state)
int MMReading; // a reading off the MM set button (not yet used to set the state)
int HHButtonState; // current reading on HH button
int MMButtonState; // current reading on MM button
int lastHHButtonState = HIGH; // last reading on HH button
int lastMMButtonState = HIGH; // last reading on MM button
long lastHHDebounceTime = 0; // last time HH button was toggled
long lastMMDebounceTime = 0; // last time MM button was toggled
long debounceDelay = 50; // debounce time in mS
//time related stuff
unsigned long startMillis; // 0 to 4,294,967,295 (2^32 - 1). Will count for up to 8 years before a problem...
byte startTimeHH = 00; // can adjust via HH button
byte startTimeMM = 00; // can adjust via MM button
byte startTimeSS = 00;
byte timeHH, timeMM, timeSS, timeAMPM; // current time
byte loopCycle = 0;
// LED pins: 1, 2, 3 <-- pins 1 and 3 get run on odd cycles, pin 2 on even cycles
// cycle: 0, 1, 0 <-- order here is important, alternate even/odd cycles
byte neg[3] = {14,15,16}; // cathode pins on Teensy
// LED pins: 4 5 6 9 10 11 12 13 14 15 16 17 18 19 24
// function: dotPM dotAM B1 E2/C1 G2/B2 D2/C2 F2/A2 A3/F3 B3/G3 C3/D3 E4/E3 G4/B4 D4/C4 F4/A4 colon
// cycle: 0 0 1 0/1 0/1 0/1 0/1 0/1 0/1 0/1 0/1 0/1 0/1 0/1 0
byte pos[15] = {17, 18, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; // anode pins on Teensy
// Each pair below indicates {cycle,anode} for each LED segment
// A B C D E F G
byte HR_10s[2][2] = {{1,12},{1,11}}; // 10s HR
byte HR_1s[7][2] = {{1,8},{1,10}, {1,9},{0,9},{0,11},{0,8},{0,10}}; // 1s HR
byte MIN_10s[7][2] = {{0,7}, {0,6}, {0,5},{1,5}, {1,4},{1,7}, {1,6}}; // 10s MIN
byte MIN_1s[7][2] = {{1,1}, {1,3}, {1,2},{0,2}, {0,4},{0,1}, {0,3}}; // 1s MIN
byte colon[2] = {0, 0}; // colon
byte dotPM[2] = {0,18}; // upper dot
byte dotAM[2] = {0,17}; // lower dot
// Lookup table to help with forming numbers
// LED segments A B C D E F G
byte digits[10][7] = {{1, 1, 1, 1, 1, 1, 0}, // 0
{0, 1, 1, 0, 0, 0, 0}, // 1
{1, 1, 0, 1, 1, 0, 1}, // 2
{1, 1, 1, 1, 0, 0, 1}, // 3
{0, 1, 1, 0, 0, 1, 1}, // 4
{1, 0, 1, 1, 0, 1, 1}, // 5
{1, 0, 1, 1, 1, 1, 1}, // 6
{1, 1, 1, 0, 0, 0, 0}, // 7
{1, 1, 1, 1, 1, 1, 1}, // 8
{1, 1, 1, 0, 0, 1, 1}}; // 9
// extra in case want to display letters
byte letters[19][7] {{1, 1, 1, 0, 1, 1, 1}, // A
{0, 0, 1, 1, 1, 1, 1}, // b
{1, 0, 0, 1, 1, 1, 0}, // C
{0, 1, 1, 1, 1, 0, 1}, // d
{1, 0, 0, 1, 1, 1, 1}, // E
{1, 0, 0, 0, 1, 1, 1}, // F
{1, 0, 1, 1, 1, 1, 1}, // G
{0, 1, 1, 0, 1, 1, 1}, // H
{0, 1, 1, 0, 0, 0, 0}, // I
{0, 1, 1, 1, 1, 0, 0}, // J
{0, 0, 0, 1, 1, 1, 0}, // L
{0, 0, 1, 0, 1, 0, 1}, // n
{0, 0, 1, 1, 1, 0, 1}, // o
{1, 1, 1, 1, 1, 1, 0}, // O
{1, 1, 0, 0, 1, 1, 1}, // P
{1, 0, 1, 1, 0, 1, 1}, // S
{0, 1, 1, 1, 1, 1, 0}, // U
{0, 1, 1, 1, 0, 1, 1}, // Y
{1, 1, 0, 1, 1, 0, 1}}; // Z
// displayDigit
// display a value to the display
void displayDigit(byte digit, byte value, byte cycle) {
switch(digit) {
case 1:
// HOUR 10s DIGIT
// set all segments in digit on or off depending on if cycle matches AND digit "1" is needed
// if cycle doesn't match, do nothing as anode may be used for a different thing
for (int i=0; i < 2; i++) {
if (HR_10s[i][0] == cycle) { // only change anything if in this cycle
if (value == 1) // if 1 digit needed, turn on segment
digitalWrite(HR_10s[i][1],HIGH);
else // if 1 digit not needed, turn off segment
digitalWrite(HR_10s[i][1],LOW);
}
}
break;
case 2:
// HOUR 1s DIGIT
for (int i=0; i < 7; i++) {
if (HR_1s[i][0] == cycle) { // only change anything if in this cycle
if (digits[value][i] == 1) // if needed, turn on segment
digitalWrite(HR_1s[i][1],HIGH);
else // if not needed, turn off segment
digitalWrite(HR_1s[i][1],LOW);
}
}
break;
case 3:
// MINUTES 10s DIGIT
for (int i=0; i < 7; i++) {
if (MIN_10s[i][0] == cycle) { // only change anything if in this cycle
if (digits[value][i] == 1) // if needed, turn on segment
digitalWrite(MIN_10s[i][1],HIGH);
else // if not needed, turn off segment
digitalWrite(MIN_10s[i][1],LOW);
}
}
break;
case 4:
for (int i=0; i < 7; i++) {
if (MIN_1s[i][0] == cycle) { // only change anything if in this cycle
if (digits[value][i] == 1) // if needed, turn on segment
digitalWrite(MIN_1s[i][1],HIGH);
else // if not needed, turn off segment
digitalWrite(MIN_1s[i][1],LOW);
}
}
break;
}
}
// Write out time in 2 cycles--
// one cycle per thing that needs to be turned on
// instead of doing it by digit. Determine which
// segments are active based on digits that need to
// be displayed, but generally be cycling through the
// digits no matter what
//
// HH : hours in 24 hour format (0-23)
// MM : minutes (0-59)
// cycle: 0 (even cycle)
// 1 (odd cycle)
//
void displayCurrentTime(byte HH, byte MM, byte cycle) {
char buffer [80];
int n;
// HH comes in 24 hour mode, i.e. 0-23:
// 24 HR => 12 HR
// 0 12am
// 1-9 1-9am
// 10-11 10-11am
// 12 12pm
// 13-21 1-9pm
// 22-23 10-11pm
byte hh12 = (HH % 12); // convert 24hr to 12hr format: 0-11
if (hh12 == 0) // convert 0 to 12
hh12 = 12;
byte HR10s_dig = (hh12 / 10); // 0 or 1
byte HR1s_dig = (hh12 % 10); // 0 - 9
byte MM10s_dig = (MM / 10);
byte MM1s_dig = (MM % 10);
boolean AMPM = (HH > 11); // true if PM
if (DEBUGME == 1) {
n = sprintf (buffer, "Cycle: %i, HH: %2i, MM: %2i\r\n", cycle, HH, MM);
Serial.println (buffer);
n = sprintf( buffer, " HR10s_dig: %i, HR1s_dig: %i, hh12: %i\r\n", HR10s_dig, HR1s_dig, hh12);
Serial.println (buffer);
}
// Display time digits
displayDigit(1, HR10s_dig, cycle);
displayDigit(2, HR1s_dig, cycle);
displayDigit(3, MM10s_dig, cycle);
displayDigit(4, MM1s_dig, cycle);
// display colon
displayColon(cycle);
// display AM or PM
displayAMPM(AMPM, cycle);
}
// updates global variables timeHH and timeMM with current time
// calculated from device boot relative to starting time above
void measureCurrentHHMM() {
unsigned long currentMillis = millis();
unsigned long secElapsed = ((currentMillis - startMillis) * FASTFACTOR / 1000); // figure time in seconds elapsed since program start
unsigned long minOfDay = (((secElapsed / 60) + (startTimeHH*60) + startTimeMM) % 1440); // figure the time of day in minutes (0 to 1439)
// calc current minutes
timeMM = (minOfDay % 60); // 0 - 59
// calc current hour
// use 24-hour time, convert when displaying back to 12-hour w/ AM/PM set
timeHH = (minOfDay / 60); // 0 - 23, no need to do mod 24 since minOfDay limited via mod 1440 already.
}
void displayColon(byte cycle) {
// display colon
if (colon[0] == cycle)
digitalWrite(colon[1],HIGH);
}
void displayAMPM(byte AMPM, byte cycle) {
// display AM or PM
if (dotPM[0] == cycle) {
if (AMPM) // PM
digitalWrite(dotPM[1],HIGH);
else // AM
digitalWrite(dotPM[1],LOW);
}
if (dotAM[0] == cycle) {
if (!AMPM) // AM
digitalWrite(dotAM[1],HIGH);
else // PM
digitalWrite(dotAM[1],LOW);
}
}
void displayElapsedMMSS(byte cycle) {
unsigned long currentMillis = millis();
unsigned long secElapsed = ((currentMillis - startMillis) * FASTFACTOR / 1000); //figure time in seconds elapsed since program start
unsigned long minElapsed = (secElapsed / 60); // note that remainder seconds are truncated
byte dig1 = ((minElapsed % 20) / 10); // 0 or 1
byte dig2 = ((minElapsed % 20) % 10); // 0 - 9
byte dig3 = ((secElapsed % 60) / 10);
byte dig4 = ((secElapsed % 60) % 10);
// Display time digits
displayDigit(1, dig1, cycle);
displayDigit(2, dig2, cycle);
displayDigit(3, dig3, cycle);
displayDigit(4, dig4, cycle);
displayColon(cycle);
}
// handle reading the button, debounce
// causes modeState to change
void buttonStuff() {
// read current state of pushbuttons
HHReading = digitalRead(HH_BUTTON);
MMReading = digitalRead(MM_BUTTON);
// If the switch changed, due to noise or pressing:
if (HHReading != lastHHButtonState) {
// reset the debouncing timer
lastHHDebounceTime = millis();
}
// If the switch changed, due to noise or pressing:
if (MMReading != lastMMButtonState) {
// reset the debouncing timer
lastMMDebounceTime = millis();
}
if ((millis() - lastHHDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state:
// if the button state has changed:
if (HHReading != HHButtonState) {
// then change current buttonState
HHButtonState = HHReading;
// only change HH if the new button state is LOW
if (HHButtonState == LOW) {
startTimeHH += 1;
}
}
}
if ((millis() - lastMMDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state:
// if the button state has changed:
if (MMReading != MMButtonState) {
// then change current buttonState
MMButtonState = MMReading;
// only change MM if the new button state is LOW
if (MMButtonState == LOW) {
startTimeMM += 1;
}
}
}
// save the reading. Next time through the loop,
// it'll be the lastHHButtonState:
lastHHButtonState = HHReading;
// save the reading. Next time through the loop,
// it'll be the lastMMButtonState:
lastMMButtonState = MMReading;
}
// initial setup for inputs/outputs
void setup() {
startMillis = millis();
// startTimeHH = hour();
// startTimeMM = minutes();
if (DEBUGME == 1) {
Serial.begin(9600);
}
pinMode(HH_BUTTON, INPUT);
pinMode(MM_BUTTON, INPUT);
// set outputs and turn all off
// cathode pins
for (int i=0; i < NUM_NEG; i++) {
pinMode(neg[i], OUTPUT);
digitalWrite(neg[i],HIGH);
}
// anode pins
for (int i=0; i < NUM_POS; i++) {
pinMode(pos[i], OUTPUT);
digitalWrite(pos[i],LOW);
}
// mode LED
pinMode(CYCLELED, OUTPUT);
}
// the loop routine runs over and over again forever
// basically like main with while(1).
//
// DH TBD
// add functionality to sync the time with PC when program is started!
// maybe bluetooth??
// add alarm
// add countdown feature
//
void loop() {
// read buttons, adjust time if necessary
buttonStuff();
// each loop through we alternate 'cycle' which is
// how we drive the 4-digit 7-segment LED display
// cathode pins - enable or disable based on even or odd cycle
for (int i=0; i < NUM_NEG; i++) {
if ( i % 2 == loopCycle ) { // turn on those matching cycle
digitalWrite(neg[i],LOW);
} else { // turn off those not matching cycle
digitalWrite(neg[i],HIGH);
}
}
// now display what we want
switch (MODE) {
case 0: // DISPLAY CURRENT TIME
// update the current time values for timeHH, timeMM
measureCurrentHHMM();
// display time based on which cycle, odd or even, we are in
displayCurrentTime(timeHH,timeMM,loopCycle);
break;
case 1:
displayElapsedMMSS(loopCycle);
break;
}
// determine cycle for next iteration of loop()
if (loopCycle == 1) {
if (DEBUGME == 1)
digitalWrite(CYCLELED,HIGH);
loopCycle = 0;
} else {
if (DEBUGME == 1)
digitalWrite(CYCLELED,LOW);
loopCycle = 1;
}
// optional delay for display purposes
if (DEBUGME == 1)
delay(1000);
else
delayMicroseconds(LOOPDELAY);
}