-
Notifications
You must be signed in to change notification settings - Fork 1
/
LED_Clock_v2.ino
3270 lines (2545 loc) · 92 KB
/
LED_Clock_v2.ino
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
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// April 2017 v2
// Using BMP180 sensor - easily modifiable.
// On Slide Mode, alternate Temp / Date
// Removed PONG - more tidy up to be done
// Seems to only compile to Arduino MEGA, need to shorten to fit UNO
//
// Updates will be made available on my Github
// Thanks to Nick Hall and Brett Oliver for all the really Hard Work!
/***********************************************************************
Original Pong Clock v 5.1, Oct 2013 by Nick Hall
Distributed under the terms of the GPL.
See Nick's blog:
http://123led.wordpress.com/
############################################################################
******************Works with IDE 1.6.5 *************************
############################################################################
Code modded by Brett Oliver from V5.3
http://home.btconnect.com/brettoliver1/Pong_Clock/Pong_Clock.htm
Added the following
PIR motion detected Display enable
Sync from master clock every 30 seconds
Quick brightness control added
DS3231 added (no change to code required)
1 seconds, 30 second and sync LEDs added
Display mode order re-arranged with my favorite "Slide" in position 1
Countdown timer with alarm added on Normal Mode
Randon mode changed to dual mode, Slide & Pong switching on preset minutes/seconds
Added Adafruit MPC9808 temperature Sensor displayed in Celsius on Slide mode
Removed the following
1 display mode (jumbled character effect)
Release Notes for v7.5 Kitchen clock with timer
Fix bug where timer set before and ending after midnight fails
Added quck brightness call to set_brightness() on press of button Disp Adj on Pong, Slide and Timer mode only
v7.4 test version for 7.5 has code to set RTC for 23:57:00 for testing timer past midnight + many print statements
Release Notes for v7.3 Kitchen clock with timer
Addded temperature display on slide mode
Added Adafruit MPC9808 temperature Sensor and library
Timer function added to normal mode
Added buttons for timer and cntrol buttons for sond module
Release Notes for v7.2 Random mode changed to only use Slide and Pong. Now preset every 8th minute 50 secs Slide until the next 5th minute 10 secs Pong
Slide will then start again at the next 8th minute 50 secs etc etc
Release Notes for v7.1 Slide mode when 1st digit of mins = 9,0,1,2 or 3 Pong mode when 4,5,6,7 or 8 mins VOID
Release Notes for v7.0 set slide & pong as only modes in random mode change every 5 mins VOID
Release Notes for v6.9 serial print of time re enabled
Release Notes for V6.8
Pong clock with rotaion every other minute added
Release Notes for V6.7
Final code tidy up
Release Notes for V6.6
Void
Release Notes for V6.5
Remote Btn 4 directly controls brightness without using setup menu
Brightness adjustable directly over 5 steps
Release Notes for V6.4
6.3 VOID
sync30sec was pin 7 now A0
pin 7 remote BTN 4 connected but not used
increased key reapeat delay when setting clock to allow for remote control input
Release Notes for V6.2
LEDsyncenable replaced with 1 sec LED PIR control of status LEDs
Release Notes for V6.1
30 sync LED held on for 1 sec @ 30secs
Release Notes for V6.0
Sync mods
Release Notes for V5.9
Sync and sync'd LEDs added
Release Notes for V5.8
Button library used for all switches
Release Notes for V5.7
Jumble mode removed to save memory
Slide set as default
Release Notes for V5.6
Man sync button added
Release Notes for V5.5
PIR detector added to blank LEDs when no movement detected
Release Notes for V5.4
Sync pulse now does not sync if RTC already on 30 seconds
Release Notes for V5.3
My Name added to version number to avoid confusion with original clock
DS3231 added (no code changes)
30 second synchronization added to keep RTC in sync to Master Clock
###################################################################################################
Release Notes for V5.1.
*Fixed a bug in pong mode where the bat missed ball and it wasn't on the minute
*Fixed a bug in normal and slide mode where the date tens digit wasn't cleared when some months changed
*Fixed a bug where the display was corrupted in digits mode and also in 12hr mode
*Addded lower case font. A tweaked version of the one courtesy of Richard Shipman
*Added full LED test on startup.
*Time is printed to serial port for testing
*Menu and set clock items reordered.
Release Notes for V5.0.
*Tested to be compatible with Arduino IDE 1.6.5
*Normal mode now has seconds
*New slide effect mode
*Separate setup menu
*New pong “Ready?” message
*Daylight Savings mode (DST ADJ) adds / removes 1 hr
*Time now set on upload
*Various other display and code tweaks. Now using the RTClib from Adafruit
Thanks to all who contributed to this including:
SuperTech-IT over at Instructibles, Kirby Heintzelman, Alexandre Suter, Richard Shipman.
Uses 2x Sure 2416 LED modules, arduino and DS1307 clock chip.
Distributed under the terms of the GPL.
Holtek HT1632 LED driver chip code:
As implemented on the Sure Electronics DE-DP016 display board (16*24 dot matrix LED module.)
Nov, 2008 by Bill Westfield ("WestfW")
Copyrighted and distributed under the terms of the Berkely license (copy freely, but include this notice of original author.)
***********************************************************************/
//include libraries
#include <SFE_BMP180.h>
SFE_BMP180 tempsensor;
#define ALTITUDE 1655.0 // Altitude of SparkFun's HQ in Boulder, CO. in meters
//#include <Adafruit_MCP9808.h>
// Create the MCP9808 temperature sensor object
//Adafruit_MCP9808 tempsensor = Adafruit_MCP9808();
#include <ht1632c.h> // Holtek LED driver by WestFW - updated to HT1632C by Nick Hall
#include <avr/pgmspace.h> // Enable data to be stored in Flash Mem as well as SRAM
#include <Font.h> // Font library
#include <Wire.h> // DS1307 clock
#include "RTClib.h" // DS1307 clock
#include <Button.h> // Button library by Alexander Brevig
//define constants
#define ASSERT(condition) // Nothing
#define X_MAX 47 // Matrix X max LED coordinate (for 2 displays placed next to each other)
#define Y_MAX 15 // Matrix Y max LED coordinate (for 2 displays placed next to each other)
#define NUM_DISPLAYS 2 // Num displays for shadow ram data allocation
#define FADEDELAY 30 // Time to fade display to black
#define NUM_MODES 9 // Number of clock & display modes (conting zero as the first mode)
#define NUM_SETTINGS_MODES 5 // Number settings modes = 6 (conting zero as the first mode)
#define NUM_DISPLAY_MODES 4 // Number display modes = 5 (conting zero as the first mode) Brett changed to 4
#define plot(x,y,v) ht1632_plot(x,y,v) // Plot LED
#define cls ht1632_clear // Clear display
#define SLIDE_DELAY 20 // The time in milliseconds for the slide effect per character in slide mode. Make this higher for a slower effect
#define NEXT_DATE_MIN 10 // After the date has been displayed automatically, the next time it's displayed is at least 10 mintues later
#define NEXT_DATE_MAX 15 // After the date has been displayed automatically, the next time it's displayed is at most 15 mintues later
//global variables
static const byte ht1632_data = 10; // Data pin for sure module
static const byte ht1632_wrclk = 11; // Write clock pin for sure module
static const byte ht1632_cs[2] = {
4,5}; // Chip_selects one for each sure module. Remember to set the DIP switches on the modules too.
//Temp and Digits - moved from local in function - may not be so efficient...
static byte digits_x_pos[6] = {
42,36,24,18,6,0 }; //x pos for which to draw each digit at - last 2 are bottom line
//Bottom Row Temperature
static char celsius_old[6] = {
0,0,0,0,0,0}; //old values we store time in. Set to somthing that will never match the time initially so all digits get drawn wnen the mode starts
static char celsiusStr[5]; //new digits time will slide to reveal
static byte celsius_x_pos[6] = {6,12,20,24,30}; //x pos for which to draw each digit at - last 2 are bottom line
char old_char[2]; //used when we use itoa to transpose the current digit (type byte) into a char to pass to the animation function
char new_char[2]; //used when we use itoa to transpose the new digit (type byte) into a char to pass to the animation function
static byte digits_old[6] = {
99,99,99,99,99,99 }; //old values we store time in. Set to somthing that will never match the time initially so all digits get drawn wnen the mode starts
static byte digits_new[6]; //new digits time will slide to reveal
Button buttonA = Button(2,BUTTON_PULLUP); // Setup button A (using button library)
Button buttonB = Button(3,BUTTON_PULLUP); // Setup button B (using button library)
Button buttonSync = Button(6,BUTTON_PULLUP); // Setup button Sync (using button library)
Button buttonbrightness = Button(7,BUTTON_PULLUP);// Setup button D brightness quick adj (using button library)
Button buttonPIR = Button(8,BUTTON_PULLUP); // Setup PIR switch (using button library)
Button buttonStTmr = Button(15,BUTTON_PULLUP); // Setup Start timer button (using button library)
Button buttonSetTime = Button(16,BUTTON_PULLUP); // Setup Set time button (using button library)
int sync30sec = A0 ; // 30 second sync pulse input
int sync30secval = 0;
int syncset = 1; // set to 1 on 0 seconds to enable 30 sec sync pulse
int PIR = 8; // PIR input
int LED30sec = 9; // 30 second pulse monitor LED
int LEDsync = 12; // LED lights when clock is sync'd
int minutes1 = 0; // gets 1st mins char from mins
int minutes2 = 0; // gets 2nd mins char from mins
int start = 0; //time timer is started
//int stop = 0; // time timer to stop
//int t3[3]; //time to stop timer
int rmn3[2]; // time remaining
int timerEnable = 0; // enables timer buttonStTmr if 0
int timerSetEnable = 0; // enables timer buttonSetTime if 0
int stopTime = 0; // this is the time the timer will run out
int timeNow = 0; // current time to be taken off stopTime to get timeRemain
int timeRemain = 0; // this is the time remaining on the timer
int tmrStep = 30; // amount of seconds timer steps by when buttonSetTime is pressed
int timerValue = 210; // this is the time set on the timer when buttonStTmr value incremented by buttonSetTime
// timer deafults to 210 or 4mins (30 will be added on 1st press)
int tmrOutput = 17; // (A3) output when timer reaches 0
int timerVal = 0; // used to convert timerValue to show on display when timer setting
float c = 0; // temp in C from Adafruit_MCP9808
char status;
double T,P,p0,a;
RTC_DS1307 ds1307; //RTC object
int rtc[7]; // Holds real time clock output
byte clock_mode = 0; // Default clock mode. Default = 0 (slide)
byte old_mode = clock_mode; // Stores the previous clock mode, so if we go to date or whatever, we know what mode to go back to after.
bool ampm = 0; // Define 12 or 24 hour time. 0 = 24 hour. 1 = 12 hour
bool random_mode = 0; // Define random mode - changes the display type every few hours. Default = 0 (off)
bool random_modestore = 0; // stores value of Random mode when using timer
bool daylight_mode = 0; // Define if DST is on or off. Default = 0 (off).
byte change_mode_time = 0; // Holds hour when clock mode will next change if in random mode.
byte brightness = 7; // Screen brightness - default is 7 which half.
byte next_display_date; // Holds the minute at which the date is automatically next displayed
char days[7][4] = {
"Sun","Mon","Tue", "Wed", "Thu", "Fri", "Sat"}; //day array - used in slide, normal and jumble modes (The DS1307 outputs 1-7 values for day of week)
char daysfull[7][9]={
"Sunday", "Monday", "Tuesday", "Wed", "Thursday", "Friday", "Saturday" };
char suffix[4][3]={
"st", "nd", "rd", "th"}; //date suffix array, used in slide, normal and jumble modes. e,g, 1st 2nd ...
//intial setup
void setup ()
{
Serial.println("Startting Up!");
pinMode(13,OUTPUT);
pinMode(LEDsync,OUTPUT);
pinMode(LED30sec,OUTPUT);
pinMode(sync30sec,INPUT);
pinMode(tmrOutput, OUTPUT);
digitalWrite(2, HIGH); // turn on pullup resistor for button on pin 2
digitalWrite(3, HIGH); // turn on pullup resistor for button on pin 3
digitalWrite(6, HIGH); // turn on pullup resistor for button on pin 6
digitalWrite(7, HIGH); // turn on pullup resistor for button on pin 6
digitalWrite(8, HIGH); // turn on pullup resistor for button on pin 8
digitalWrite(15, HIGH); // turn on pullup resistor for button on pin 15
digitalWrite(16, HIGH); // turn on pullup resistor for button on pin 16
Serial.begin(57600); // Setup serial output
ht1632_setup(); // Setup display (uses flow chart from page 17 of sure datasheet)
randomSeed(analogRead(1)); // Setup random number generator
ht1632_sendcmd(0, HT1632_CMD_PWM + brightness);
ht1632_sendcmd(1, HT1632_CMD_PWM + brightness);
//Setup DS1307 RTC
#ifdef AVR
Wire.begin();
#else
Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino
#endif
ds1307.begin(); //start RTC Clock
if (! ds1307.isrunning()) {
Serial.println("RTC is NOT running!");
ds1307.adjust(DateTime(__DATE__, __TIME__)); // sets the RTC to the date & time this sketch was compiled
}
printver(); // Display clock software version
digitalWrite(tmrOutput, HIGH); //Turns off timer alarm sound
Serial.println("Setting up Temp Sensor!");
// Initialize the sensor (it is important to get calibration values stored on the device).
if (tempsensor.begin())
Serial.println("BMP180 init success");
else
{
// Oops, something went wrong, this is usually a connection problem,
// see the comments at the top of this sketch for the proper connections.
Serial.println("BMP180 init fail\n\n");
while(1); // Pause forever.
}
/*
//Adafruit MCP9808 temp sensor
// Make sure the sensor is found, you can also pass in a different i2c
// address with tempsensor.begin(0x19) for example
if (!tempsensor.begin(0x18)) {
Serial.println("Couldn't find MCP9808!");
while (1);
}
*/
// tempsensor.begin(0x18); // loads the MSP9808 default address
// Brett set ds1307 time to check timer workings uncomment to use
// DateTime now = ds1307.now();
// ds1307.adjust(DateTime(now.year(), now.month(), now.day(), 23, 56, 0));
// Brett set ds1307 time to check timer workings uncomment to use
}
// ****** MAIN LOOP ******
void loop ()
{
digitalWrite(tmrOutput, HIGH); //Turns off timer alarm sound
//run the clock with whatever mode is set by clock_mode - the default is set at top of code.
switch (clock_mode){
case 0:
slide();
break;
case 1:
// pong();
// put jumble back in???
break;
case 2:
digits();
break;
case 3:
word_clock();
break;
case 4:
normal(); // now Timer
break;
case 5:
setup_menu();
break;
}
// Serial.println("I'm in the loop");
}
// ****** DISPLAY-ROUTINES ******
//ht1632_chipselect / ht1632_chipfree
//Select or de-select a particular ht1632 chip. De-selecting a chip ends the commands being sent to a chip.
//CD pins are active-low; writing 0 to the pin selects the chip.
void ht1632_chipselect(byte chipno)
{
DEBUGPRINT("\nHT1632(%d) ", chipno);
digitalWrite(chipno, 0);
}
void ht1632_chipfree(byte chipno)
{
DEBUGPRINT(" [done %d]", chipno);
digitalWrite(chipno, 1);
}
//ht1632_writebits
//Write bits (up to 8) to h1632 on pins ht1632_data, ht1632_wrclk Chip is assumed to already be chip-selected
//Bits are shifted out from MSB to LSB, with the first bit sent being (bits & firstbit), shifted till firsbit is zero.
void ht1632_writebits (byte bits, byte firstbit)
{
DEBUGPRINT(" ");
while (firstbit) {
DEBUGPRINT((bits&firstbit ? "1" : "0"));
digitalWrite(ht1632_wrclk, LOW);
if (bits & firstbit) {
digitalWrite(ht1632_data, HIGH);
}
else {
digitalWrite(ht1632_data, LOW);
}
digitalWrite(ht1632_wrclk, HIGH);
firstbit >>= 1;
}
}
// ht1632_sendcmd
// Send a command to the ht1632 chip. A command consists of a 3-bit "CMD" ID, an 8bit command, and one "don't care bit".
// Select 1 0 0 c7 c6 c5 c4 c3 c2 c1 c0 xx Free
static void ht1632_sendcmd (byte d, byte command)
{
ht1632_chipselect(ht1632_cs[d]); // Select chip
ht1632_writebits(HT1632_ID_CMD, 1<<2); // send 3 bits of id: COMMMAND
ht1632_writebits(command, 1<<7); // send the actual command
ht1632_writebits(0, 1); // one extra dont-care bit in commands.
ht1632_chipfree(ht1632_cs[d]); //done
}
//ht1632_senddata
//send a nibble (4 bits) of data to a particular memory location of the
//ht1632. The command has 3 bit ID, 7 bits of address, and 4 bits of data.
// Select 1 0 1 A6 A5 A4 A3 A2 A1 A0 D0 D1 D2 D3 Free
//Note that the address is sent MSB first, while the data is sent LSB first!
//This means that somewhere a bit reversal will have to be done to get
//zero-based addressing of words and dots within words.
static void ht1632_senddata (byte d, byte address, byte data)
{
ht1632_chipselect(ht1632_cs[d]); // Select chip
ht1632_writebits(HT1632_ID_WR, 1<<2); // Send ID: WRITE to RAM
ht1632_writebits(address, 1<<6); // Send address
ht1632_writebits(data, 1<<3); // Send 4 bits of data
ht1632_chipfree(ht1632_cs[d]); // Done.
}
//ht1632_setup
//setup the ht1632 chips
void ht1632_setup()
{
for (byte d=0; d<NUM_DISPLAYS; d++) {
pinMode(ht1632_cs[d], OUTPUT);
digitalWrite(ht1632_cs[d], HIGH); // Unselect (active low)
pinMode(ht1632_wrclk, OUTPUT);
pinMode(ht1632_data, OUTPUT);
ht1632_sendcmd(d, HT1632_CMD_SYSON); // System on
ht1632_sendcmd(d, HT1632_CMD_LEDON); // LEDs on
ht1632_sendcmd(d, HT1632_CMD_COMS01); // NMOS Output 24 row x 24 Com mode
for (byte i=0; i<128; i++)
ht1632_senddata(d, i, 0); // clear the display!
}
}
//we keep a copy of the display controller contents so that we can know which bits are on without having to (slowly) read the device.
//Note that we only use the low four bits of the shadow ram, since we're shadowing 4-bit memory. This makes things faster, and we
//use the other half for a "snapshot" when we want to plot new data based on older data...
byte ht1632_shadowram[NUM_DISPLAYS * 96]; // our copy of the display's RAM
//plot a point on the display, with the upper left hand corner being (0,0).
//Note that Y increases going "downward" in contrast with most mathematical coordiate systems, but in common with many displays
//No error checking; bad things may happen if arguments are out of bounds! (The ASSERTS compile to nothing by default
void ht1632_plot (char x, char y, char val)
{
char addr, bitval;
ASSERT(x >= 0);
ASSERT(x <= X_MAX);
ASSERT(y >= 0);
ASSERT(y <= y_MAX);
byte d;
//select display depending on plot values passed in
if (x >= 0 && x <=23 ) {
d = 0;
}
if (x >=24 && x <=47) {
d = 1;
x = x-24;
}
/*
* The 4 bits in a single memory word go DOWN, with the LSB (first transmitted) bit being on top. However, writebits()
* sends the MSB first, so we have to do a sort of bit-reversal somewhere. Here, this is done by shifting the single bit in
* the opposite direction from what you might expect.
*/
bitval = 8>>(y&3); // compute which bit will need set
addr = (x<<2) + (y>>2); // compute which memory word this is in
if (val) { // Modify the shadow memory
ht1632_shadowram[(d * 96) + addr] |= bitval;
}
else {
ht1632_shadowram[(d * 96) + addr] &= ~bitval;
}
// Now copy the new memory value to the display
ht1632_senddata(d, addr, ht1632_shadowram[(d * 96) + addr]);
}
//get_shadowram
//return the value of a pixel from the shadow ram.
byte get_shadowram(byte x, byte y)
{
byte addr, bitval, d;
//select display depending on plot values passed in
if (x >= 0 && x <=23 ) {
d = 0;
}
if (x >=24 && x <=47) {
d = 1;
x = x-24;
}
bitval = 8>>(y&3); // compute which bit will need set
addr = (x<<2) + (y>>2); // compute which memory word this is in
return (0 != (ht1632_shadowram[(d * 96) + addr] & bitval));
}
//snapshot_shadowram
//Copy the shadow ram into the snapshot ram (the upper bits)
//This gives us a separate copy so we can plot new data while
//still having a copy of the old data. snapshotram is NOT
//updated by the plot functions (except "clear")
void snapshot_shadowram()
{
for (byte i=0; i< sizeof ht1632_shadowram; i++) {
ht1632_shadowram[i] = (ht1632_shadowram[i] & 0x0F) | ht1632_shadowram[i] << 4; // Use the upper bits
}
}
//get_snapshotram
//get a pixel value from the snapshot ram (instead of
//the actual displayed (shadow) memory
byte get_snapshotram(byte x, byte y)
{
byte addr, bitval;
byte d = 0;
//select display depending on plot values passed in
if (x >=24 && x <=47) {
d = 1;
x = x-24;
}
bitval = 128>>(y&3); // user upper bits!
addr = (x<<2) + (y>>2); // compute which memory word this is in
if (ht1632_shadowram[(d * 96) + addr] & bitval)
return 1;
return 0;
}
//ht1632_clear
//clear the display, and the shadow memory, and the snapshot
//memory. This uses the "write multiple words" capability of
//the chipset by writing all 96 words of memory without raising
//the chipselect signal.
void ht1632_clear()
{
char i;
for(byte d=0; d<NUM_DISPLAYS; d++)
{
ht1632_chipselect(ht1632_cs[d]); // Select chip
ht1632_writebits(HT1632_ID_WR, 1<<2); // send ID: WRITE to RAM
ht1632_writebits(0, 1<<6); // Send address
for (i = 0; i < 96/2; i++) // Clear entire display
ht1632_writebits(0, 1<<7); // send 8 bits of data
ht1632_chipfree(ht1632_cs[d]); // done
for (i=0; i < 96; i++)
ht1632_shadowram[96*d + i] = 0;
}
}
//ht1632_clear_bottom
//clear the bottom line of the display, and the shadow memory, and the snapshot
//memory. This uses the "write multiple words" capability of
//the chipset by writing all 96 words of memory without raising
//the chipselect signal.
void ht1632_clear_bottom()
{
ht1632_putchar(0,8,32);
ht1632_putchar(6,8,32);
ht1632_putchar(18,8,32);
ht1632_putchar(24,8,32);
ht1632_putchar(30,8,32);
ht1632_putchar(36,8,32);
ht1632_putchar(42,8,32);
ht1632_putchar(50,8,32);
}
/*
* fade_down
* fade the display to black
*/
void fade_down() {
char intensity;
for (intensity=brightness; intensity >= 0; intensity--) {
ht1632_sendcmd(0, HT1632_CMD_PWM + intensity); //send intensity commands using CS0 for display 0
ht1632_sendcmd(1, HT1632_CMD_PWM + intensity); //send intensity commands using CS0 for display 1
delay(FADEDELAY);
}
//clear the display and set it to full brightness again so we're ready to plot new stuff
cls();
ht1632_sendcmd(0, HT1632_CMD_PWM + brightness);
ht1632_sendcmd(1, HT1632_CMD_PWM + brightness);
}
/*
* fade_up
* fade the display up to full brightness
*/
void fade_up() {
char intensity;
for ( intensity=0; intensity < brightness; intensity++) {
ht1632_sendcmd(0, HT1632_CMD_PWM + intensity); //send intensity commands using CS0 for display 0
ht1632_sendcmd(1, HT1632_CMD_PWM + intensity); //send intensity commands using CS0 for display 1
delay(FADEDELAY);
}
}
/* ht1632_putchar
* Copy a 5x7 character glyph from the myfont data structure to display memory, with its upper left at the given coordinate
* This is unoptimized and simply uses plot() to draw each dot.
*/
void ht1632_putchar(byte x, byte y, char c)
{
byte dots;
// if (c >= 'A' && c <= 'Z' || (c >= 'a' && c <= 'z') ) {
// c &= 0x1F; // A-Z maps to 1-26
// }
if (c >= 'A' && c <= 'Z' ) {
c &= 0x1F; // A-Z maps to 1-26
}
else if (c >= 'a' && c <= 'z') {
c = (c - 'a') + 41; // A-Z maps to 41-67
}
else if (c >= '0' && c <= '9') {
c = (c - '0') + 31;
}
else if (c == ' ') {
c = 0; // space
}
else if (c == '.') {
c = 27; // full stop
}
else if (c == '\'') {
c = 28; // single quote mark
}
else if (c == ':') {
c = 29; // clock_mode selector arrow
}
else if (c == '>') {
c = 30; // clock_mode selector arrow
}
else if (c >= -80 && c<= -67) {
c *= -1;
}
for (char col=0; col< 5; col++) {
dots = pgm_read_byte_near(&myfont[c][col]);
for (char row=0; row < 7; row++) {
//check coords are on screen before trying to plot
if ((x >= 0) && (x <= X_MAX) && (y >= 0) && (y <= Y_MAX)){
if (dots & (64>>row)) { // only 7 rows.
plot(x+col, y+row, 1);
} else {
plot(x+col, y+row, 0);
}
}
}
}
}
//ht1632_putbigchar
//Copy a 10x14 character glyph from the myfont data structure to display memory, with its upper left at the given coordinate
//This is unoptimized and simply uses plot() to draw each dot.
void ht1632_putbigchar(byte x, byte y, char c)
{
byte dots;
if (c >= 'A' && c <= 'Z' || (c >= 'a' && c <= 'z') ) {
return; //return, as the 10x14 font contains only numeric characters
}
if (c >= '0' && c <= '9') {
c = (c - '0');
c &= 0x1F;
}
for (byte col=0; col< 10; col++) {
dots = pgm_read_byte_near(&mybigfont[c][col]);
for (char row=0; row < 8; row++) {
if (dots & (128>>row))
plot(x+col, y+row, 1);
else
plot(x+col, y+row, 0);
}
dots = pgm_read_byte_near(&mybigfont[c][col+10]);
for (char row=0; row < 8; row++) {
if (dots & (128>>row))
plot(x+col, y+row+8, 1);
else
plot(x+col, y+row+8, 0);
}
}
}
// ht1632_puttinychar
// Copy a 3x5 character glyph from the myfont data structure to display memory, with its upper left at the given coordinate
// This is unoptimized and simply uses plot() to draw each dot.
void ht1632_puttinychar(byte x, byte y, char c)
{
byte dots;
if (c >= 'A' && c <= 'Z' || (c >= 'a' && c <= 'z') ) {
c &= 0x1F; // A-Z maps to 1-26
}
else if (c >= '0' && c <= '9') {
c = (c - '0') + 31;
}
else if (c == ' ') {
c = 0; // space
}
else if (c == '.') {
c = 27; // full stop
}
else if (c == '\'') {
c = 28; // single quote mark
}
else if (c == '!') {
c = 29; // single quote mark
}
else if (c == '?') {
c = 30; // single quote mark
}
for (byte col=0; col< 3; col++) {
dots = pgm_read_byte_near(&mytinyfont[c][col]);
for (char row=0; row < 5; row++) {
if (dots & (16>>row))
plot(x+col, y+row, 1);
else
plot(x+col, y+row, 0);
}
}
}
//************ CLOCK MODES ***************
// digits()
// show the time in 10x14 characters and update it whilst the loop runs
void digits()
{
//Brett turns random mode on if on before and returns random mode store to 0
if (random_modestore == 1)
{
random_mode = random_modestore;
}
//Brett end turns random mode on if on before
cls();
char buffer[3]; //for int to char conversion to turn rtc values into chars we can print on screen
byte offset = 0; //used to offset the x postition of the digits and centre the display when we are in 12 hour mode and the clock shows only 3 digits. e.g. 3:21
byte x,y; //used to draw a clear box over the left hand "1" of the display when we roll from 12:59 -> 1:00am in 12 hour mode.
//do 12/24 hour conversion if ampm set to 1
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
//set the next minute we show the date at
set_next_date();
// initially set mins to value 100 - so it wll never equal rtc[1] on the first loop of the clock, meaning we draw the clock display when we enter the function
byte secs = 100;
byte mins = 100;
int count = 0;
//run clock main loop as long as run_mode returns true
while (run_mode()){
//get the time from the clock chip
get_time();
//check to see if the buttons have been pressed
if(buttonA.uniquePress()){
switch_mode(); // pick the new mode via the menu
return; //exit this mode back to loop(), so the new clock mode function is called.
}
if(buttonB.uniquePress()){
display_date();
fade_down();
return; //exit the mode back to loop(), Then we reenter the function and the clock mode starts again. This refreshes the display
}
//Brett select timer mode from any other mode by pressing buttonStTmr
if(buttonStTmr.uniquePress()){
fade_down();
clock_mode = 4;
return; //exit the mode back to loop(), Then we reenter the function and the clock mode starts again. This refreshes the display
}
//check whether it's time to automatically display the date
check_show_date();
//draw the flashing : as on if the secs have changed.
if (secs != rtc[0]) {
//update secs with new value
secs = rtc[0];
//draw :
plot (23 - offset,4,1); //top point
plot (23 - offset,5,1);
plot (24 - offset,4,1);
plot (24 - offset,5,1);
plot (23 - offset,10,1); //bottom point
plot (23 - offset,11,1);
plot (24 - offset,10,1);
plot (24 - offset,11,1);
count = 400;
}
//if count has run out, turn off the :
if (count == 0){
plot (23 - offset,4,0); //top point
plot (23 - offset,5,0);
plot (24 - offset,4,0);
plot (24 - offset,5,0);
plot (23 - offset,10,0); //bottom point
plot (23 - offset,11,0);
plot (24 - offset,10,0);
plot (24 - offset,11,0);
}
else {
count--;
}
//re draw the display if mins != rtc[1] i.e. if the time has changed from what we had stored in mins, (also trigggered on first entering function when mins is 100)
if (mins != rtc[1]) {
//update mins and hours with the new values
mins = rtc[1];
hours = rtc[2];
//adjust hours of ampm set to 12 hour mode
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
itoa(hours,buffer,10);
//if hours < 10 the num e.g. "3" hours, itoa coverts this to chars with space "3 " which we dont want
if (hours < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
//print hours
//if we in 12 hour mode and hours < 10, then don't print the leading zero, and set the offset so we centre the display with 3 digits.
if (ampm && hours < 10) {
offset = 5;
//if the time is 1:00am clear the entire display as the offset changes at this time and we need to blank out the old 12:59
if((hours == 1 && mins == 0) ){
cls();
}
}
else {
//else no offset and print hours tens digit
offset = 0;
//if the time is 10:00am clear the entire display as the offset changes at this time and we need to blank out the old 9:59
if (hours == 10 && mins == 0) {
cls();
}
ht1632_putbigchar(0, 1, buffer[0]);
}
//print hours ones digit
ht1632_putbigchar(12 - offset, 1, buffer[1]);
//print mins
//add leading zero if mins < 10
itoa (mins, buffer, 10);
if (mins < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
//print mins tens and ones digits
ht1632_putbigchar(26 - offset, 1, buffer[0]);
ht1632_putbigchar(38 - offset, 1, buffer[1]);
}
}
fade_down();
}
//print a clock using words rather than numbers
void word_clock() {
//Brett turns random mode on if on before and returns random mode store to 0
if (random_modestore == 1)
{
random_mode = random_modestore;
}
//Brett end turns random mode on if on before
cls();
char numbers[19][10] = {
"one", "two", "three", "four","five","six","seven","eight","nine","ten",
"eleven","twelve", "thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen"};
char numberstens[5][7] = {
"ten","twenty","thirty","forty","fifty"};
byte hours_y, mins_y; //hours and mins and positions for hours and mins lines
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
get_time(); //get the time from the clock chip
byte old_mins = 100; //store mins in old_mins. We compare mins and old mins & when they are different we redraw the display. Set this to 100 initially so display is drawn when mode starts.
byte mins;
//run clock main loop as long as run_mode returns true
while (run_mode()){
if(buttonA.uniquePress()){
switch_mode();
return;
}
if(buttonB.uniquePress()){
display_date();
fade_down();
return;
}
//Brett select timer mode from any other mode by pressing buttonStTmr
if(buttonStTmr.uniquePress()){
//void normal();
clock_mode = 4;
// fade_down();