├── 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 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 49 | H:\Makerspace\Jenna\Clock\FINAL_CLOCK\acry.dxf - scale = 1.000000, origin = (0.000000, 0.000000), auto = False 51 | 53 | 59 | 64 | 65 | 72 | 78 | 84 | 90 | 91 | 93 | 95 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /Laser Files/0_bottom.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 49 | H:\Makerspace\Jenna\Clock\clock_final1.dxf - scale = 1.000000, origin = (0.000000, 0.000000), auto = False 51 | 53 | 59 | 64 | 65 | 72 | 78 | 84 | 90 | 91 | 93 | 95 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 167 | 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 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 48 | H:\Makerspace\Jenna\Clock\FINAL_CLOCK\acryl_disc2.dxf - scale = 1.000000, origin = (0.000000, 0.000000), auto = False 50 | 52 | 58 | 62 | 63 | 70 | 76 | 82 | 88 | 89 | 91 | 93 | 94 | 98 | 101 | 106 | 111 | 112 | 115 | 120 | 125 | 126 | 129 | 134 | 139 | 140 | 143 | 148 | 153 | 154 | 159 | 164 | 166 | 169 | 174 | 179 | 180 | 183 | 188 | 193 | 194 | 197 | 202 | 207 | 208 | 211 | 216 | 221 | 222 | 227 | 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /Laser Files/1_bumper.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 49 | H:\Makerspace\Jenna\Clock\clock_final1.dxf - scale = 1.000000, origin = (0.000000, 0.000000), auto = False 51 | 53 | 59 | 64 | 65 | 72 | 78 | 84 | 90 | 91 | 93 | 95 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 176 | 181 | 186 | 191 | 196 | 201 | 206 | 211 | 216 | 221 | 226 | 231 | 236 | 241 | 246 | 251 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /Laser Files/circuit_case.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 49 | H:\Makerspace\Jenna\Clock\circuit_holder4.dxf - scale = 1.000000, origin = (0.000000, 0.000000), auto = False 51 | 53 | 59 | 64 | 65 | 72 | 78 | 84 | 90 | 91 | 93 | 95 | 96 | 101 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 175 | 176 | 180 | 185 | 190 | 195 | 200 | 205 | 210 | 215 | 220 | 225 | 230 | 235 | 240 | 241 | 245 | 251 | 256 | 257 | 262 | 267 | 272 | 277 | 282 | 287 | 292 | 297 | 302 | 307 | 312 | 317 | 322 | 327 | 332 | 337 | 342 | 347 | 352 | 357 | 362 | 367 | 368 | 369 | -------------------------------------------------------------------------------- /Laser Files/3_4_digits.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 49 | H:\Makerspace\Jenna\Clock\clock_final1.dxf - scale = 1.000000, origin = (0.000000, 0.000000), auto = False 51 | 53 | 59 | 64 | 65 | 72 | 78 | 84 | 90 | 91 | 93 | 95 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 176 | 181 | 186 | 191 | 196 | 201 | 206 | 211 | 216 | 221 | 226 | 231 | 236 | 241 | 246 | 251 | 256 | 261 | 266 | 271 | 276 | 281 | 286 | 291 | 296 | 301 | 306 | 311 | 316 | 321 | 326 | 331 | 336 | 341 | 346 | 347 | 348 | -------------------------------------------------------------------------------- /Laser Files/5_nut_digits.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 49 | H:\Makerspace\Jenna\Clock\clock_final1.dxf - scale = 1.000000, origin = (0.000000, 0.000000), auto = False 51 | 53 | 59 | 64 | 65 | 72 | 78 | 84 | 90 | 91 | 93 | 95 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 176 | 181 | 186 | 191 | 196 | 201 | 206 | 211 | 216 | 221 | 226 | 231 | 236 | 241 | 246 | 251 | 256 | 261 | 266 | 271 | 276 | 281 | 286 | 291 | 296 | 301 | 306 | 311 | 316 | 321 | 326 | 331 | 336 | 341 | 346 | 347 | 348 | -------------------------------------------------------------------------------- /Laser Files/6_magnet_digits.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 49 | H:\Makerspace\Jenna\Clock\clock_final1.dxf - scale = 1.000000, origin = (0.000000, 0.000000), auto = False 51 | 53 | 59 | 64 | 65 | 72 | 78 | 84 | 90 | 91 | 93 | 95 | 97 | 99 | 106 | 112 | 118 | 124 | 125 | 131 | 136 | 137 | 138 | 143 | 147 | 152 | 157 | 162 | 167 | 172 | 177 | 182 | 187 | 192 | 197 | 202 | 207 | 212 | 213 | 218 | 223 | 228 | 233 | 238 | 243 | 248 | 253 | 258 | 263 | 268 | 273 | 278 | 283 | 288 | 293 | 298 | 303 | 308 | 313 | 318 | 323 | 328 | 333 | 338 | 343 | 348 | 353 | 358 | 363 | 368 | 373 | 378 | 383 | 388 | 393 | 394 | 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 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 29 | 50 | H:\Makerspace\Jenna\Clock\clock_v2.dxf - scale = 1.000000, origin = (0.000000, 0.000000), auto = False 52 | 54 | 57 | 61 | 62 | 68 | 73 | 74 | 81 | 87 | 93 | 99 | 100 | 102 | 104 | 110 | 115 | 116 | 123 | 129 | 135 | 141 | 142 | 144 | 146 | 147 | 152 | 156 | 161 | 166 | 171 | 176 | 181 | 186 | 191 | 196 | 201 | 206 | 211 | 216 | 221 | 226 | 231 | 236 | 241 | 246 | 14 0 1 2 3 4 5 6 7 8 9 10 11 12 13 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 635 | 640 | 645 | 650 | 655 | 660 | 665 | 670 | 675 | 680 | 685 | 690 | 695 | 700 | 705 | 710 | 715 | 720 | 725 | 730 | 735 | 740 | 745 | 750 | - D+ 773 | - D+ 786 | - D+ 799 | - D+ 812 | - D+ 825 | - D+ 840 | - D+ 852 | - D+ 864 | - D+ 877 | - D+ 890 | - D+ 903 | - D+ 918 | - D+ 930 | - D+ 943 | - D+ 955 | - D+ 968 | - D+ 981 | - D+ 994 | - D+ 1009 | - D+ 1021 | - D+ 1034 | - D+ 1046 | - D+ 1059 | - D+ 1072 | - D+ 1085 | - D+ 1100 | - D+ 1112 | - D+ 1125 | - D+ 1137 | - D+ 1149 | - D+ 1161 | - D+ 1173 | 1178 | 1183 | 1188 | 1193 | 1194 | 1199 | 1200 | 1201 | --------------------------------------------------------------------------------