├── clock_circuit.fzz
├── clock_circuit.png
├── US Schedule grid.pdf
├── Code
└── classroomClock
│ ├── classroomClock.h
│ └── classroomClock.ino
├── customizations
└── toneClassroomClock
│ ├── classroomClock.h
│ ├── pitches.h
│ └── toneClassroomClock.ino
├── Laser Files
├── README.md
├── 7_acrylic.svg
├── 0_bottom.svg
├── acrylic_discs.svg
├── 1_bumper.svg
├── circuit_case.svg
├── 3_4_digits.svg
├── 5_nut_digits.svg
├── 6_magnet_digits.svg
└── 2_neopixels.svg
└── README.md
/clock_circuit.fzz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jdeboi/Classroom-Clock/HEAD/clock_circuit.fzz
--------------------------------------------------------------------------------
/clock_circuit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jdeboi/Classroom-Clock/HEAD/clock_circuit.png
--------------------------------------------------------------------------------
/US Schedule grid.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jdeboi/Classroom-Clock/HEAD/US Schedule grid.pdf
--------------------------------------------------------------------------------
/Code/classroomClock/classroomClock.h:
--------------------------------------------------------------------------------
1 | #ifndef CLASSROOM_CLOCK
2 | #define CLASSROOM_CLOCK
3 |
4 | #define A_BLOCK 0
5 | #define B_BLOCK 1
6 | #define C_BLOCK 2
7 | #define D_BLOCK 3
8 | #define E_BLOCK 4
9 | #define F_BLOCK 5
10 | #define G_BLOCK 6
11 | #define H_BLOCK 7
12 |
13 | #define ACADEMIC 0
14 | #define ASSEMBLY 1
15 | #define LUNCH 2
16 |
17 | #endif
18 |
19 |
--------------------------------------------------------------------------------
/customizations/toneClassroomClock/classroomClock.h:
--------------------------------------------------------------------------------
1 | #ifndef CLASSROOM_CLOCK
2 | #define CLASSROOM_CLOCK
3 |
4 | #define A_BLOCK 0
5 | #define B_BLOCK 1
6 | #define C_BLOCK 2
7 | #define D_BLOCK 3
8 | #define E_BLOCK 4
9 | #define F_BLOCK 5
10 | #define G_BLOCK 6
11 | #define H_BLOCK 7
12 |
13 | #define ACADEMIC 0
14 | #define ASSEMBLY 1
15 | #define LUNCH 2
16 |
17 | #endif
18 |
19 |
--------------------------------------------------------------------------------
/Laser Files/README.md:
--------------------------------------------------------------------------------
1 | # Classroom-Clock
2 | a DIY digital clock designed for and by teachers
3 |
4 |
5 | [jdeboi.com](http://jdeboi.com/)
6 | [Instructable](http://www.instructables.com/editInstructable/edit/E8J84XOION6POZY/)
7 |
8 |
9 | These vector (.svg) files were designed in Autodesk Fusion 360, but can be edited by any vector program (Inkscape, Illustrator, etc.). The files are numbered. Refer to the Instructable for details.
--------------------------------------------------------------------------------
/customizations/toneClassroomClock/pitches.h:
--------------------------------------------------------------------------------
1 | /*************************************************
2 | * Public Constants
3 | *************************************************/
4 |
5 | #define NOTE_B0 31
6 | #define NOTE_C1 33
7 | #define NOTE_CS1 35
8 | #define NOTE_D1 37
9 | #define NOTE_DS1 39
10 | #define NOTE_E1 41
11 | #define NOTE_F1 44
12 | #define NOTE_FS1 46
13 | #define NOTE_G1 49
14 | #define NOTE_GS1 52
15 | #define NOTE_A1 55
16 | #define NOTE_AS1 58
17 | #define NOTE_B1 62
18 | #define NOTE_C2 65
19 | #define NOTE_CS2 69
20 | #define NOTE_D2 73
21 | #define NOTE_DS2 78
22 | #define NOTE_E2 82
23 | #define NOTE_F2 87
24 | #define NOTE_FS2 93
25 | #define NOTE_G2 98
26 | #define NOTE_GS2 104
27 | #define NOTE_A2 110
28 | #define NOTE_AS2 117
29 | #define NOTE_B2 123
30 | #define NOTE_C3 131
31 | #define NOTE_CS3 139
32 | #define NOTE_D3 147
33 | #define NOTE_DS3 156
34 | #define NOTE_E3 165
35 | #define NOTE_F3 175
36 | #define NOTE_FS3 185
37 | #define NOTE_G3 196
38 | #define NOTE_GS3 208
39 | #define NOTE_A3 220
40 | #define NOTE_AS3 233
41 | #define NOTE_B3 247
42 | #define NOTE_C4 262
43 | #define NOTE_CS4 277
44 | #define NOTE_D4 294
45 | #define NOTE_DS4 311
46 | #define NOTE_E4 330
47 | #define NOTE_F4 349
48 | #define NOTE_FS4 370
49 | #define NOTE_G4 392
50 | #define NOTE_GS4 415
51 | #define NOTE_A4 440
52 | #define NOTE_AS4 466
53 | #define NOTE_B4 494
54 | #define NOTE_C5 523
55 | #define NOTE_CS5 554
56 | #define NOTE_D5 587
57 | #define NOTE_DS5 622
58 | #define NOTE_E5 659
59 | #define NOTE_F5 698
60 | #define NOTE_FS5 740
61 | #define NOTE_G5 784
62 | #define NOTE_GS5 831
63 | #define NOTE_A5 880
64 | #define NOTE_AS5 932
65 | #define NOTE_B5 988
66 | #define NOTE_C6 1047
67 | #define NOTE_CS6 1109
68 | #define NOTE_D6 1175
69 | #define NOTE_DS6 1245
70 | #define NOTE_E6 1319
71 | #define NOTE_F6 1397
72 | #define NOTE_FS6 1480
73 | #define NOTE_G6 1568
74 | #define NOTE_GS6 1661
75 | #define NOTE_A6 1760
76 | #define NOTE_AS6 1865
77 | #define NOTE_B6 1976
78 | #define NOTE_C7 2093
79 | #define NOTE_CS7 2217
80 | #define NOTE_D7 2349
81 | #define NOTE_DS7 2489
82 | #define NOTE_E7 2637
83 | #define NOTE_F7 2794
84 | #define NOTE_FS7 2960
85 | #define NOTE_G7 3136
86 | #define NOTE_GS7 3322
87 | #define NOTE_A7 3520
88 | #define NOTE_AS7 3729
89 | #define NOTE_B7 3951
90 | #define NOTE_C8 4186
91 | #define NOTE_CS8 4435
92 | #define NOTE_D8 4699
93 | #define NOTE_DS8 4978
94 |
95 |
96 |
--------------------------------------------------------------------------------
/Laser Files/7_acrylic.svg:
--------------------------------------------------------------------------------
1 |
2 |
168 |
--------------------------------------------------------------------------------
/Laser Files/0_bottom.svg:
--------------------------------------------------------------------------------
1 |
2 |
168 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Classroom-Clock
2 | a DIY digital clock designed for and by teachers
3 |
4 |
5 | [jdeboi.com](http://jdeboi.com/)
6 | [Instructable](http://www.instructables.com/editInstructable/edit/E8J84XOION6POZY/)
7 |
8 |
9 | ## Editing Schedule
10 |
11 | This clock is currently set up for 12-hour, [rotating block schedule] (https://github.com/jdeboi/Classroom-Clock/blob/master/US%20Schedule%20grid.pdf). If you develop a 24-hour clock, please let me know! Would love to include a link!
12 |
13 | To add your own schedule, there are a number of changes. Look for the following section of code:
14 |
15 | ```c++
16 | ////////////////////////////////////////////////////////
17 | /////////////////////////////////////////////////////////
18 | // CUSTOMIZE THIS STUFF
19 | /////////////////////////////////////////////////////////
20 |
21 | /*
22 | * Set the functionality of the extra digit
23 | * show nothing in the extra digit is mode 0
24 | * show rotating block is mode 1
25 | * show period number is mode 2
26 | */
27 | int extraDigitMode = 1;
28 |
29 | // setup for a rotating block schedule
30 | uint8_t currentBlock = A_BLOCK;
31 |
32 | // this number should match the number of entries in schedule[]
33 | const uint8_t numTimeBlocks = 9;
34 |
35 | uint8_t schedule[numTimeBlocks][5] = {
36 | // use 24 hour clock numbers even though this clock is a 12 hour clock
37 | // {starting hour, start min, end hour, end min}
38 | {8, 0, 9, 0, ACADEMIC}, // 0 - Period 1: 8 - 9
39 | {9, 3, 9, 48, ACADEMIC}, // 1 - Period 2: 9:03 - 9:48
40 | {9, 48, 10, 18, ASSEMBLY}, // 2 - Assembly: 9:48 - 10:18
41 | {10, 18, 11, 3, ACADEMIC}, // 3 - Period 3: 10:18 - 11:03
42 | {11, 6, 11, 51, ACADEMIC}, // 4 - Period 4: 11:06 - 11:51
43 | {11, 54, 12, 39, ACADEMIC}, // 5 - Period 5: 11:54 - 12:39
44 | {12, 39, 13, 24, LUNCH}, // 6 - Lunch: 12:39 - 1:24
45 | {13, 27, 14, 12, ACADEMIC}, // 7 - Period 6: 1:27 - 2:12
46 | {14, 15, 15, 0, ACADEMIC} // 8 - Period 7: 2:15 - 3:00
47 | };
48 |
49 |
50 | // these are the array indicies of non-academic time blocks
51 | const uint8_t assemblyBlock = 2;
52 | const uint8_t lunchBlock = 6;
53 |
54 | // number of minutes before end of class when countdown clock is triggered
55 | uint8_t countdownM = 6;
56 | uint8_t secBetweenFlashes = 4;
57 | ```
58 |
59 | Edit the classPeriods array to match your schedule. For example, if your first period starts at 7:45AM and ends at 8:45AM:
60 |
61 | ```c++
62 |
63 | uint8_t classPeriods[numPeriods][4] = {
64 | // use 24 hour clock numbers even though this clock is a 12 hour clock
65 | // {starting hour, start min, end hour, end min}
66 | {7, 45, 8, 45}, // Period 0: 7:45AM - 8:45AM
67 |
68 | ```
69 |
70 | classPeriods[] should only include periods for academic classes- not homeroom, lunch, advisory, club, etc. Those time blocks should be entered into the "otherBlocks" which we'll talk about in a second. Separating academic classes from other time blocks is important in order to ensure the rotating block letter updates appropriately.
71 |
72 | Once you've entered all of your relevant classPeriods[], count up the number of entries (should be the number of class periods), and set "numPeriods" to this value:
73 |
74 | ```c++
75 | const uint8_t numPeriods = 7;
76 | ```
77 |
78 | All other time blocks should be entered into numOtherBlocks array:
79 |
80 | ```c++
81 | const uint8_t numOtherBlocks = 2; // this number should match the number
82 | // of entries in otherBlocks[]
83 |
84 | uint8_t otherBlocks[numOtherBlocks][4] = {
85 | {9, 48, 10, 18}, // assembly: 9:48 - 10:18
86 | {12, 39, 13, 24} // Lunch: 12:39 - 1:24
87 | };
88 |
89 | ```
90 |
91 | If you'd like to add another "other block", such as a homeroom:
92 |
93 | ```c++
94 | const uint8_t numOtherBlocks = 3; // this number should match the number
95 | // of entries in otherBlocks[]
96 |
97 | uint8_t otherBlocks[numOtherBlocks][4] = {
98 | {9, 48, 10, 18}, // assembly: 9:48 - 10:18
99 | {12, 39, 13, 24}, // Lunch: 12:39 - 1:24
100 | {8, 15, 8, 20} // homeroom: 8:15 - 8:20AM
101 | };
102 |
103 | const uint8_t assemblyBlock = 0;
104 | const uint8_t lunchBlock = 1;
105 | const uint8_t homeroomBlock = 2; // adding this
106 | ```
107 | If your school doesn't begin with an academic class (like a homeroom), you will need to change the "boolean isBeforeSchool()" function. IF it starts with the homeroom block from the previous example:
108 |
109 | ```c++
110 | boolean isBeforeSchool() {
111 | // You may need to edit these values if school begins with a homeroom or
112 | // otherBlock[] that isn't a classperiod[]
113 | return (now.hour() <= otherBlocks[homeroomBlock][0] && now.minute() < otherBlocks[homeroomBlock][1]);
114 | }
115 | ```
116 | There's probably other code editing you'll have to do for these types of schedule modifications. Please let me know (or submit pull requests) as you run into problems specific to your use case.
117 |
118 | To set the number of minutes before the countdown timer triggered, edit:
119 |
120 | ```c++
121 | uint8_t countdownM = 6;
122 | ```
123 |
124 | ## Editing Clock Display
125 | The other main section of code that you'll have to edit is the display clock section:
126 |
127 | ```c++
128 | void displayClock() {
129 | if(isEndOfDay()) nextDay();
130 | else if (isWeekend()) pulseClock(Wheel(250),10);
131 | else if (isBeforeSchool()) colorClock(Wheel(240));
132 | else if (isAfterSchool()) pulseClock(Wheel(100),5);
133 | else if (isEndFlash()) countdownClock();
134 | else if (isLunch()) pulseClock(Wheel(100),5);
135 | else if (isAssembly()) rainbowClock(5);
136 | else if (isDuringClass()) gradientClock();
137 | else colorClock(Wheel(20));
138 | }
139 | ```
140 |
141 | To get rid of the countdown timer triggered at the end of the class, comment out ("//") the countdownClock() line:
142 |
143 | ```c++
144 | void displayClock() {
145 | if(isEndOfDay()) nextDay();
146 | else if (isWeekend()) pulseClock(Wheel(250),10);
147 | else if (isBeforeSchool()) colorClock(Wheel(240));
148 | else if (isAfterSchool()) pulseClock(Wheel(100),5);
149 | //else if (isEndFlash()) countdownClock();
150 | else if (isLunch()) pulseClock(Wheel(100),5);
151 | else if (isAssembly()) rainbowClock(5);
152 | else if (isDuringClass()) gradientClock();
153 | else colorClock(Wheel(20));
154 | }
155 | ```
156 |
157 | ## Clock functions
158 | - You can change the light effect by changing the function after the else if. Possible options include: colorClock(), pulseClock(), randoClock(), rainbowClock(), mardiGrasClock(), etc. Check the code for examples.
159 | - You can change colors of some clocks (colorClock(), pulseClock, colorClock()) by changing the number in Wheel(). 0 is red and 255 is purple; experiment with values in between.
160 | - The number passed to rainbowClock() represents the speed that it cycles through the colors of the rainbow.
161 | - countdownClock(), gradientClock(), xMasClock(), mardiGrasClock(), birthdayClock(), etc. don't take arguments (i.e. no values passed inside the parentheses).
162 | - pulseCock() takes a color (passed inside Wheel()), and a number representing the amount of time to wait between pulses.
163 |
164 | Add your own functions! Feel free to do a pull request to add your functionality to the Classroom Clock!
165 |
--------------------------------------------------------------------------------
/Laser Files/acrylic_discs.svg:
--------------------------------------------------------------------------------
1 |
2 |
235 |
--------------------------------------------------------------------------------
/Laser Files/1_bumper.svg:
--------------------------------------------------------------------------------
1 |
2 |
258 |
--------------------------------------------------------------------------------
/Laser Files/circuit_case.svg:
--------------------------------------------------------------------------------
1 |
2 |
369 |
--------------------------------------------------------------------------------
/Laser Files/3_4_digits.svg:
--------------------------------------------------------------------------------
1 |
2 |
348 |
--------------------------------------------------------------------------------
/Laser Files/5_nut_digits.svg:
--------------------------------------------------------------------------------
1 |
2 |
348 |
--------------------------------------------------------------------------------
/Laser Files/6_magnet_digits.svg:
--------------------------------------------------------------------------------
1 |
2 |
395 |
--------------------------------------------------------------------------------
/Code/classroomClock/classroomClock.ino:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////
2 | /*
3 | Jenna deBoisblanc
4 | 2016
5 | http://jdeboi.com/
6 |
7 | CLASSROOM CLOCK
8 | This customizable Arduino clock was developed for the Isidore
9 | Newman School Makerspace. Some of the features include:
10 | - tracks and displays Newman's rotating block (A, B, C...)
11 | - uses a red->green color gradient to show the amount of time
12 | remaining in the period
13 | - flashes between the time and a countdown timer when the end
14 | of the period approaches
15 | - rainbows during lunch and Assembly, pulses after school...
16 | And so much more! Add your own functions to make School Clock
17 | even cooler!
18 |
19 | */
20 | /////////////////////////////////////////////////////////
21 |
22 | #include "classroomClock.h"
23 | #include
24 | #include
25 | #include "RTClib.h" // https://github.com/adafruit/RTClib
26 | #include
27 | #include
28 | #include // https://github.com/adafruit/Adafruit_NeoPixel
29 |
30 | #define PIN 3
31 | #define NUM_PIXELS 32
32 |
33 | #define DEBUG false
34 | #define SPEED_CLOCK true
35 |
36 | Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, PIN, NEO_GRB + NEO_KHZ800);
37 | RTC_DS1307 RTC;
38 | DateTime now;
39 | uint8_t currentPeriod = 0;
40 | uint8_t lastHour = 0;
41 | uint8_t timeBlockIndex = 0;
42 |
43 | DateTime lastFlash; // for countdown "flash"
44 | boolean flashOn = false;
45 | long lastSpeedTest = 0;
46 |
47 | byte numbers[] = {
48 | B11101110, // 0
49 | B10001000, // 1
50 | B01111100, // 2 5555
51 | B11011100, // 3 6 4
52 | B10011010, // 4 3333
53 | B11010110, // 5 2 0
54 | B11110110, // 6 1111
55 | B10001100, // 7
56 | B11111110, // 8
57 | B10011110 // 9
58 | };
59 | byte letters[] = {
60 | B10111110, // A 55555
61 | B11110010, // B 6 4
62 | B01110000, // C 6 4
63 | B11111000, // D 33333
64 | B01110110, // E 2 0
65 | B00110110, // F 2 0
66 | B11011110, // G 11111
67 | B10111010, // H
68 | B01100010, // L
69 | B00000000 //
70 | };
71 |
72 | /////////////////////////////////////////////////////////
73 | /////////////////////////////////////////////////////////
74 | // CUSTOMIZE THIS STUFF
75 | /////////////////////////////////////////////////////////
76 |
77 | /*
78 | * Set the functionality of the extra digit
79 | * show nothing in the extra digit is mode 0
80 | * show rotating block is mode 1
81 | * show period number is mode 2
82 | */
83 | int extraDigitMode = 1;
84 |
85 | // setup for a rotating block schedule
86 | uint8_t currentBlock = D_BLOCK;
87 |
88 | // this number should match the number of entries in schedule[]
89 | const uint8_t numTimeBlocks = 9;
90 |
91 | uint8_t schedule[numTimeBlocks][5] = {
92 | // use 24 hour clock numbers even though this clock is a 12 hour clock
93 | // {starting hour, start min, end hour, end min}
94 | {8, 0, 9, 0, ACADEMIC}, // 0 - Period 1: 8 - 9
95 | {9, 3, 9, 48, ACADEMIC}, // 1 - Period 2: 9:03 - 9:48
96 | {9, 48, 10, 15, ASSEMBLY}, // 2 - Assembly: 9:48 - 10:18
97 | {10, 18, 11, 3, ACADEMIC}, // 3 - Period 3: 10:18 - 11:03
98 | {11, 6, 11, 51, ACADEMIC}, // 4 - Period 4: 11:06 - 11:51
99 | {11, 54, 12, 39, ACADEMIC}, // 5 - Period 5: 11:54 - 12:39
100 | {12, 39, 13, 24, LUNCH}, // 6 - Lunch: 12:39 - 1:24
101 | {13, 27, 14, 12, ACADEMIC}, // 7 - Period 6: 1:27 - 2:12
102 | {14, 15, 15, 0, ACADEMIC} // 8 - Period 7: 2:15 - 3:00
103 | };
104 |
105 | // this number should match the number of entries in vacations[]
106 | const uint8_t numVacations = 3;
107 | uint8_t vacations[numVacations][3] = {
108 | {2016, 1, 10},
109 | {2016, 2, 20},
110 | {2016, 3, 30}
111 | };
112 |
113 | // number of minutes before end of class when countdown clock is triggered
114 | uint8_t countdownM = 6;
115 | uint8_t secBetweenFlashes = 4;
116 |
117 | /////////////////////////////////////////////////////////
118 | /////////////////////////////////////////////////////////
119 | // THE GUTS
120 | /////////////////////////////////////////////////////////
121 | void setup() {
122 | Serial.begin(57600);
123 | if (DEBUG) Serial.begin(57600);
124 | /*
125 | * For testing purposes, you can set the clock to custom values, e.g.:
126 | * initChronoDot(year, month, day, hour, minute, seconds);
127 | * Otherwise, the clock automatically sets itself to your computer's
128 | * time with the function initChronoDot();
129 | */
130 | // initChronoDot(2016, 11, 7, 15, 59, 50);
131 | initChronoDot();
132 | strip.begin();
133 | strip.show();
134 | setSchedule();
135 | delay(3000);
136 | }
137 |
138 | void loop() {
139 | now = RTC.now();
140 | if (isEndOfDay()) nextDay();
141 | if (isSchoolDay()) checkBlock();
142 | displayClock();
143 | }
144 |
145 | /////////////////////////////////////////////////////////
146 | /////////////////////////////////////////////////////////
147 | // CUSTOMIZE DISPLAY FUNCTIONALITY
148 | /////////////////////////////////////////////////////////
149 |
150 | void displayClock() {
151 | if (isWeekend()) colorClock(Wheel(0));
152 | else if (!isSchoolDay()) colorClock(Wheel(20));
153 | else if (isBeforeSchool()) pulseClock(Wheel(40), 5);
154 | else if (isAfterSchool()) colorClock(Wheel(100));
155 | else if (isEndFlash()) countdownClock();
156 | else if (isLunch()) birthdayClock(300);
157 | else if (isAssembly()) rainbowClock(5);
158 | else if (isDuringClass()) gradientClock();
159 | else {
160 | //between classes
161 | mardiGrasClock();
162 | }
163 | }
164 |
165 |
166 | /////////////////////////////////////////////////////////
167 | /////////////////////////////////////////////////////////
168 | // CHECK
169 | /////////////////////////////////////////////////////////
170 | void checkBlock() {
171 | if (timeBlockIndex < numTimeBlocks) {
172 | uint8_t h = schedule[timeBlockIndex][2];
173 | uint8_t m = schedule[timeBlockIndex][3];
174 | if (isAfterTime(now.hour(), now.minute(), h, m)) {
175 | if (schedule[timeBlockIndex][4] == ACADEMIC) {
176 | currentPeriod++;
177 | currentBlock++;
178 | if (currentBlock == 8) currentBlock = 0;
179 | if (DEBUG) {
180 | Serial.print("Academic period changed to: ");
181 | Serial.print(currentPeriod);
182 | Serial.print(" & block changed to: ");
183 | Serial.println(currentBlock);
184 | }
185 | }
186 | timeBlockIndex++;
187 | if (DEBUG) {
188 | Serial.print("Time block index: ");
189 | Serial.println(timeBlockIndex);
190 | }
191 | }
192 | }
193 | }
194 |
195 | int getCurrentPeriod() {
196 | int period = -1;
197 | if (isBeforeSchool() || isAfterSchool()) period = 0;
198 | // check if it's before the end of an academic period
199 | // if it's not during an academic period, find the next academic period
200 | for (int i = 0; i < numTimeBlocks; i++ ) {
201 | if (schedule[i][4] == ACADEMIC) {
202 | period++;
203 | }
204 | if (isBeforeTime(now.hour(), now.minute(), schedule[i][2], schedule[i][3])) {
205 | break;
206 | }
207 | }
208 | return period;
209 | }
210 |
211 | int getCurrentTimeBlock() {
212 | int tb = 0;
213 | if (isBeforeSchool() || isAfterSchool()) tb = 0;
214 | // check if it's before the end of an academic period
215 | // if it's not during an academic period, find the next academic period
216 | for (int i = 0; i < numTimeBlocks; i++ ) {
217 | if (isBeforeTime(now.hour(), now.minute(), schedule[i][2], schedule[i][3])) {
218 | return i;
219 | }
220 | }
221 | return tb;
222 | }
223 |
224 | // returns true if the first number is before the
225 | boolean isBeforeTime(uint8_t h0, uint8_t m0, uint8_t h1, uint8_t m1) {
226 | if (timeDiff(h0, m0, h1, m1) < 0) return true;
227 | return false;
228 | }
229 |
230 | boolean isAfterTime(uint8_t h0, uint8_t m0, uint8_t h1, uint8_t m1) {
231 | if (timeDiff(h0, m0, h1, m1) >= 0) return true;
232 | return false;
233 | }
234 |
235 | // returns the difference in minutes
236 | // later time first
237 | int timeDiff(uint8_t h0, uint8_t m0, uint8_t h1, uint8_t m1) {
238 | uint16_t t0 = h0 * 60 + m0;
239 | uint16_t t1 = h1 * 60 + m1;
240 | return t0 - t1;
241 | }
242 |
243 | boolean isEndFlash() {
244 | uint8_t h;
245 | uint8_t m;
246 | if (isDuringTimeBlocks()) {
247 | h = schedule[timeBlockIndex][2];
248 | m = schedule[timeBlockIndex][3];
249 | }
250 | else if (isBetweenTimeBlocks()) {
251 | h = schedule[timeBlockIndex][0];
252 | m = schedule[timeBlockIndex][1];
253 | }
254 | else {
255 | return false;
256 | }
257 | if (timeDiff(h, m, now.hour(), now.minute()) < countdownM) {
258 | if (DEBUG) {
259 | Serial.print("End Flash: ");
260 | Serial.print(timeDiff( h, m, now.hour(), now.minute()));
261 | Serial.println(" minutes remaining");
262 | }
263 | if (now.unixtime() - lastFlash.unixtime() > secBetweenFlashes) {
264 | lastFlash = now;
265 | flashOn = !flashOn;
266 | }
267 | return flashOn;
268 | }
269 | return false;
270 | }
271 |
272 | boolean isSchoolDay() {
273 | if (isWeekend()) return false;
274 | else if (isVacation()) return false;
275 | return true;
276 | }
277 |
278 | boolean isWeekend() {
279 | // 0 = Sunday, 1 = Monday, ...., 6 = Saturday
280 | if (now.dayOfWeek() == 0 || now.dayOfWeek() == 6) return true;
281 | return false;
282 | }
283 |
284 | boolean isVacation() {
285 | for (int i = 0; i < numVacations; i++) {
286 | if(now.year() == vacations[i][0] && now.month() == vacations[i][1] && now.day() == vacations[i][2]) {
287 | return true;
288 | }
289 | }
290 | return false;
291 | }
292 |
293 | boolean isBetweenTime(uint8_t h0, uint8_t m0, uint8_t h1, uint8_t m1) {
294 | DateTime startTime (now.year(), now.month(), now.day(), h0, m0, 0);
295 | DateTime endTime (now.year(), now.month(), now.day(), h1, m1, 0);
296 | return (now.unixtime() >= startTime.unixtime() && now.unixtime() < endTime.unixtime());
297 | }
298 |
299 | boolean isLunch() {
300 | if (schedule[timeBlockIndex][4] == LUNCH) {
301 | if (DEBUG) Serial.println("Lunch!");
302 | return true;
303 | }
304 | return false;
305 | }
306 |
307 | boolean isAssembly() {
308 | if (schedule[timeBlockIndex][4] == ASSEMBLY) {
309 | if (DEBUG) Serial.println("Assembly!");
310 | return true;
311 | }
312 | return false;
313 | }
314 |
315 | boolean isAfterSchool() {
316 | // You may need to edit these values if school ends with a homeroom or
317 | // otherBlock[] that isn't a classperiod[]
318 | if (now.hour() >= schedule[numTimeBlocks - 1][2] && now.minute() >= schedule[numTimeBlocks - 1][3]) {
319 | if (DEBUG) Serial.println("After School!");
320 | return true;
321 | }
322 | return false;
323 | }
324 |
325 | boolean isBeforeSchool() {
326 | // You may need to edit these values if school begins with a homeroom or
327 | // otherBlock[] that isn't a classperiod[]
328 | if (now.hour() <= schedule[0][0] && now.minute() < schedule[0][1]) {
329 | if (DEBUG) Serial.println("Before School!");
330 | return true;
331 | }
332 | return false;
333 | }
334 |
335 | boolean isEndOfDay() {
336 | boolean changed = false;
337 | if (now.hour() == 0 && lastHour == 23) {
338 | changed = true;
339 | }
340 | lastHour = now.hour();
341 | return changed;
342 | }
343 |
344 | boolean isBetweenTimeBlocks() {
345 | return isDuringSchoolDay() && !isDuringTimeBlocks();
346 | }
347 |
348 | boolean isHourChange() {
349 | if (now.minute() == 0 && now.second() < 15) return true;
350 | return false;
351 | }
352 |
353 | boolean isEnd() {
354 | uint8_t h = schedule[currentPeriod][2];
355 | uint8_t m = schedule[currentPeriod][3];
356 | DateTime endTime (now.year(), now.month(), now.day(), h, m, 0);
357 | if (endTime.unixtime() - now.unixtime() < 7 * 60) return true;
358 | return false;
359 | }
360 |
361 | boolean isDuringSchoolDay() {
362 | return isSchoolDay() && !isAfterSchool() && !isBeforeSchool();
363 | }
364 |
365 | boolean isDuringClass() {
366 | for (int i = 0; i < numTimeBlocks; i++ ) {
367 | if (isBetweenTime(schedule[i][0], schedule[i][1], schedule[i][2], schedule[i][3])) {
368 | if (schedule[i][4] == ACADEMIC) {
369 | if (DEBUG) Serial.println("During class!");
370 | return true;
371 | }
372 | }
373 | }
374 | return false;
375 | }
376 |
377 | boolean isDuringTimeBlocks() {
378 | for (int i = 0; i < numTimeBlocks; i++ ) {
379 | if (isBetweenTime(schedule[i][0], schedule[i][1], schedule[i][2], schedule[i][3])) {
380 | if (DEBUG) Serial.println("During a time block!");
381 | return true;
382 | }
383 | }
384 | return false;
385 | }
386 |
387 | /////////////////////////////////////////////////////////
388 | /////////////////////////////////////////////////////////
389 | // DISPLAY
390 | /////////////////////////////////////////////////////////
391 |
392 | void displayColon(uint32_t c) {
393 | if (now.second() % 2 == 0) {
394 | strip.setPixelColor(21, c);
395 | strip.setPixelColor(22, c);
396 | }
397 | else {
398 | strip.setPixelColor(21, 0);
399 | strip.setPixelColor(22, 0);
400 | }
401 | }
402 |
403 | void countdownClock() {
404 | uint8_t h;
405 | uint8_t m;
406 | if (isDuringTimeBlocks()) {
407 | h = schedule[timeBlockIndex][2];
408 | m = schedule[timeBlockIndex][3];
409 | }
410 | else if (isBetweenTimeBlocks()) {
411 | h = schedule[timeBlockIndex][0];
412 | m = schedule[timeBlockIndex][1];
413 | }
414 | DateTime endTime(now.year(), now.month(), now.day(), h, m, 0);
415 | uint8_t minLeft = (endTime.unixtime() - now.unixtime()) / 60;
416 | uint8_t secLeft = (endTime.unixtime() - now.unixtime()) - minLeft * 60;
417 | if (minLeft == 0) displayHour(minLeft, 0);
418 | else displayHour(minLeft, Wheel(190));
419 | displayMinute(secLeft, Wheel(190));
420 | displayLetter(getLetter(), Wheel(190));
421 | displayColon(Wheel(190));
422 | strip.show();
423 | }
424 |
425 | void colorClock(int c) {
426 | displayHour(now.hour(), Wheel(c));
427 | displayMinute(now.minute(), Wheel(c));
428 | displayLetter(getLetter(), Wheel(c));
429 | displayColon(Wheel(c));
430 | strip.show();
431 | }
432 |
433 | void rainbowClock(int delayTime) {
434 | for (int j = 0; j < 256; j++) {
435 | displayHour(now.hour(), Wheel(j));
436 | displayMinute(now.minute(), Wheel(j));
437 | displayLetter(getLetter(), Wheel(j));
438 | displayColon(Wheel(j));
439 | strip.show();
440 | unsigned long t = millis();
441 | while (millis() - t < delayTime) {
442 | displayColon(Wheel(j));
443 | strip.show();
444 | }
445 | }
446 | }
447 |
448 | void birthdayClock(int delayTime) {
449 | displayHour(now.hour(), Wheel(random(0, 255)));
450 | displayMinute(now.minute(), Wheel(random(0, 255)));
451 | displayLetter(getLetter(), Wheel(random(0, 255)));
452 | displayColon(Wheel(random(0, 255)));
453 | unsigned long t = millis();
454 | strip.show();
455 | while (millis() - t < delayTime) displayColon(Wheel(random(0, 255)));
456 | }
457 |
458 | void xmasClock() {
459 | displayHour(now.hour(), Wheel(0));
460 | displayMinute(now.minute(), Wheel(80));
461 | displayLetter(getLetter(), Wheel(0));
462 | displayColon(Wheel(0));
463 | strip.show();
464 | }
465 |
466 | void mardiGrasClock() {
467 | displayHour(now.hour(), Wheel(200));
468 | displayMinute(now.minute(), Wheel(80));
469 | displayLetter(getLetter(), Wheel(50));
470 | displayColon(Wheel(50));
471 | strip.show();
472 | }
473 |
474 | void randoClock(int delayTime) {
475 | displayHour(now.hour(), Wheel(random(0, 255)));
476 | displayMinute(now.minute(), Wheel(random(0, 255)));
477 | displayLetter(getLetter(), Wheel(random(0, 255)));
478 | displayColon(Wheel(random(0, 255)));
479 | unsigned long t = millis();
480 | while (millis() - t < delayTime) displayColon(Wheel(random(0, 255)));
481 | }
482 |
483 | void pulseClock(uint32_t col, int delayTime) {
484 | for (int j = 255; j >= 0; j--) {
485 | displayHour(now.hour(), col);
486 | displayMinute(now.minute(), col);
487 | displayLetter(getLetter(), col);
488 | displayColon(col);
489 | strip.setBrightness(j);
490 | strip.show();
491 | unsigned long t = millis();
492 | while (millis() - t < delayTime) {
493 | displayColon(col);
494 | strip.show();
495 | }
496 | }
497 | for (int j = 0; j < 256; j++) {
498 | displayHour(now.hour(), col);
499 | displayMinute(now.minute(), col);
500 | displayLetter(getLetter(), col);
501 | displayColon(col);
502 | strip.setBrightness(j);
503 | strip.show();
504 | unsigned long t = millis();
505 | while (millis() - t < delayTime) {
506 | displayColon(col);
507 | strip.show();
508 | }
509 | }
510 | }
511 |
512 | void gradientClock() {
513 | uint32_t c = Wheel(getGradientColor(now.hour(), now.minute()));
514 | displayHour(now.hour(), c);
515 | displayMinute(now.minute(), c);
516 | displayColon(c);
517 | displayLetter(getLetter(), c);
518 | strip.show();
519 | }
520 |
521 | int getGradientColor(uint8_t h, uint8_t m) {
522 | // DateTime (year, month, day, hour, min, sec);
523 | uint8_t h0 = schedule[timeBlockIndex][0];
524 | uint8_t m0 = schedule[timeBlockIndex][1];
525 | uint8_t h1 = schedule[timeBlockIndex][2];
526 | uint8_t m1 = schedule[timeBlockIndex][3];
527 | DateTime startTime(now.year(), now.month(), now.day(), h0, m0, 0);
528 | DateTime endTime(now.year(), now.month(), now.day(), h1, m1, 0);
529 | if (DEBUG) {
530 | Serial.print("Gradient Clock: ");
531 | Serial.print(map(now.unixtime(), startTime.unixtime(), endTime.unixtime(), 0, 100));
532 | Serial.println("% through period");
533 | }
534 | return map(now.unixtime(), startTime.unixtime(), endTime.unixtime(), 80, 0);
535 | }
536 |
537 | void displayHour(uint8_t h, uint32_t col) {
538 | if (h == 0) h = 12;
539 | else if (h > 12) h -= 12;
540 | uint8_t firstDigit = h / 10;
541 | uint8_t secondDigit = h % 10;
542 | // TODO - this only works for non-military time
543 | // firstDigit is either off or all on (0 or 1)
544 | if (firstDigit > 0) {
545 | strip.setPixelColor(30, col);
546 | strip.setPixelColor(31, col);
547 | }
548 | else {
549 | strip.setPixelColor(30, 0);
550 | strip.setPixelColor(31, 0);
551 | }
552 | // secondDigit
553 | for (int i = 0; i < 7; i++) {
554 | if (numbers[secondDigit] & (1 << 7 - i)) strip.setPixelColor(i + (7 * 3 + 2), col);
555 | else strip.setPixelColor(i + (7 * 3 + 2), 0);
556 | }
557 | if (now.minute() / 10 == 2) strip.setPixelColor(14, 0);
558 | }
559 |
560 | void displayMinute(uint8_t m, uint32_t col) {
561 | uint8_t firstDigit = m / 10;
562 | uint8_t secondDigit = m % 10;
563 |
564 | // first digit (first from left to right)
565 | for (int i = 0; i < 7; i++) {
566 | if (numbers[firstDigit] & (1 << 7 - i)) strip.setPixelColor(i + (7 * 2), col);
567 | else {
568 | strip.setPixelColor(i + (7 * 2), 0);
569 | }
570 | }
571 | // second digit (least significant)
572 | for (int i = 0; i < 7; i++) {
573 | if (numbers[secondDigit] & (1 << 7 - i)) {
574 | strip.setPixelColor(i + 7, col);
575 | }
576 | else {
577 | strip.setPixelColor(i + 7, 0);
578 | }
579 | }
580 | }
581 |
582 | void displayLetter(uint8_t letter, uint32_t col) {
583 | // if (letter < 8) {
584 | // for (int i = 0; i < 7; i++) {
585 | // if (letters[letter] & (1 << 7 - i)) strip.setPixelColor(i, col);
586 | // else strip.setPixelColor(i, 0);
587 | // }
588 | // }
589 | // else {
590 | // for (int i = 0; i < 7; i++) {
591 | // strip.setPixelColor(i, 0);
592 | // }
593 | // }
594 | }
595 |
596 | // Input a value 0 to 255 to get a color value.
597 | // The colours are a transition r - g - b - back to r.
598 | // credit - Adafruit
599 | uint32_t Wheel(byte WheelPos) {
600 | WheelPos = 255 - WheelPos;
601 | if (WheelPos < 85) {
602 | return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
603 | } else if (WheelPos < 170) {
604 | WheelPos -= 85;
605 | return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
606 | } else {
607 | WheelPos -= 170;
608 | return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
609 | }
610 | }
611 |
612 | /////////////////////////////////////////////////////////
613 | /////////////////////////////////////////////////////////
614 | // GET/SET
615 | /////////////////////////////////////////////////////////
616 | // credit: Adafruit
617 | // Use this function to automatically set an initial DateTime
618 | // using time from the computer/ compiler
619 | void initChronoDot() {
620 | // Instantiate the RTC
621 | Wire.begin();
622 | RTC.begin();
623 | // Check if the RTC is running.
624 | if (DEBUG) {
625 | if (! RTC.isrunning()) {
626 | Serial.println("RTC is NOT running");
627 | }
628 | }
629 | // This section grabs the current datetime and compares it to
630 | // the compilation time. If necessary, the RTC is updated.
631 | now = RTC.now();
632 | DateTime compiled = DateTime(__DATE__, __TIME__);
633 | if (now.unixtime() < compiled.unixtime()) {
634 | if (DEBUG) Serial.println("RTC is older than compile time! Updating");
635 | RTC.adjust(DateTime(__DATE__, __TIME__));
636 | }
637 | now = RTC.now();
638 | if (DEBUG) Serial.println("Setup complete.");
639 | }
640 |
641 | // Use this function to set a specific initial DateTime
642 | // useful for debugging
643 | void initChronoDot(int y, int mon, int d, int h, int minu, int s) {
644 | // Instantiate the RTC
645 | Wire.begin();
646 | RTC.begin();
647 | // Check if the RTC is running.
648 | if (! RTC.isrunning()) {
649 | Serial.println("RTC is NOT running");
650 | }
651 | now = RTC.now();
652 | RTC.adjust(DateTime(y, mon, d, h, minu, s));
653 | now = RTC.now();
654 | if (DEBUG) Serial.println("Setup complete.");
655 | }
656 |
657 | uint8_t getLetter() {
658 | if (extraDigitMode == 0) return 9;
659 | else if (!isSchoolDay()) return 9;
660 | else if (isAfterSchool()) return 9;
661 | else if (extraDigitMode == 2) return currentPeriod + 1;
662 | else return currentBlock;
663 | }
664 |
665 | void setSchedule() {
666 | currentPeriod = getCurrentPeriod();
667 | timeBlockIndex = getCurrentTimeBlock();
668 | if (DEBUG) {
669 | Serial.print("Current (starting) period is set to: ");
670 | Serial.println(currentPeriod);
671 | Serial.print("Current (starting) time block index is set to: ");
672 | Serial.println(timeBlockIndex);
673 | }
674 |
675 | }
676 |
677 |
678 | void nextDay() {
679 | if (isSchoolDay()) {
680 | currentPeriod = 0;
681 | timeBlockIndex = 0;
682 | }
683 | }
684 |
685 | void printClock() {
686 | if (DEBUG) {
687 | Serial.print(now.hour());
688 | Serial.print(":");
689 | Serial.print(now.minute());
690 | Serial.print(":");
691 | Serial.println(now.second());
692 | }
693 | }
694 |
--------------------------------------------------------------------------------
/customizations/toneClassroomClock/toneClassroomClock.ino:
--------------------------------------------------------------------------------
1 | /////////////////////////////////////////////////////////
2 | /*
3 | Jenna deBoisblanc
4 | 2016
5 | http://jdeboi.com/
6 |
7 | CLASSROOM CLOCK with tone()
8 | A customization to the CLASSROOM CLOCK: adding a sound
9 | when the countdown timer is triggered.
10 |
11 | */
12 | /////////////////////////////////////////////////////////
13 |
14 | #include "pitches.h"
15 |
16 | #include "classroomClock.h"
17 | #include
18 | #include
19 | #include "RTClib.h" // https://github.com/adafruit/RTClib
20 | #include
21 | #include
22 | #include // https://github.com/adafruit/Adafruit_NeoPixel
23 |
24 | #define PIN 3
25 | #define NUM_PIXELS 32
26 |
27 | #define DEBUG false
28 | #define SPEED_CLOCK true
29 |
30 | Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, PIN, NEO_GRB + NEO_KHZ800);
31 | RTC_DS1307 RTC;
32 | DateTime now;
33 | uint8_t currentPeriod = 0;
34 | uint8_t lastHour = 0;
35 | uint8_t timeBlockIndex = 0;
36 |
37 | DateTime lastFlash; // for countdown "flash"
38 | boolean flashOn = false;
39 | long lastSpeedTest = 0;
40 | boolean playedTone = false;
41 |
42 | byte numbers[] = {
43 | B11101110, // 0
44 | B10001000, // 1
45 | B01111100, // 2 5555
46 | B11011100, // 3 6 4
47 | B10011010, // 4 3333
48 | B11010110, // 5 2 0
49 | B11110110, // 6 1111
50 | B10001100, // 7
51 | B11111110, // 8
52 | B10011110 // 9
53 | };
54 | byte letters[] = {
55 | B10111110, // A 55555
56 | B11110010, // B 6 4
57 | B01110000, // C 6 4
58 | B11111000, // D 33333
59 | B01110110, // E 2 0
60 | B00110110, // F 2 0
61 | B11011110, // G 11111
62 | B10111010, // H
63 | B01100010, // L
64 | B00000000 //
65 | };
66 |
67 | /////////////////////////////////////////////////////////
68 | /////////////////////////////////////////////////////////
69 | // CUSTOMIZE THIS STUFF
70 | /////////////////////////////////////////////////////////
71 |
72 | /*
73 | Set the functionality of the extra digit
74 | show nothing in the extra digit is mode 0
75 | show rotating block is mode 1
76 | show period number is mode 2
77 | */
78 | int extraDigitMode = 1;
79 |
80 | // setup for a rotating block schedule
81 | uint8_t currentBlock = A_BLOCK;
82 |
83 | // this number should match the number of entries in schedule[]
84 | const uint8_t numTimeBlocks = 9;
85 |
86 | uint8_t schedule[numTimeBlocks][5] = {
87 | // use 24 hour clock numbers even though this clock is a 12 hour clock
88 | // {starting hour, start min, end hour, end min}
89 | {8, 0, 9, 0, ACADEMIC}, // 0 - Period 1: 8 - 9
90 | {9, 3, 9, 48, ACADEMIC}, // 1 - Period 2: 9:03 - 9:48
91 | {9, 48, 10, 15, ASSEMBLY}, // 2 - Assembly: 9:48 - 10:18
92 | {10, 18, 11, 3, ACADEMIC}, // 3 - Period 3: 10:18 - 11:03
93 | {11, 6, 11, 51, ACADEMIC}, // 4 - Period 4: 11:06 - 11:51
94 | {11, 54, 12, 39, ACADEMIC}, // 5 - Period 5: 11:54 - 12:39
95 | {12, 39, 13, 24, LUNCH}, // 6 - Lunch: 12:39 - 1:24
96 | {13, 27, 14, 12, ACADEMIC}, // 7 - Period 6: 1:27 - 2:12
97 | {14, 15, 15, 0, ACADEMIC} // 8 - Period 7: 2:15 - 3:00
98 | };
99 |
100 | // this number should match the number of entries in vacations[]
101 | const uint8_t numVacations = 3;
102 | uint8_t vacations[numVacations][3] = {
103 | {2016, 1, 10},
104 | {2016, 2, 20},
105 | {2016, 3, 30}
106 | };
107 |
108 | // these are the array indicies of non-academic time blocks
109 | const uint8_t assemblyBlock = 2;
110 | const uint8_t lunchBlock = 6;
111 |
112 | // number of minutes before end of class when countdown clock is triggered
113 | uint8_t countdownM = 6;
114 | uint8_t secBetweenFlashes = 4;
115 |
116 | /////////////////////////////////////////////////////////
117 | /////////////////////////////////////////////////////////
118 | // THE GUTS
119 | /////////////////////////////////////////////////////////
120 | void setup() {
121 | Serial.begin(57600);
122 | /*
123 | For testing purposes, you can set the clock to custom values, e.g.:
124 | initChronoDot(year, month, day, hour, minute, seconds);
125 | Otherwise, the clock automatically sets itself to your computer's
126 | time with the function initChronoDot();
127 | */
128 | //initChronoDot(2016, 11, 7, 8, 54, 50);
129 | initChronoDot();
130 | strip.begin();
131 | strip.show();
132 | setSchedule();
133 | delay(3000);
134 | }
135 |
136 | void loop() {
137 | now = RTC.now();
138 | if (isEndOfDay()) nextDay();
139 | checkBlock();
140 | displayClock();
141 | }
142 |
143 | /////////////////////////////////////////////////////////
144 | /////////////////////////////////////////////////////////
145 | // CUSTOMIZE DISPLAY FUNCTIONALITY
146 | /////////////////////////////////////////////////////////
147 |
148 | void displayClock() {
149 | if (!isSchoolDay()) colorClock(Wheel(0));
150 | else if (isBeforeSchool()) pulseClock(Wheel(40), 5);
151 | else if (isAfterSchool()) colorClock(Wheel(100));
152 | else if (isEndFlash()) countdownClock();
153 | else if (isLunch()) birthdayClock(300);
154 | else if (isAssembly()) rainbowClock(5);
155 | else if (isDuringClass()) gradientClock();
156 | else {
157 | //between classes
158 | mardiGrasClock();
159 | }
160 | }
161 |
162 |
163 | /////////////////////////////////////////////////////////
164 | /////////////////////////////////////////////////////////
165 | // CHECK
166 | /////////////////////////////////////////////////////////
167 | void checkBlock() {
168 | if (timeBlockIndex < numTimeBlocks) {
169 | uint8_t h = schedule[timeBlockIndex][2];
170 | uint8_t m = schedule[timeBlockIndex][3];
171 | if (isAfterTime(now.hour(), now.minute(), h, m)) {
172 | if (schedule[timeBlockIndex][4] == ACADEMIC) {
173 | currentPeriod++;
174 | currentBlock++;
175 | if (currentBlock == 8) currentBlock = 0;
176 | if (DEBUG) {
177 | Serial.print("Academic period changed to: ");
178 | Serial.print(currentPeriod);
179 | Serial.print(" & block changed to: ");
180 | Serial.println(currentBlock);
181 | }
182 | }
183 | timeBlockIndex++;
184 | if (DEBUG) {
185 | Serial.print("Time block index: ");
186 | Serial.println(timeBlockIndex);
187 | }
188 | }
189 | }
190 | }
191 |
192 | int getCurrentPeriod() {
193 | int period = -1;
194 | if (isBeforeSchool() || isAfterSchool()) period = 0;
195 | // check if it's before the end of an academic period
196 | // if it's not during an academic period, find the next academic period
197 | for (int i = 0; i < numTimeBlocks; i++ ) {
198 | if (schedule[i][4] == ACADEMIC) {
199 | period++;
200 | }
201 | if (isBeforeTime(now.hour(), now.minute(), schedule[i][2], schedule[i][3])) {
202 | break;
203 | }
204 | }
205 | return period;
206 | }
207 |
208 | int getCurrentTimeBlock() {
209 | int tb = 0;
210 | if (isBeforeSchool() || isAfterSchool()) tb = 0;
211 | // check if it's before the end of an academic period
212 | // if it's not during an academic period, find the next academic period
213 | for (int i = 0; i < numTimeBlocks; i++ ) {
214 | if (isBeforeTime(now.hour(), now.minute(), schedule[i][2], schedule[i][3])) {
215 | return i;
216 | }
217 | }
218 | return tb;
219 | }
220 |
221 | // returns true if the first number is before the
222 | boolean isBeforeTime(uint8_t h0, uint8_t m0, uint8_t h1, uint8_t m1) {
223 | if (timeDiff(h0, m0, h1, m1) < 0) return true;
224 | return false;
225 | }
226 |
227 | boolean isAfterTime(uint8_t h0, uint8_t m0, uint8_t h1, uint8_t m1) {
228 | if (timeDiff(h0, m0, h1, m1) >= 0) return true;
229 | return false;
230 | }
231 |
232 | boolean isAfterTime(uint8_t h0, uint8_t m0, uint8_t s0, uint8_t h1, uint8_t m1, uint8_t s1) {
233 | if (timeDiff(h0, m0, s0, h1, m1, s0) >= 0) return true;
234 | return false;
235 | }
236 |
237 | // returns the difference in minutes
238 | // later time first
239 | int timeDiff(uint8_t h0, uint8_t m0, uint8_t h1, uint8_t m1) {
240 | uint16_t t0 = h0 * 60 + m0;
241 | uint16_t t1 = h1 * 60 + m1;
242 | return t0 - t1;
243 | }
244 |
245 | // returns the difference in seconds
246 | // later time first
247 | int timeDiff(uint8_t h0, uint8_t m0, uint8_t s0, uint8_t h1, uint8_t m1, uint8_t s1) {
248 | uint16_t t0 = h0 * 60 * 60 + m0 * 60 + s0;
249 | uint16_t t1 = h1 * 60 * 60 + m1 * 60 + s1;
250 | return t0 - t1;
251 | }
252 |
253 | boolean isEndFlash() {
254 | uint8_t h;
255 | uint8_t m;
256 | if (isDuringTimeBlocks()) {
257 | h = schedule[timeBlockIndex][2];
258 | m = schedule[timeBlockIndex][3];
259 | }
260 | else if (isBetweenTimeBlocks()) {
261 | h = schedule[timeBlockIndex][0];
262 | m = schedule[timeBlockIndex][1];
263 | }
264 | else {
265 | return false;
266 | }
267 | if (timeDiff(h, m, now.hour(), now.minute()) < countdownM) {
268 | if (!playedTone) {
269 | Serial.println("tone!");
270 | alarm();
271 | playedTone = true;
272 | }
273 | if (DEBUG) {
274 | Serial.print("End Flash: ");
275 | Serial.print(timeDiff( h, m, now.hour(), now.minute()));
276 | Serial.println(" minutes remaining");
277 | }
278 | if (now.unixtime() - lastFlash.unixtime() > secBetweenFlashes) {
279 | lastFlash = now;
280 | flashOn = !flashOn;
281 | }
282 | return flashOn;
283 | }
284 | playedTone = false;
285 | return false;
286 | }
287 |
288 | boolean isSchoolDay() {
289 | // 0 = Sunday, 1 = Monday, ...., 6 = Saturday
290 | if (now.dayOfWeek() == 0 || now.dayOfWeek() == 6) {
291 | if (DEBUG) Serial.println("Weekend!");
292 | return false;
293 | }
294 | else if (isVacation()) return false;
295 | return true;
296 | }
297 |
298 | boolean isVacation() {
299 | for (int i = 0; i < numVacations; i++) {
300 | if (now.year() == vacations[i][0] && now.month() == vacations[i][1] && now.day() == vacations[i][2]) {
301 | return true;
302 | }
303 | }
304 | return false;
305 | }
306 |
307 | boolean isBetweenTime(uint8_t h0, uint8_t m0, uint8_t h1, uint8_t m1) {
308 | DateTime startTime (now.year(), now.month(), now.day(), h0, m0, 0);
309 | DateTime endTime (now.year(), now.month(), now.day(), h1, m1, 0);
310 | return (now.unixtime() >= startTime.unixtime() && now.unixtime() < endTime.unixtime());
311 | }
312 |
313 | boolean isAssembly() {
314 | if (isBetweenTime(schedule[assemblyBlock][0], schedule[assemblyBlock][1], schedule[assemblyBlock][2], schedule[assemblyBlock][3])) {
315 | if (DEBUG) Serial.println("Assembly!");
316 | return true;
317 | }
318 | return false;
319 | }
320 |
321 | boolean isLunch() {
322 | if (isBetweenTime(schedule[lunchBlock][0], schedule[lunchBlock][1], schedule[lunchBlock][2], schedule[lunchBlock][3])) {
323 | if (DEBUG) Serial.println("Lunch!");
324 | return true;
325 | }
326 | return false;
327 | }
328 |
329 | boolean isAfterSchool() {
330 | // You may need to edit these values if school ends with a homeroom or
331 | // otherBlock[] that isn't a classperiod[]
332 | if (now.hour() >= schedule[numTimeBlocks - 1][2] && now.minute() >= schedule[numTimeBlocks - 1][3]) {
333 | if (DEBUG) Serial.println("After School!");
334 | return true;
335 | }
336 | return false;
337 | }
338 |
339 | boolean isBeforeSchool() {
340 | // You may need to edit these values if school begins with a homeroom or
341 | // otherBlock[] that isn't a classperiod[]
342 | if (now.hour() <= schedule[0][0] && now.minute() < schedule[0][1]) {
343 | if (DEBUG) Serial.println("Before School!");
344 | return true;
345 | }
346 | return false;
347 | }
348 |
349 | boolean isEndOfDay() {
350 | boolean changed = false;
351 | if (now.hour() == 0 && lastHour == 23) {
352 | changed = true;
353 | }
354 | lastHour = now.hour();
355 | return changed;
356 | }
357 |
358 | boolean isBetweenTimeBlocks() {
359 | return isDuringSchoolDay() && !isDuringTimeBlocks();
360 | }
361 |
362 | boolean isHourChange() {
363 | if (now.minute() == 0 && now.second() < 15) return true;
364 | return false;
365 | }
366 |
367 | boolean isDuringSchoolDay() {
368 | return isSchoolDay() && !isAfterSchool() && !isBeforeSchool();
369 | }
370 |
371 | boolean isDuringClass() {
372 | for (int i = 0; i < numTimeBlocks; i++ ) {
373 | if (isBetweenTime(schedule[i][0], schedule[i][1], schedule[i][2], schedule[i][3])) {
374 | if (schedule[i][4] == ACADEMIC) {
375 | if (DEBUG) Serial.println("During class!");
376 | return true;
377 | }
378 | }
379 | }
380 | return false;
381 | }
382 |
383 | boolean isDuringTimeBlocks() {
384 | for (int i = 0; i < numTimeBlocks; i++ ) {
385 | if (isBetweenTime(schedule[i][0], schedule[i][1], schedule[i][2], schedule[i][3])) {
386 | if (DEBUG) Serial.println("During a time block!");
387 | return true;
388 | }
389 | }
390 | return false;
391 | }
392 |
393 | /////////////////////////////////////////////////////////
394 | /////////////////////////////////////////////////////////
395 | // DISPLAY
396 | /////////////////////////////////////////////////////////
397 |
398 | void displayColon(uint32_t c) {
399 | if (now.second() % 2 == 0) {
400 | strip.setPixelColor(21, c);
401 | strip.setPixelColor(22, c);
402 | }
403 | else {
404 | strip.setPixelColor(21, 0);
405 | strip.setPixelColor(22, 0);
406 | }
407 | }
408 |
409 | void countdownClock() {
410 | uint8_t h;
411 | uint8_t m;
412 | if (isDuringTimeBlocks()) {
413 | h = schedule[timeBlockIndex][2];
414 | m = schedule[timeBlockIndex][3];
415 | }
416 | else if (isBetweenTimeBlocks()) {
417 | h = schedule[timeBlockIndex][0];
418 | m = schedule[timeBlockIndex][1];
419 | }
420 | DateTime endTime(now.year(), now.month(), now.day(), h, m, 0);
421 | uint8_t minLeft = (endTime.unixtime() - now.unixtime()) / 60;
422 | uint8_t secLeft = (endTime.unixtime() - now.unixtime()) - minLeft * 60;
423 | if (minLeft == 0) displayHour(minLeft, 0);
424 | else displayHour(minLeft, Wheel(190));
425 | displayMinute(secLeft, Wheel(190));
426 | displayLetter(getLetter(), Wheel(190));
427 | displayColon(Wheel(190));
428 | strip.show();
429 | }
430 |
431 | void colorClock(int c) {
432 | displayHour(now.hour(), Wheel(c));
433 | displayMinute(now.minute(), Wheel(c));
434 | displayLetter(getLetter(), Wheel(c));
435 | displayColon(Wheel(c));
436 | strip.show();
437 | }
438 |
439 | void rainbowClock(int delayTime) {
440 | for (int j = 0; j < 256; j++) {
441 | displayHour(now.hour(), Wheel(j));
442 | displayMinute(now.minute(), Wheel(j));
443 | displayLetter(getLetter(), Wheel(j));
444 | displayColon(Wheel(j));
445 | strip.show();
446 | unsigned long t = millis();
447 | while (millis() - t < delayTime) {
448 | displayColon(Wheel(j));
449 | strip.show();
450 | }
451 | }
452 | }
453 |
454 | void birthdayClock(int delayTime) {
455 | displayHour(now.hour(), Wheel(random(0, 255)));
456 | displayMinute(now.minute(), Wheel(random(0, 255)));
457 | displayLetter(getLetter(), Wheel(random(0, 255)));
458 | displayColon(Wheel(random(0, 255)));
459 | unsigned long t = millis();
460 | strip.show();
461 | while (millis() - t < delayTime) displayColon(Wheel(random(0, 255)));
462 | }
463 |
464 | void xmasClock() {
465 | displayHour(now.hour(), Wheel(0));
466 | displayMinute(now.minute(), Wheel(80));
467 | displayLetter(getLetter(), Wheel(0));
468 | displayColon(Wheel(0));
469 | strip.show();
470 | }
471 |
472 | void mardiGrasClock() {
473 | displayHour(now.hour(), Wheel(200));
474 | displayMinute(now.minute(), Wheel(80));
475 | displayLetter(getLetter(), Wheel(50));
476 | displayColon(Wheel(50));
477 | strip.show();
478 | }
479 |
480 | void randoClock(int delayTime) {
481 | displayHour(now.hour(), Wheel(random(0, 255)));
482 | displayMinute(now.minute(), Wheel(random(0, 255)));
483 | displayLetter(getLetter(), Wheel(random(0, 255)));
484 | displayColon(Wheel(random(0, 255)));
485 | unsigned long t = millis();
486 | while (millis() - t < delayTime) displayColon(Wheel(random(0, 255)));
487 | }
488 |
489 | void pulseClock(uint32_t col, int delayTime) {
490 | for (int j = 255; j >= 0; j--) {
491 | displayHour(now.hour(), col);
492 | displayMinute(now.minute(), col);
493 | displayLetter(getLetter(), col);
494 | displayColon(col);
495 | strip.setBrightness(j);
496 | strip.show();
497 | unsigned long t = millis();
498 | while (millis() - t < delayTime) {
499 | displayColon(col);
500 | strip.show();
501 | }
502 | }
503 | for (int j = 0; j < 256; j++) {
504 | displayHour(now.hour(), col);
505 | displayMinute(now.minute(), col);
506 | displayLetter(getLetter(), col);
507 | displayColon(col);
508 | strip.setBrightness(j);
509 | strip.show();
510 | unsigned long t = millis();
511 | while (millis() - t < delayTime) {
512 | displayColon(col);
513 | strip.show();
514 | }
515 | }
516 | }
517 |
518 | void gradientClock() {
519 | uint32_t c = Wheel(getGradientColor(now.hour(), now.minute()));
520 | displayHour(now.hour(), c);
521 | displayMinute(now.minute(), c);
522 | displayColon(c);
523 | displayLetter(getLetter(), c);
524 | strip.show();
525 | }
526 |
527 | int getGradientColor(uint8_t h, uint8_t m) {
528 | // DateTime (year, month, day, hour, min, sec);
529 | uint8_t h0 = schedule[timeBlockIndex][0];
530 | uint8_t m0 = schedule[timeBlockIndex][1];
531 | uint8_t h1 = schedule[timeBlockIndex][2];
532 | uint8_t m1 = schedule[timeBlockIndex][3];
533 | DateTime startTime(now.year(), now.month(), now.day(), h0, m0, 0);
534 | DateTime endTime(now.year(), now.month(), now.day(), h1, m1, 0);
535 | if (DEBUG) {
536 | Serial.print("Gradient Clock: ");
537 | Serial.print(map(now.unixtime(), startTime.unixtime(), endTime.unixtime(), 0, 100));
538 | Serial.println("% through period");
539 | }
540 | return map(now.unixtime(), startTime.unixtime(), endTime.unixtime(), 80, 0);
541 | }
542 |
543 | void displayHour(uint8_t h, uint32_t col) {
544 | if (h == 0) h = 12;
545 | else if (h > 12) h -= 12;
546 | uint8_t firstDigit = h / 10;
547 | uint8_t secondDigit = h % 10;
548 | // TODO - this only works for non-military time
549 | // firstDigit is either off or all on (0 or 1)
550 | if (firstDigit > 0) {
551 | strip.setPixelColor(30, col);
552 | strip.setPixelColor(31, col);
553 | }
554 | else {
555 | strip.setPixelColor(30, 0);
556 | strip.setPixelColor(31, 0);
557 | }
558 | // secondDigit
559 | for (int i = 0; i < 7; i++) {
560 | if (numbers[secondDigit] & (1 << 7 - i)) strip.setPixelColor(i + (7 * 3 + 2), col);
561 | else strip.setPixelColor(i + (7 * 3 + 2), 0);
562 | }
563 | if (now.minute() / 10 == 2) strip.setPixelColor(14, 0);
564 | }
565 |
566 | void displayMinute(uint8_t m, uint32_t col) {
567 | uint8_t firstDigit = m / 10;
568 | uint8_t secondDigit = m % 10;
569 |
570 | // first digit (first from left to right)
571 | for (int i = 0; i < 7; i++) {
572 | if (numbers[firstDigit] & (1 << 7 - i)) strip.setPixelColor(i + (7 * 2), col);
573 | else {
574 | strip.setPixelColor(i + (7 * 2), 0);
575 | }
576 | }
577 | // second digit (least significant)
578 | for (int i = 0; i < 7; i++) {
579 | if (numbers[secondDigit] & (1 << 7 - i)) {
580 | strip.setPixelColor(i + 7, col);
581 | }
582 | else {
583 | strip.setPixelColor(i + 7, 0);
584 | }
585 | }
586 | }
587 |
588 | void displayLetter(uint8_t letter, uint32_t col) {
589 | if (letter < 8) {
590 | for (int i = 0; i < 7; i++) {
591 | if (letters[letter] & (1 << 7 - i)) strip.setPixelColor(i, col);
592 | else strip.setPixelColor(i, 0);
593 | }
594 | }
595 | else {
596 | for (int i = 0; i < 7; i++) {
597 | strip.setPixelColor(i, 0);
598 | }
599 | }
600 | }
601 |
602 | // Input a value 0 to 255 to get a color value.
603 | // The colours are a transition r - g - b - back to r.
604 | // credit - Adafruit
605 | uint32_t Wheel(byte WheelPos) {
606 | WheelPos = 255 - WheelPos;
607 | if (WheelPos < 85) {
608 | return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
609 | } else if (WheelPos < 170) {
610 | WheelPos -= 85;
611 | return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
612 | } else {
613 | WheelPos -= 170;
614 | return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
615 | }
616 | }
617 |
618 | /////////////////////////////////////////////////////////
619 | /////////////////////////////////////////////////////////
620 | // GET/SET
621 | /////////////////////////////////////////////////////////
622 | // credit: Adafruit
623 | // Use this function to automatically set an initial DateTime
624 | // using time from the computer/ compiler
625 | void initChronoDot() {
626 | // Instantiate the RTC
627 | Wire.begin();
628 | RTC.begin();
629 | // Check if the RTC is running.
630 | if (DEBUG) {
631 | if (! RTC.isrunning()) {
632 | Serial.println("RTC is NOT running");
633 | }
634 | }
635 | // This section grabs the current datetime and compares it to
636 | // the compilation time. If necessary, the RTC is updated.
637 | now = RTC.now();
638 | DateTime compiled = DateTime(__DATE__, __TIME__);
639 | if (now.unixtime() < compiled.unixtime()) {
640 | if (DEBUG) Serial.println("RTC is older than compile time! Updating");
641 | RTC.adjust(DateTime(__DATE__, __TIME__));
642 | }
643 | now = RTC.now();
644 | if (DEBUG) Serial.println("Setup complete.");
645 | }
646 |
647 | // Use this function to set a specific initial DateTime
648 | // useful for debugging
649 | void initChronoDot(int y, int mon, int d, int h, int minu, int s) {
650 | // Instantiate the RTC
651 | Wire.begin();
652 | RTC.begin();
653 | // Check if the RTC is running.
654 | if (! RTC.isrunning()) {
655 | Serial.println("RTC is NOT running");
656 | }
657 | now = RTC.now();
658 | RTC.adjust(DateTime(y, mon, d, h, minu, s));
659 | now = RTC.now();
660 | if (DEBUG) Serial.println("Setup complete.");
661 | }
662 |
663 | uint8_t getLetter() {
664 | if (extraDigitMode == 0) return 9;
665 | else if (!isSchoolDay()) return 9;
666 | else if (isAfterSchool()) return 9;
667 | else if (extraDigitMode == 2) return currentPeriod + 1;
668 | else return currentBlock;
669 | }
670 |
671 | void setSchedule() {
672 | currentPeriod = getCurrentPeriod();
673 | timeBlockIndex = getCurrentTimeBlock();
674 | if (DEBUG) {
675 | Serial.print("Current (starting) period is set to: ");
676 | Serial.println(currentPeriod);
677 | Serial.print("Current (starting) time block index is set to: ");
678 | Serial.println(timeBlockIndex);
679 | }
680 |
681 | }
682 |
683 |
684 | void nextDay() {
685 | if (isSchoolDay()) {
686 | currentPeriod = 0;
687 | timeBlockIndex = 0;
688 | }
689 | }
690 |
691 | void printClock() {
692 | if (DEBUG) {
693 | Serial.print(now.hour());
694 | Serial.print(":");
695 | Serial.print(now.minute());
696 | Serial.print(":");
697 | Serial.println(now.second());
698 | }
699 | }
700 |
701 | /*
702 | * Pass a time that you'd like the alarm to go off
703 | * as well as the amount of time (alarmSeconds) that you'd like it to go off
704 | */
705 | void checkCustomAlarm(int hr, int mn, int sec, int alarmSeconds) {
706 | DateTime startAlarm (now.year(), now.month(), now.day(), hr, mn, sec);
707 | if (now.unixtime() > startAlarm.unixtime() && now.unixtime() < startAlarm.unixtime() + alarmSeconds) {
708 | alarm();
709 | }
710 | }
711 |
712 | void alarm() {
713 | int melody [] = {NOTE_G6, NOTE_B6, NOTE_A6, NOTE_G6, NOTE_B5};
714 | for (int i = 0; i < 5; i++) {
715 | tone(8, melody[i], 500);
716 | delay(800);
717 | }
718 | noTone(8);
719 | }
720 |
721 |
--------------------------------------------------------------------------------
/Laser Files/2_neopixels.svg:
--------------------------------------------------------------------------------
1 |
2 |
1201 |
--------------------------------------------------------------------------------