The World's Most Accurate 7 Time Zone Clock :-)
Auto-setting (using DCF77 time signal from Frankfurt, Germany) 7 time zone clock with awesome animations
Components and supplies
1
DCF Receiver Module
7
TM1637 4-Digit 7 Segment Display
1
Arduino Mega 2560
7
Through Hole Resistor, 330 kohm
1
7-26VDC To 5VDC 3A Step Down Module
7
LED (generic)
1
360 Degree Rotary Encoder Module with Push Button
Apps and platforms
1
DaVinci Resolve
1
Arduino IDE
1
SketchUp
1
Affinity Designer
Project description
Code
Self-setting 7 time zone clock
arduino
1#include <DCF77.h> //https://github.com/thijse/Arduino-Libraries/downloads 2// Custom update to line 42 of DCF77.cpp from... 3// pinMode(dCF77Pin, INPUT); 4// to 5// pinMode(dCF77Pin, INPUT_PULLUP); 6// ...as DCF77 module signal goes to ground/LOW when signal is received, but otherwise "floats" so 7// INPUT_PULLUP activates the built-in pull-up resistor which pulls the line up HIGH 8 9#include <Time.h> //http://www.arduino.cc/playground/Code/Time 10#include <Timezone.h> //https://github.com/JChristensen/Timezone 11#include <TM1637Display.h> 12 13#define DCF_PIN 2 // Connection pin to DCF 77 device 14#define DCF_INTERRUPT 0 // Interrupt number associated with pin 15DCF77 DCF = DCF77(DCF_PIN,DCF_INTERRUPT,false); 16 17TimeChangeRule rDST1 = {"DST1", Second, Sun, Mar, 2, -240}; // Fort Worth Daylight Savings Time 18TimeChangeRule rST1 = {"ST1", First, Sun, Nov, 2, -300}; // Fort Worth Standard Time 19TimeChangeRule rDST2 = {"DST2", Second, Sun, Mar, 2, -180}; // New York Daylight Savings Time 20TimeChangeRule rST2 = {"ST2", First, Sun, Nov, 2, -240}; // New York Standard Time 21TimeChangeRule rDST3 = {"DST3", Last, Sun, Mar, 1, 120}; // London Daylight Savings Time 22TimeChangeRule rST3 = {"ST3", Last, Sun, Oct, 2, 60}; // London Standard Time 23TimeChangeRule rDST4 = {"DST4", Last, Sun, Mar, 2, 180}; // Zurich Daylight Savings Time 24TimeChangeRule rST4 = {"ST4", Last, Sun, Oct, 3, 120}; // Zurich Standard Time 25TimeChangeRule rST5 = {"ST5", Last, Sun, Oct, 1, 330}; // Pune Standard Time - No DST 26TimeChangeRule rST6 = {"ST6", Last, Sun, Oct, 1, 480}; // Hong Kong Standard Time - No DST 27TimeChangeRule rST7 = {"ST7", Last, Sun, Mar, 1, 540}; // Tokyo Standard Time - No DST 28Timezone Timezones[] { (rDST1, rST1), (rDST2, rST2), (rDST3, rST3), (rDST4, rST4), (rST5), (rST6), (rST7) }; 29const int localTimezone = 3; // Array pointer to local time zone -> Zurich 30 31time_t time; 32time_t varTime; 33 34const int numDigitDisplays = 7; // Number of 4 digit 7-segment display panels 35const int numDigits = numDigitDisplays * 4; // Total number of 7-segment digits available 36TM1637Display DigitDisplay[] { TM1637Display(28, 29), TM1637Display(47, 46), TM1637Display(45, 44), TM1637Display(26, 27), TM1637Display(43, 42), TM1637Display(24, 25), TM1637Display(41, 40) }; 37 38// Array to keep track of the current intensity set for each of the 7-segment displays 39int displayIntensity[numDigitDisplays]; 40 41// We will use this array to represent what is currently shown in each of the 7-segment digits 42// Think of this as the "display memory" 43byte digitShown[numDigits]; 44 45// We will use this array to prepare what we want to show in each of the 7-segment digits 46// Various functions will then use these two arrays to transition from what is currently being shown 47// to what we want to show 48byte digitToShow[numDigits]; 49 50// Binary 7-segment values corresponding to each decimal number 51const byte digitToSegment[] = { 52 0b00111111, // 0 53 0b00000110, // 1 54 0b01011011, // 2 55 0b01001111, // 3 56 0b01100110, // 4 57 0b01101101, // 5 58 0b01111101, // 6 59 0b00000111, // 7 60 0b01111111, // 8 61 0b01101111 // 9 62}; 63 64int prevMinute = 0; 65int nextAnimation = 0; 66 67const int menuButton = 22; 68boolean lastMenuButton = HIGH; 69boolean currentMenuButton = HIGH; 70 71boolean showDateAndTime = false; 72unsigned long startTime; 73 74const int DATA[1] = {31}; // Rotary encoder data pin 75const int CLK[1] = {30}; // Rotary encoder clock pin 76 77void setup() { 78 79 randomSeed(analogRead(0)); 80 81 //Serial.begin(9600); 82 83 DCF.Start(); 84 setSyncInterval(30); 85 setSyncProvider(getDCFTime); 86 87 pinMode(menuButton, INPUT_PULLUP); 88 pinMode(CLK[0], INPUT_PULLUP); 89 pinMode(DATA[0], INPUT_PULLUP); 90 91 // Blank out all displays and set default intensity 92 initDisplayIntensities(); 93 initDisplays(); 94 95 // Load 8's in array of what we want to display 96 for (int i = 0; i < numDigits; i++) { 97 digitToShow[i] = 255; 98 } 99 // Now show all 8's by "printing" segments (like a print head) from center outwards 100 printFromCenter(0); 101 delay(2000); 102 103 // Load nothing in array of what we want to display 104 for (int i = 0; i < numDigits; i++) { 105 digitToShow[i] = 0; 106 } 107 108 // Now show nothing by removing segments from center outwards 109 printFromExterior(0); 110 delay(1000); 111 112 // Show random animation until time is acquired 113 animationRandomPatterns(); 114 115 // Load acquired times in array of what we want to display 116 loadTimes(); 117 // Now show times by printing segments from center outwards 118 printFromCenter(15); 119 updateAllDisplays(); 120} 121 122void animationBulbTestComposeAndDecompose() { 123 124 // We want to show "8"s on all displays so lets load up our array with 8s 125 for (int i = 0; i < numDigits; i++) { 126 digitToShow[i] = 255; 127 } 128 printRandomSegment(5); 129 130 delay(2000); 131 // We now want to clear all of the displays so lets load 0s into the array 132 for (int i = 0; i < numDigits; i++) { 133 digitToShow[i] = 0; 134 } 135 printRandomSegment(5); 136 137 delay(2000); 138} 139 140void loadTimes() { 141 int p = 0, digitValue; 142 for (int i = 0; i < numDigitDisplays; i++) { 143 varTime = Timezones[i].toLocal(now()); 144 145 if (hour(varTime) > 9) { 146 digitValue = hour(varTime) / 10; 147 } else { 148 digitValue = 0; 149 } 150 digitToShow[p] = digitToSegment[digitValue]; 151 p++; 152 153 if (hour(varTime) > 0) { 154 digitValue = hour(varTime) - ((hour(varTime) / 10) * 10); 155 } else { 156 digitValue = hour(varTime); 157 } 158 digitToShow[p] = digitToSegment[digitValue] + 0b10000000; 159 p++; 160 161 if (minute(varTime) > 9) { 162 digitValue = minute(varTime) / 10; 163 } else { 164 digitValue = 0; 165 } 166 digitToShow[p] = digitToSegment[digitValue]; 167 p++; 168 169 if (minute(varTime) > 0) { 170 digitValue = minute(varTime) - ((minute(varTime) / 10) * 10); 171 } else { 172 digitValue = minute(varTime); 173 } 174 digitToShow[p] = digitToSegment[digitValue]; 175 p++; 176 } 177} 178 179void updateAllDisplays() { 180 int p = 0; 181 for (int i = 0; i < numDigitDisplays; i++) { 182 for (int j = 0; j < 4; j++) { 183 byte Segments[] = { digitToShow[p] }; 184 DigitDisplay[i].setSegments(Segments, 1, j); 185 digitShown[p] = digitToShow[p]; 186 p++; 187 } 188 } 189} 190 191void printRandomSegment(int delayMillis) { 192 193 // Count number of digits currently shown (in digitShown array) that don't match what we want to show (in digitToShow array) 194 int digitsLeft = 0; 195 for (int i = 0; i < numDigits; i++) { 196 if (digitShown[i] != digitToShow[i]) { digitsLeft++; } 197 } 198 199 while (digitsLeft > 0) { 200 long randomDigit = random(4); 201 long randomDisplay = random(numDigitDisplays); 202 int p = (randomDisplay * 4) + randomDigit; 203 if (digitShown[p] != digitToShow[p]) { 204 205 int varDigitShown = digitShown[p]; 206 int varDigitToShow = digitToShow[p]; 207 int bitPositionValue = 1; // Decimal value of 1st bit position 208 int diffValueFound[8]; 209 int diffValuesFound = 0; 210 for (int i = 0; i < 8; i++) { 211 if ((varDigitShown & 1) != (varDigitToShow & 1)) { 212 if (varDigitToShow & 1) { 213 diffValueFound[diffValuesFound] = bitPositionValue; // Store bit position decimal value in next available array position 214 } else { 215 diffValueFound[diffValuesFound] = (bitPositionValue * -1); // Store bit position decimal value in next available array position 216 } 217 diffValuesFound++; 218 } 219 varDigitShown >>= 1; 220 varDigitToShow >>= 1; 221 bitPositionValue = bitPositionValue * 2; // Calculate decimal value of next bit position 222 } 223 224 // Turn a random bit off 225 digitShown[p] = digitShown[p] + diffValueFound[random(diffValuesFound)]; 226 227 byte Segments[] = { digitShown[p] }; 228 DigitDisplay[randomDisplay].setSegments(Segments, 1, randomDigit); 229 230 if (digitShown[p] == digitToShow[p]) { digitsLeft--; }; 231 232 delay(delayMillis); 233 } 234 } 235} 236 237void printFromCenter(int delayMillis) { 238 239 // Compute starting position for left and right "print head" 240 int rightDigit = (numDigits / 2); 241 242 for (int leftDigit = (numDigits / 2) - 1; leftDigit >= 0; leftDigit--) { 243 244 // Compute 4-digit display panel number for left digit 245 int leftDisplay = leftDigit / 4; 246 // Compute digit number within 4-digit display panel for left digit 247 int leftDigitPanelPos = leftDigit - (leftDisplay * 4); 248 249 // Compute 4-digit display panel number for right digit 250 int rightDisplay = rightDigit / 4; 251 // Compute digit number within 4-digit display panel for right digit 252 int rightDigitPanelPos = rightDigit - (rightDisplay * 4); 253 254 // Show right-most vertical segments in left digit 255 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_B); 256 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_C); 257 delay(delayMillis); 258 259 // Show left-most vertical segments in right digit 260 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_F); 261 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_E); 262 delay(delayMillis); 263 264 // Show center vertical segments in left digit 265 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_A); 266 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_G); 267 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_D); 268 delay(delayMillis); 269 270 // Show center veritcal segments in right digit 271 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_A); 272 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_G); 273 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_D); 274 delay(delayMillis); 275 276 // Show left-most vertical segments in left digit 277 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_F); 278 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_E); 279 delay(delayMillis); 280 281 // Show right-most vertical segments in right digit 282 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_B); 283 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_C); 284 delay(delayMillis); 285 286 rightDigit++; 287 288 } 289} 290 291void printFromExterior(int delayMillis) { 292 293 // Compute starting position for left and right "print head" 294 int rightDigit = numDigits - 1; 295 296 for (int leftDigit = 0 ; leftDigit <= (numDigits / 2) - 1; leftDigit++) { 297 298 // Compute 4-digit display panel number for left digit 299 int leftDisplay = leftDigit / 4; 300 // Compute digit number within 4-digit display panel for left digit 301 int leftDigitPanelPos = leftDigit - (leftDisplay * 4); 302 303 // Compute 4-digit display panel number for right digit 304 int rightDisplay = rightDigit / 4; 305 // Compute digit number within 4-digit display panel for right digit 306 int rightDigitPanelPos = rightDigit - (rightDisplay * 4); 307 308 // Show left-most vertical segments in left digit 309 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_F); 310 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_E); 311 delay(delayMillis); 312 313 // Show right-most vertical segments in right digit 314 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_B); 315 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_C); 316 delay(delayMillis); 317 318 // Show center vertical segments in left digit 319 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_A); 320 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_G); 321 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_D); 322 delay(delayMillis); 323 324 // Show center veritcal segments in right digit 325 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_A); 326 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_G); 327 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_D); 328 delay(delayMillis); 329 330 // Show right-most vertical segments in left digit 331 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_B); 332 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_C); 333 delay(delayMillis); 334 335 // Show left-most vertical segments in right digit 336 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_F); 337 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_E); 338 delay(delayMillis); 339 340 rightDigit--; 341 342 } 343} 344 345 346void printFromLeft(int delayMillis) { 347 int p = 0; 348 for (int i = 0; i < numDigitDisplays; i++) { 349 for (int j = 0; j < 4; j++) { 350 printOneSegment(p, i, j, SEG_F); 351 printOneSegment(p, i, j, SEG_E); 352 delay(delayMillis); 353 printOneSegment(p, i, j, SEG_A); 354 printOneSegment(p, i, j, SEG_G); 355 printOneSegment(p, i, j, SEG_D); 356 delay(delayMillis); 357 printOneSegment(p, i, j, SEG_B); 358 printOneSegment(p, i, j, SEG_C); 359 delay(delayMillis); 360 if (j == 1) { 361 printOneSegment(p, i, j, 0b10000000); // Colon after 2nd digit! 362 delay(delayMillis); 363 } 364 p++; 365 } 366 } 367} 368 369void printOneSegment(int p, int i, int j, byte segmentToShow) { 370 // If the segment is present in the digit that should be shown... 371 if ((digitToShow[p] & segmentToShow) == segmentToShow) { 372 // If the segment is not currently being shown... 373 if ((digitShown[p] & segmentToShow) == 0) { 374 // ...then we should add it / show it 375 digitShown[p] = digitShown[p] | segmentToShow; 376 } 377 // ...else the segment is not present in the digit that should be shown... 378 } else { 379 // If the digit currently shown has the segment... 380 if ((digitShown[p] & segmentToShow) == segmentToShow) { 381 // ...then we should remove it / turn it off 382 digitShown[p] = digitShown[p] ^ segmentToShow; 383 } 384 } 385 byte Segments[] = { digitShown[p] }; 386 DigitDisplay[i].setSegments(Segments, 1, j); 387} 388 389void diplayTimesByCountingUp() { 390 for (int i = 0; i < numDigitDisplays; i++) { 391 varTime = Timezones[i].toLocal(now()); 392 int intHour = hour(varTime); 393 for (int j = 0; j <= intHour; j++) { 394 DigitDisplay[i].showNumberDec(j, true, 2, 0); 395 delay(15); 396 } 397 delay(250); 398 int intMinute = minute(varTime); 399 for (int j = 0; j <= intMinute; j++) { 400 DigitDisplay[i].showNumberDecEx(j, 0b11100000, true, 2, 2); 401 delay(15); 402 } 403 delay(250); 404 } 405 loadTimes(); 406 updateAllDisplays(); 407} 408 409void diplayTimesByCountingUpDigitByDigit() { 410 for (int i = 0; i < numDigitDisplays; i++) { 411 varTime = Timezones[i].toLocal(now()); 412 413 int intHour = 0; 414 if (hour(varTime) > 9) { 415 intHour = hour(varTime) / 10; 416 for (int j = 0; j <= intHour; j++) { 417 DigitDisplay[i].showNumberDec(j, true, 1, 0); 418 delay(15); 419 } 420 } else { 421 DigitDisplay[i].showNumberDec(0, true, 1, 0); 422 delay(15); 423 } 424 delay(250); 425 426 int intHour2 = hour(varTime) - (intHour * 10); 427 for (int j = 0; j <= intHour2; j++) { 428 DigitDisplay[i].showNumberDec(j, true, 1, 1); 429 delay(15); 430 } 431 delay(250); 432 433 int intMinute = 0; 434 if (minute(varTime) > 9) { 435 intMinute = minute(varTime) / 10; 436 for (int j = 0; j <= intMinute; j++) { 437 DigitDisplay[i].showNumberDecEx(j, 0b11100000, true, 1, 2); 438 delay(15); 439 } 440 } else { 441 DigitDisplay[i].showNumberDecEx(0, 0b11100000, true, 1, 2); 442 delay(15); 443 } 444 delay(250); 445 446 int intMinute2 = minute(varTime) - (intMinute * 10); 447 for (int j = 0; j <= intMinute2; j++) { 448 DigitDisplay[i].showNumberDec(j, true, 1, 3); 449 delay(15); 450 } 451 delay(250); 452 } 453 loadTimes(); 454 updateAllDisplays(); 455} 456 457void animationRandomPatterns() { 458 459 while(timeStatus() == timeNotSet) { 460 461 long randomDigit = random(4); 462 long randomDisplay = random(numDigitDisplays); 463 byte randomNum[] = { random(1,128) }; 464 byte noDigit[] = { 0 }; 465 long randomWait = random(15,50); 466 467 DigitDisplay[randomDisplay].setSegments(randomNum, 1, randomDigit); 468 delay(randomWait * 2); 469 for (int i = 6; i >= 0; i--) { 470 DigitDisplay[randomDisplay].setBrightness(i, true); 471 DigitDisplay[randomDisplay].setSegments(randomNum, 1, randomDigit); 472 delay(randomWait); 473 } 474 DigitDisplay[randomDisplay].setBrightness(7, true); 475 DigitDisplay[randomDisplay].setSegments(noDigit, 1, randomDigit); 476 } 477 delay(500); 478 initDisplays(); 479 initDisplayIntensities(); 480} 481 482void initDisplays() { 483 // Clear all 7-segment displays and set to default brightness 484 for (int i = 0; i < numDigitDisplays; i++) { 485 DigitDisplay[i].clear(); 486 } 487 // Reflect empty displays in array keeping track of current digits shown 488 for (int i = 0; i < numDigits; i++) { 489 digitShown[i] = 0; 490 } 491} 492 493void initDisplayIntensities() { 494 for (int i = 0; i < numDigitDisplays; i++) { 495 DigitDisplay[i].setBrightness(0, true); 496 displayIntensity[i] = 0; 497 } 498} 499 500void showLocalDateAndTime() { 501 varTime = Timezones[localTimezone].toLocal(now()); 502 DigitDisplay[0].showNumberDec(year(varTime), false, 4, 0); 503 DigitDisplay[1].showNumberDec(month(varTime), true, 2, 0); 504 DigitDisplay[1].showNumberDec(day(varTime), true, 2, 2); 505 DigitDisplay[numDigitDisplays - 1].showNumberDec(hour(varTime), true, 2, 0); 506 // Seems a bit complicated but one way to show colon between hours and minutes... 507 int intMinute = 0; 508 if (minute(varTime) > 9) { 509 intMinute = minute(varTime) / 10; 510 DigitDisplay[numDigitDisplays - 1].showNumberDecEx(intMinute, 0b11100000, true, 1, 2); 511 } else { 512 DigitDisplay[numDigitDisplays - 1].showNumberDecEx(0, 0b11100000, true, 1, 2); 513 } 514 int intMinute2 = minute(varTime) - (intMinute * 10); 515 DigitDisplay[numDigitDisplays - 1].showNumberDec(intMinute2, true, 1, 3); 516 showDateAndTime = true; 517} 518 519void removeLocalDateAndTime() { 520 showDateAndTime = false; 521 // Remove local date and time and randomly choose a way to show all timezones 522 initDisplays(); 523 delay(250); 524 switch (nextAnimation) { 525 case 0: 526 loadTimes(); 527 printFromCenter(15); 528 break; 529 case 1: 530 loadTimes(); 531 printFromLeft(15); 532 break; 533 case 2: 534 loadTimes(); 535 printRandomSegment(5); 536 break; 537 case 3: 538 diplayTimesByCountingUp(); 539 break; 540 case 4: 541 diplayTimesByCountingUpDigitByDigit(); 542 break; 543 case 5: 544 loadTimes(); 545 printFromExterior(15); 546 break; 547 } 548 nextAnimation++; 549 if (nextAnimation > 5) { 550 nextAnimation = 0; 551 } 552 loadTimes(); 553 updateAllDisplays(); 554} 555 556void loop() { 557 558 static int8_t val[2]; 559 560 // Update time only if minute has changed 561 if ( prevMinute != minute() ) { 562 prevMinute = minute(); 563 564 // If local date and time are not currently shown... 565 if (!showDateAndTime) { 566 567 // Update all timezones shown 568 loadTimes(); 569 updateAllDisplays(); 570 571 // Flash all displays 3 times at top of hour and 30 minutes into the hour during business hours of weekdays 572 // Get local time 573 varTime = Timezones[localTimezone].toLocal(now()); 574 // If now is a weekday (Monday - Friday) 575 if ((weekday(varTime) > 1) && (weekday(varTime) < 7)) { 576 // ...and now is between 8:00 and 17:59 577 if ((hour(varTime) > 7) && (hour(varTime) < 18)) { 578 // ...and we are at the top of the hour or 30 minutes into the hour... 579 if ((minute(varTime) == 0) || (minute(varTime) == 30)) { 580 long randomMode = random(3); 581 for (int j = 0; j < 3; j++) { 582 switch (randomMode) { 583 case 0: 584 delay(500); 585 // Load nothing in array of what we want to display 586 for (int i = 0; i < numDigits; i++) { 587 digitToShow[i] = 0; 588 } 589 printFromExterior(0); 590 delay(500); 591 loadTimes(); 592 printFromCenter(0); 593 break; 594 case 1: 595 delay(500); 596 // Load nothing in array of what we want to display 597 for (int i = 0; i < numDigits; i++) { 598 digitToShow[i] = 0; 599 } 600 printRandomSegment(0); 601 delay(500); 602 loadTimes(); 603 printRandomSegment(0); 604 break; 605 case 2: 606 delay(500); 607 // Turn all displays to max intensity 608 for (int i = 0; i < numDigitDisplays; i++) { 609 DigitDisplay[i].setBrightness(6, true); 610 } 611 updateAllDisplays(); 612 delay(500); 613 // Turn all displays to min intensity 614 for (int i = 0; i < numDigitDisplays; i++) { 615 DigitDisplay[i].setBrightness(0, true); 616 } 617 updateAllDisplays(); 618 // Turn all displays back to user-set intensity 619 for (int i = 0; i < numDigitDisplays; i++) { 620 DigitDisplay[i].setBrightness(displayIntensity[i], true); 621 } 622 break; 623 } 624 } 625 updateAllDisplays(); 626 } 627 } 628 } 629 } else { // Local date and time are shown... 630 // ...so update it 631 showLocalDateAndTime(); 632 } 633 } 634 635 // Remove local date and time if it has been shown for 10 seconds already 636 if ((showDateAndTime) && (millis() > (startTime + 10000))) { 637 removeLocalDateAndTime(); 638 } 639 640 // Read rotary encoder and process if it has been moved 641 val[0] = read_rotary(0); 642 if (val[0] != 0) { 643 if (val[0] < 0) { // Lower intensity of displays if turned counter-clockwise 644 for (int i = 0; i < numDigitDisplays; i++) { 645 displayIntensity[i] = displayIntensity[i] - 1; 646 if ( displayIntensity[i] < 0 ) {displayIntensity[i] = 0;} 647 DigitDisplay[i].setBrightness(displayIntensity[i], true); 648 } 649 } else { // Increase intensity of displays if turned clockwise 650 for (int i = 0; i < numDigitDisplays; i++) { 651 displayIntensity[i] = displayIntensity[i] + 1; 652 if ( displayIntensity[i] > 7 ) {displayIntensity[i] = 7;} 653 DigitDisplay[i].setBrightness(displayIntensity[i], true); 654 } 655 } 656 if (!showDateAndTime) { // If local date and time is not currently shown... 657 updateAllDisplays(); // Update all displays 658 } else { 659 showLocalDateAndTime(); // Otherwise update the local date and time 660 } 661 } 662 663 // Rotary push button 664 currentMenuButton = debounce(lastMenuButton, menuButton); 665 if (lastMenuButton == HIGH && currentMenuButton == LOW) { // If pushbutton has been pushed... 666 if (!showDateAndTime) { // Show local date and time if not currently being shown 667 initDisplays(); 668 delay(250); 669 showLocalDateAndTime(); 670 startTime = millis(); 671 } else { // Otherwise remove local date and time 672 removeLocalDateAndTime(); 673 } 674 } 675 lastMenuButton = currentMenuButton; 676} 677 678unsigned long getDCFTime() 679{ 680 time_t DCFtime = DCF.getUTCTime(); // Convert from UTC 681 682 if (DCFtime!=0) { 683 //Serial.print("X"); // Indicator that a time check is done 684 //time_t LocalTime = CET.toLocal(DCFtime); 685 //return LocalTime; 686 return DCFtime; 687 } 688 return 0; 689} 690 691// Rotary encoder processor (bullet proof!!!) 692// A valid CW or CCW move returns 1 or -1, invalid returns 0 693int8_t read_rotary(int i) { 694 695 static uint8_t prevNextCode[1] = {0}; 696 static uint16_t store[1] = {0}; 697 static int8_t rot_enc_table[] = {0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0}; 698 prevNextCode[i] <<= 2; 699 if (digitalRead(DATA[i])) prevNextCode[i] |= 0x02; 700 if (digitalRead(CLK[i])) prevNextCode[i] |= 0x01; 701 prevNextCode[i] &= 0x0f; 702 703 // If valid then store as 16 bit data. 704 if (rot_enc_table[prevNextCode[i]] ) { 705 store[i] <<= 4; 706 store[i] |= prevNextCode[i]; 707 if ((store[i]&0xff)==0x2b) return -1; 708 if ((store[i]&0xff)==0x17) return 1; 709 } 710 return 0; 711} 712 713// Button debouncer 714boolean debounce(boolean last, int pin) { 715 boolean current = digitalRead(pin); 716 if (last != current) { 717 delay(5); 718 current = digitalRead(pin); 719 } 720 return current; 721}
Multi-Time Zone Clock
arduino
1// Auto-setting 7 time zone clock 2// By Alan De Windt 3// November 2022 4 5#include <DCF77.h> //https://github.com/thijse/Arduino-DCF77 6// Custom update to line 42 of DCF77.cpp from... 7// pinMode(dCF77Pin, INPUT); 8// to 9// pinMode(dCF77Pin, INPUT_PULLUP); 10// ...as DCF77 module signal goes to ground/LOW when signal is received, but otherwise "floats" so 11// INPUT_PULLUP activates the built-in pull-up resistor which pulls the line up HIGH 12#include <TimeLib.h> //https://github.com/PaulStoffregen/Time 13#include <Timezone.h> //https://github.com/JChristensen/Timezone 14#include <TM1637Display.h> //https://github.com/avishorp/TM1637 15 16#define DCF_PIN 2 // Connection pin to DCF 77 device 17#define DCF_INTERRUPT 0 // Interrupt number associated with pin 18DCF77 DCF = DCF77(DCF_PIN,DCF_INTERRUPT,false); 19 20TimeChangeRule rDST1 = {"CDT", Second, Sun, Mar, 2, -300}; // Fort Worth Daylight Savings Time 21TimeChangeRule rST1 = {"CST", First, Sun, Nov, 2, -360}; // Fort Worth Standard Time 22 23TimeChangeRule rDST2 = {"EDT", Second, Sun, Mar, 2, -240}; // New York Daylight Savings Time 24TimeChangeRule rST2 = {"EST", First, Sun, Nov, 2, -300}; // New York Standard Time 25 26TimeChangeRule rDST3 = {"BST", Last, Sun, Mar, 1, 60}; // London Daylight Savings Time 27TimeChangeRule rST3 = {"GMT", Last, Sun, Oct, 2, 0}; // London Standard Time 28 29TimeChangeRule rDST4 = {"CEST", Last, Sun, Mar, 2, 120}; // Zurich Daylight Savings Time 30TimeChangeRule rST4 = {"CET", Last, Sun, Oct, 3, 60}; // Zurich Standard Time 31 32TimeChangeRule rST5 = {"IST", Last, Sun, Oct, 1, 330}; // Pune Standard Time - No DST 33 34TimeChangeRule rST6 = {"HKT", Last, Sun, Oct, 1, 480}; // Hong Kong Standard Time - No DST 35 36TimeChangeRule rST7 = {"JST", Last, Sun, Mar, 1, 540}; // Tokyo Standard Time - No DST 37 38Timezone Timezones[] { (rDST1, rST1), (rDST2, rST2), (rDST3, rST3), (rDST4, rST4), (rST5), (rST6), (rST7) }; 39 40const int localTimezone = 3; // Array pointer to local time zone -> Zurich 41 42time_t time(time_t *timer); 43time_t varTime; 44 45const int numDigitDisplays = 7; // Number of 4 digit 7-segment display panels 46const int numDigits = numDigitDisplays * 4; // Total number of 7-segment digits available 47TM1637Display DigitDisplay[] { TM1637Display(28, 29), TM1637Display(47, 46), TM1637Display(45, 44), TM1637Display(26, 27), TM1637Display(43, 42), TM1637Display(24, 25), TM1637Display(41, 40) }; 48 49int timeZoneLabels[] = {23, 20, 21, 18, 19, 16 , 17}; 50 51// Array to keep track of the current intensity set for each of the 7-segment displays 52int displayIntensity[numDigitDisplays]; 53 54// We will use this array to represent what is currently shown in each of the 7-segment digits 55// Think of this as the "display memory" 56byte digitShown[numDigits]; 57 58// We will use this array to prepare what we want to show in each of the 7-segment digits 59// Various functions will then use these two arrays to transition from what is currently being shown 60// to what we want to show 61byte digitToShow[numDigits]; 62 63// Binary 7-segment values corresponding to each decimal number 64const byte digitToSegment[] = { 65 0b00111111, // 0 66 0b00000110, // 1 67 0b01011011, // 2 68 0b01001111, // 3 69 0b01100110, // 4 70 0b01101101, // 5 71 0b01111101, // 6 72 0b00000111, // 7 73 0b01111111, // 8 74 0b01101111 // 9 75}; 76 77const byte SEG_COLON = 0b10000000; 78 79int prevMinute = 0; 80int nextAnimation = 0; 81int nextLabelAnimation = 4; 82 83const int menuButton = 22; 84boolean lastMenuButton = HIGH; 85boolean currentMenuButton = HIGH; 86 87boolean showDateAndTime = false; 88unsigned long startTime; 89 90const int DATA[1] = {31}; // Rotary encoder data pin 91const int CLK[1] = {30}; // Rotary encoder clock pin 92 93void setup() { 94 95 randomSeed(analogRead(0)); 96 97 //Serial.begin(9600); 98 //Serial.println("Starting..."); 99 100 DCF.Start(); 101 setSyncInterval(30); 102 setSyncProvider(getDCFTime); 103 104 pinMode(menuButton, INPUT_PULLUP); 105 pinMode(CLK[0], INPUT_PULLUP); 106 pinMode(DATA[0], INPUT_PULLUP); 107 108 // Initialize time zone label pins 109 for (int i = 0; i < numDigitDisplays; i++) { 110 pinMode(timeZoneLabels[i], OUTPUT); 111 } 112 113 // Blank out all displays and set default intensity 114 initDisplayIntensities(); 115 initDisplays(); 116 117 // Load 8's in array of what we want to display 118 for (int i = 0; i < numDigits; i++) { 119 digitToShow[i] = digitToSegment[8]; 120 } 121 // Now show all 8's by "printing" segments (like a print head) from center outwards 122 printFromCenter(0); 123 delay(2000); 124 125 // Load nothing in array of what we want to display 126 for (int i = 0; i < numDigits; i++) { 127 digitToShow[i] = 0; 128 } 129 // Now show nothing by removing segments from exterior inwards 130 printFromExterior(0); 131 for (int i = 0; i < numDigitDisplays; i++) { 132 digitalWrite(timeZoneLabels[i], LOW); 133 } 134 delay(1000); 135 136 // Show random animation until time is acquired 137 animationRandomPatterns(); 138 139 // Load acquired times in array of what we want to display 140 loadTimes(); 141 142 // Now show times by printing segments from center outwards 143 printFromCenter(15); 144 updateAllDisplays(); 145} 146 147void loadTimes() { 148 int p = 0, digitValue; 149 for (int i = 0; i < numDigitDisplays; i++) { 150 varTime = Timezones[i].toLocal(now()); 151 152 if (hour(varTime) > 9) { 153 digitValue = hour(varTime) / 10; 154 } else { 155 digitValue = 0; 156 } 157 digitToShow[p] = digitToSegment[digitValue]; 158 p++; 159 160 if (hour(varTime) > 0) { 161 digitValue = hour(varTime) - ((hour(varTime) / 10) * 10); 162 } else { 163 digitValue = hour(varTime); 164 } 165 digitToShow[p] = digitToSegment[digitValue] + SEG_COLON; 166 p++; 167 168 if (minute(varTime) > 9) { 169 digitValue = minute(varTime) / 10; 170 } else { 171 digitValue = 0; 172 } 173 digitToShow[p] = digitToSegment[digitValue]; 174 p++; 175 176 if (minute(varTime) > 0) { 177 digitValue = minute(varTime) - ((minute(varTime) / 10) * 10); 178 } else { 179 digitValue = minute(varTime); 180 } 181 digitToShow[p] = digitToSegment[digitValue]; 182 p++; 183 } 184} 185 186void updateAllDisplays() { 187 int p = 0; 188 for (int i = 0; i < numDigitDisplays; i++) { 189 for (int j = 0; j < 4; j++) { 190 byte Segments[] = { digitToShow[p] }; 191 DigitDisplay[i].setSegments(Segments, 1, j); 192 digitShown[p] = digitToShow[p]; 193 p++; 194 } 195 } 196} 197 198void turnOnOffLabels() { 199 // Cycle through each 4 digit 7-segment display 200 for (int i = 0; i < numDigitDisplays; i++) { 201 int firstDigit = i * 4; 202 // If something should be shown... 203 if (digitToShow[firstDigit] > 0) { 204 //...and it is fully shown... 205 if ( (digitShown[firstDigit] == digitToShow[firstDigit]) && 206 (digitShown[firstDigit + 1] == digitToShow[firstDigit + 1]) && 207 (digitShown[firstDigit + 2] == digitToShow[firstDigit + 2]) && 208 (digitShown[firstDigit + 3] == digitToShow[firstDigit + 3]) ) { 209 // then turn on the label 210 digitalWrite(timeZoneLabels[i], HIGH); 211 } 212 } else { // Otherwise if nothing should be shown 213 // ...and nothing is currently shown... 214 if ( (digitShown[firstDigit] == 0) && 215 (digitShown[firstDigit + 1] == 0) && 216 (digitShown[firstDigit + 2] == 0) && 217 (digitShown[firstDigit + 3] == 0) ) { 218 // then turn off the label 219 digitalWrite(timeZoneLabels[i], LOW); 220 } 221 } 222 } 223} 224 225void printRandomSegment(int delayMillis) { 226 227 // Count number of digits currently shown (in digitShown array) that don't match what we want to show (in digitToShow array) 228 int digitsLeft = 0; 229 for (int i = 0; i < numDigits; i++) { 230 if (digitShown[i] != digitToShow[i]) { digitsLeft++; } 231 } 232 233 while (digitsLeft > 0) { 234 long randomDigit = random(4); 235 long randomDisplay = random(numDigitDisplays); 236 int p = (randomDisplay * 4) + randomDigit; 237 if (digitShown[p] != digitToShow[p]) { 238 239 int varDigitShown = digitShown[p]; 240 int varDigitToShow = digitToShow[p]; 241 int bitPositionValue = 1; // Decimal value of 1st bit position 242 int diffValueFound[8]; 243 int diffValuesFound = 0; 244 for (int i = 0; i < 8; i++) { 245 if ((varDigitShown & 1) != (varDigitToShow & 1)) { 246 if (varDigitToShow & 1) { 247 diffValueFound[diffValuesFound] = bitPositionValue; // Store bit position decimal value in next available array position 248 } else { 249 diffValueFound[diffValuesFound] = (bitPositionValue * -1); // Store bit position decimal value in next available array position 250 } 251 diffValuesFound++; 252 } 253 varDigitShown >>= 1; 254 varDigitToShow >>= 1; 255 bitPositionValue = bitPositionValue * 2; // Calculate decimal value of next bit position 256 } 257 258 // Turn a random bit off 259 digitShown[p] = digitShown[p] + diffValueFound[random(diffValuesFound)]; 260 261 byte Segments[] = { digitShown[p] }; 262 DigitDisplay[randomDisplay].setSegments(Segments, 1, randomDigit); 263 264 if (digitShown[p] == digitToShow[p]) { 265 digitsLeft--; 266 turnOnOffLabels(); 267 } 268 269 delay(delayMillis); 270 } 271 } 272} 273 274void printFromCenter(int delayMillis) { 275 276 // Compute starting position for left and right "print head" 277 int rightDigit = (numDigits / 2); 278 279 for (int leftDigit = (numDigits / 2) - 1; leftDigit >= 0; leftDigit--) { 280 281 // Compute 4-digit display panel number for left digit 282 int leftDisplay = leftDigit / 4; 283 // Compute digit number within 4-digit display panel for left digit 284 int leftDigitPanelPos = leftDigit - (leftDisplay * 4); 285 286 // Compute 4-digit display panel number for right digit 287 int rightDisplay = rightDigit / 4; 288 // Compute digit number within 4-digit display panel for right digit 289 int rightDigitPanelPos = rightDigit - (rightDisplay * 4); 290 291 // Show right-most vertical segments in left digit 292 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_COLON); 293 delay(delayMillis); 294 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_B); 295 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_C); 296 delay(delayMillis); 297 298 // Show left-most vertical segments in right digit 299 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_F); 300 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_E); 301 delay(delayMillis); 302 303 // Show center vertical segments in left digit 304 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_A); 305 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_G); 306 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_D); 307 delay(delayMillis); 308 309 // Show center vertical segments in right digit 310 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_A); 311 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_G); 312 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_D); 313 delay(delayMillis); 314 315 // Show left-most vertical segments in left digit 316 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_F); 317 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_E); 318 delay(delayMillis); 319 320 // Show right-most vertical segments in right digit 321 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_B); 322 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_C); 323 delay(delayMillis); 324 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_COLON); 325 delay(delayMillis); 326 327 turnOnOffLabels(); 328 329 rightDigit++; 330 331 } 332} 333 334void printFromExterior(int delayMillis) { 335 336 // Compute starting position for left and right "print head" 337 int rightDigit = numDigits - 1; 338 339 for (int leftDigit = 0 ; leftDigit <= (numDigits / 2) - 1; leftDigit++) { 340 341 // Compute 4-digit display panel number for left digit 342 int leftDisplay = leftDigit / 4; 343 // Compute digit number within 4-digit display panel for left digit 344 int leftDigitPanelPos = leftDigit - (leftDisplay * 4); 345 346 // Compute 4-digit display panel number for right digit 347 int rightDisplay = rightDigit / 4; 348 // Compute digit number within 4-digit display panel for right digit 349 int rightDigitPanelPos = rightDigit - (rightDisplay * 4); 350 351 // Show left-most vertical segments in left digit 352 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_F); 353 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_E); 354 delay(delayMillis); 355 356 // Show right-most vertical segments in right digit 357 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_COLON); 358 delay(delayMillis); 359 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_B); 360 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_C); 361 delay(delayMillis); 362 363 // Show center vertical segments in left digit 364 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_A); 365 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_G); 366 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_D); 367 delay(delayMillis); 368 369 // Show center veritcal segments in right digit 370 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_A); 371 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_G); 372 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_D); 373 delay(delayMillis); 374 375 // Show right-most vertical segments in left digit 376 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_B); 377 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_C); 378 delay(delayMillis); 379 printOneSegment(leftDigit, leftDisplay, leftDigitPanelPos, SEG_COLON); 380 delay(delayMillis); 381 382 // Show left-most vertical segments in right digit 383 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_F); 384 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_E); 385 printOneSegment(rightDigit, rightDisplay, rightDigitPanelPos, SEG_COLON); 386 delay(delayMillis); 387 388 turnOnOffLabels(); 389 390 rightDigit--; 391 392 } 393} 394 395void printFromLeft(int delayMillis, boolean suppressLabels) { 396 int p = 0; 397 for (int i = 0; i < numDigitDisplays; i++) { 398 for (int j = 0; j < 4; j++) { 399 printOneSegment(p, i, j, SEG_F); 400 printOneSegment(p, i, j, SEG_E); 401 delay(delayMillis); 402 printOneSegment(p, i, j, SEG_A); 403 printOneSegment(p, i, j, SEG_G); 404 printOneSegment(p, i, j, SEG_D); 405 delay(delayMillis); 406 printOneSegment(p, i, j, SEG_B); 407 printOneSegment(p, i, j, SEG_C); 408 delay(delayMillis); 409 printOneSegment(p, i, j, SEG_COLON); 410 delay(delayMillis); 411 p++; 412 } 413 if (!suppressLabels) { 414 turnOnOffLabels(); 415 } 416 } 417} 418 419void printOneSegment(int p, int i, int j, byte segmentToShow) { 420 // If the segment is present in the digit that should be shown... 421 if ((digitToShow[p] & segmentToShow) == segmentToShow) { 422 // If the segment is not currently being shown... 423 if ((digitShown[p] & segmentToShow) == 0) { 424 // ...then we should add it / show it 425 digitShown[p] = digitShown[p] | segmentToShow; 426 } 427 // ...else the segment is not present in the digit that should be shown... 428 } else { 429 // If the digit currently shown has the segment... 430 if ((digitShown[p] & segmentToShow) == segmentToShow) { 431 // ...then we should remove it / turn it off 432 digitShown[p] = digitShown[p] ^ segmentToShow; 433 } 434 } 435 byte Segments[] = { digitShown[p] }; 436 DigitDisplay[i].setSegments(Segments, 1, j); 437} 438 439void displayTimesByCountingAcrossAllDisplays(int delayMillis) { 440 // Load all times into a String object 441 String timeString = ""; 442 for (int i = 0; i < numDigitDisplays; i++) { 443 varTime = Timezones[i].toLocal(now()); 444 if (hour(varTime) < 10) { 445 timeString += "0" + String(hour(varTime)); 446 } else { 447 timeString += String(hour(varTime)); 448 } 449 if (minute(varTime) < 10) { 450 timeString += "0" + String(minute(varTime)); 451 } else { 452 timeString += String(minute(varTime)); 453 } 454 } 455 456 // Cycle through numbers 0 to 9 457 for (int i = 0; i < 10; i++) { 458 // Cycle through each digit from left to right 459 for (int j = 0; j < numDigits; j++) { 460 // If the number we want to show is less than or equal to the number that needs to be shown then show it, otherwise it is already shown 461 if (int(timeString.charAt(j)) - 48 >= i) { 462 int displayNum = j / 4; 463 int positionNum = j - (displayNum * 4); 464 DigitDisplay[displayNum].showNumberDec(i, true, 1, positionNum); 465 } 466 delay(delayMillis); 467 } 468 } 469 // Turn on all of the time zone labels 470 turnAllLabelsOn(); 471 472 loadTimes(); 473 updateAllDisplays(); 474} 475 476void displayTimesByCountingUp() { 477 for (int i = 0; i < numDigitDisplays; i++) { 478 varTime = Timezones[i].toLocal(now()); 479 int intHour = hour(varTime); 480 for (int j = 0; j <= intHour; j++) { 481 DigitDisplay[i].showNumberDec(j, true, 2, 0); 482 delay(15); 483 } 484 delay(250); 485 int intMinute = minute(varTime); 486 for (int j = 0; j <= intMinute; j++) { 487 DigitDisplay[i].showNumberDecEx(j, 0b11100000, true, 2, 2); 488 delay(15); 489 } 490 digitalWrite(timeZoneLabels[i], HIGH); 491 delay(250); 492 } 493 loadTimes(); 494 updateAllDisplays(); 495} 496 497void displayTimesByCountingUpDigitByDigit() { 498 for (int i = 0; i < numDigitDisplays; i++) { 499 varTime = Timezones[i].toLocal(now()); 500 int intHour = 0; 501 if (hour(varTime) > 9) { 502 intHour = hour(varTime) / 10; 503 for (int j = 0; j <= intHour; j++) { 504 DigitDisplay[i].showNumberDec(j, true, 1, 0); 505 delay(15); 506 } 507 } else { 508 DigitDisplay[i].showNumberDec(0, true, 1, 0); 509 delay(15); 510 } 511 delay(250); 512 513 int intHour2 = hour(varTime) - (intHour * 10); 514 for (int j = 0; j <= intHour2; j++) { 515 DigitDisplay[i].showNumberDec(j, true, 1, 1); 516 delay(15); 517 } 518 delay(250); 519 520 int intMinute = 0; 521 if (minute(varTime) > 9) { 522 intMinute = minute(varTime) / 10; 523 for (int j = 0; j <= intMinute; j++) { 524 DigitDisplay[i].showNumberDecEx(j, 0b11100000, true, 1, 2); 525 delay(15); 526 } 527 } else { 528 DigitDisplay[i].showNumberDecEx(0, 0b11100000, true, 1, 2); 529 delay(15); 530 } 531 delay(250); 532 533 int intMinute2 = minute(varTime) - (intMinute * 10); 534 for (int j = 0; j <= intMinute2; j++) { 535 DigitDisplay[i].showNumberDec(j, true, 1, 3); 536 delay(15); 537 } 538 digitalWrite(timeZoneLabels[i], HIGH); 539 delay(250); 540 } 541 loadTimes(); 542 updateAllDisplays(); 543} 544 545void animationRandomPatterns() { 546 547 while(timeStatus() == timeNotSet) { 548 549 long randomDigit = random(4); 550 long randomDisplay = random(numDigitDisplays); 551 byte randomNum[] = { random(1,128) }; 552 byte noDigit[] = { 0 }; 553 long randomWait = random(15,50); 554 555 DigitDisplay[randomDisplay].setSegments(randomNum, 1, randomDigit); 556 delay(randomWait * 2); 557 for (int i = 6; i >= 0; i--) { 558 DigitDisplay[randomDisplay].setBrightness(i, true); 559 DigitDisplay[randomDisplay].setSegments(randomNum, 1, randomDigit); 560 delay(randomWait); 561 } 562 DigitDisplay[randomDisplay].setBrightness(7, true); 563 DigitDisplay[randomDisplay].setSegments(noDigit, 1, randomDigit); 564 } 565 delay(500); 566 initDisplays(); 567 initDisplayIntensities(); 568} 569 570void initDisplays() { 571 // Clear all 7-segment displays and set to default brightness 572 for (int i = 0; i < numDigitDisplays; i++) { 573 DigitDisplay[i].clear(); 574 digitalWrite(timeZoneLabels[i], LOW); 575 } 576 // Reflect empty displays in array keeping track of current digits shown 577 for (int i = 0; i < numDigits; i++) { 578 digitShown[i] = 0; 579 } 580} 581 582void initDisplayIntensities() { 583 for (int i = 0; i < numDigitDisplays; i++) { 584 DigitDisplay[i].setBrightness(0, true); 585 displayIntensity[i] = 0; 586 } 587} 588 589void loadLocalDateAndTime() { 590 varTime = Timezones[localTimezone].toLocal(now()); 591 592 // Load date and time into as String object 593 String localDateAndTime = String(year(varTime)); 594 if (month(varTime) < 10) { 595 localDateAndTime += "0" + String(month(varTime)); 596 } else { 597 localDateAndTime += String(month(varTime)); 598 } 599 if (day(varTime) < 10) { 600 localDateAndTime += "0" + String(day(varTime)); 601 } else { 602 localDateAndTime += String(day(varTime)); 603 } 604 605 // Add spaces for number of 4 digit panels between 2nd panel and last panel 606 for (int i = 0; i < (numDigitDisplays - 3); i++) { 607 localDateAndTime += " "; 608 } 609 610 if (hour(varTime) < 10) { 611 localDateAndTime += "0" + String(hour(varTime)); 612 } else { 613 localDateAndTime += String(hour(varTime)); 614 } 615 if (minute(varTime) < 10) { 616 localDateAndTime += "0" + String(minute(varTime)); 617 } else { 618 localDateAndTime += String(minute(varTime)); 619 } 620 621 // Load this into the array of what needs to be shown 622 for (int i = 0; i < localDateAndTime.length(); i++) { 623 if (localDateAndTime.charAt(i) != " ") { 624 digitToShow[i] = digitToSegment[int(localDateAndTime.charAt(i)) - 48]; 625 } else { 626 digitToShow[i] = 0; 627 } 628 } 629} 630 631void showTimesWithAnimation() { 632 nextAnimation++; 633 if (nextAnimation > 6) { 634 nextAnimation = 0; 635 } 636 switch (nextAnimation) { 637 case 0: // From center 638 loadTimes(); 639 printFromCenter(15); 640 break; 641 case 1: // From left 642 loadTimes(); 643 printFromLeft(15,false); 644 break; 645 case 2: // Random segment 646 loadTimes(); 647 printRandomSegment(5); 648 break; 649 case 3: // By counting up two digits at a time 650 displayTimesByCountingUp(); 651 break; 652 case 4: // By counting up one digit at a time 653 displayTimesByCountingUpDigitByDigit(); 654 break; 655 case 5: // From exterior 656 loadTimes(); 657 printFromExterior(15); 658 break; 659 case 6: // By counting up across all displays at once 660 displayTimesByCountingAcrossAllDisplays(30); 661 break; 662 } 663 loadTimes(); 664 updateAllDisplays(); 665} 666 667void removeTimesWithAnimation() { 668 for (int i = 0; i < numDigits; i++) { 669 digitToShow[i] = 0; 670 } 671 switch (nextAnimation) { 672 case 0: // From center 673 printFromExterior(15); 674 break; 675 case 1: // From left 676 printFromLeft(15,false); 677 break; 678 case 2: // Random segment 679 printRandomSegment(5); 680 break; 681 case 3: // By counting up two digits at a time 682 printRandomSegment(5); 683 break; 684 case 4: // By counting up one digit at a time 685 printRandomSegment(5); 686 break; 687 case 5: // From exterior 688 printFromCenter(15); 689 break; 690 case 6: // From exterior 691 printFromCenter(15); 692 break; 693 } 694 initDisplays(); 695} 696 697void turnAllLabelsOff() { 698 for (int i = 0; i < numDigitDisplays; i++) { 699 digitalWrite(timeZoneLabels[i], LOW); 700 } 701} 702 703void turnAllLabelsOn() { 704 for (int i = 0; i < numDigitDisplays; i++) { 705 digitalWrite(timeZoneLabels[i], HIGH); 706 } 707} 708 709void loop() { 710 711 static int8_t val[2]; 712 713 // Update time only if minute has changed 714 if ( prevMinute != minute() ) { 715 prevMinute = minute(); 716 717 // If local date and time are not currently shown... 718 if (!showDateAndTime) { 719 720 // Update all timezones shown 721 loadTimes(); 722 updateAllDisplays(); 723 724 // Animate labels at top of hour and 30 minutes into the hour during business hours of weekdays 725 // Get local time 726 varTime = Timezones[localTimezone].toLocal(now()); 727 // If now is a weekday (Monday - Friday) 728 if ((weekday(varTime) > 1) && (weekday(varTime) < 7)) { 729 // ...and now is between 8:00 and 17:59 730 if ((hour(varTime) > 7) && (hour(varTime) < 18)) { 731 // ...and we are at the top of the hour or 30 minutes into the hour... 732 if ((minute(varTime) == 0) || (minute(varTime) == 30)) { 733 switch (nextLabelAnimation) { 734 case 0: // Random flash 735 turnAllLabelsOff(); 736 for (int j = 0; j < 150; j++) { 737 long randomLabel = random(numDigitDisplays); 738 digitalWrite(timeZoneLabels[randomLabel], HIGH); 739 delay(50); 740 digitalWrite(timeZoneLabels[randomLabel], LOW); 741 delay(50); 742 } 743 break; 744 case 1: // Flash several times fast then pause before repeating 745 for (int k = 0; k < 15; k++) { 746 for (int j = 0; j < 6; j++) { 747 turnAllLabelsOff(); 748 delay(50); 749 turnAllLabelsOn(); 750 delay(50); 751 } 752 turnAllLabelsOff(); 753 delay(400); 754 } 755 break; 756 case 2: // "Kit" car effect 757 turnAllLabelsOff(); 758 for (int j = 0; j < (15000 / (numDigitDisplays * 100)); j++) { 759 for (int i = 0; i < numDigitDisplays; i++) { 760 digitalWrite(timeZoneLabels[i], HIGH); 761 delay(50); 762 digitalWrite(timeZoneLabels[i], LOW); 763 } 764 for (int i = numDigitDisplays - 1; i >= 0; i--) { 765 digitalWrite(timeZoneLabels[i], HIGH); 766 delay(50); 767 digitalWrite(timeZoneLabels[i], LOW); 768 } 769 } 770 break; 771 case 3: // All flashing on & off 772 for (int j = 0; j < (15000 / 200); j++) { 773 turnAllLabelsOff(); 774 delay(100); 775 turnAllLabelsOn(); 776 delay(100); 777 } 778 break; 779 case 4: // All flashing progressively faster and faster 780 int delayMillis = 250; 781 int numLoops = 1; 782 do { 783 for (int j = 0; j < numLoops; j++) { 784 turnAllLabelsOff(); 785 delay(delayMillis); 786 turnAllLabelsOn(); 787 delay(delayMillis); 788 } 789 delayMillis = delayMillis - 30; 790 numLoops = numLoops * 2; 791 } while (delayMillis > 0); 792 break; 793 } 794 turnAllLabelsOn(); 795 nextLabelAnimation++; 796 if (nextLabelAnimation > 4) { 797 nextLabelAnimation = 0; 798 } 799 } 800 } 801 } 802 } else { // Local date and time are shown... 803 // ...so update it 804 updateAllDisplays(); 805 } 806 } 807 808 // Remove local date and time if it has been shown for 10 seconds already 809 if ((showDateAndTime) && (millis() > (startTime + 10000))) { 810 initDisplays(); 811 delay(250); 812 showTimesWithAnimation(); 813 showDateAndTime = false; 814 } 815 816 // Read rotary encoder and process if it has been moved 817 val[0] = read_rotary(0); 818 if (val[0] != 0) { 819 if (val[0] < 0) { // Lower intensity of displays if turned counter-clockwise 820 for (int i = 0; i < numDigitDisplays; i++) { 821 displayIntensity[i] = displayIntensity[i] - 1; 822 if ( displayIntensity[i] < 0 ) {displayIntensity[i] = 0;} 823 DigitDisplay[i].setBrightness(displayIntensity[i], true); 824 } 825 } else { // Increase intensity of displays if turned clockwise 826 for (int i = 0; i < numDigitDisplays; i++) { 827 displayIntensity[i] = displayIntensity[i] + 1; 828 if ( displayIntensity[i] > 7 ) {displayIntensity[i] = 7;} 829 DigitDisplay[i].setBrightness(displayIntensity[i], true); 830 } 831 } 832 updateAllDisplays(); 833 } 834 835 // Rotary push button 836 currentMenuButton = debounce(lastMenuButton, menuButton); 837 if (lastMenuButton == HIGH && currentMenuButton == LOW) { // If pushbutton has been pushed... 838 if (!showDateAndTime) { // Show local date and time if not currently being shown 839 // Load local date and time and then show it 840 initDisplays(); 841 delay(250); 842 loadLocalDateAndTime(); 843 updateAllDisplays(); 844 digitalWrite(timeZoneLabels[localTimezone], HIGH); 845 startTime = millis(); 846 showDateAndTime = true; 847 } else { // Otherwise remove local date and time 848 initDisplays(); 849 delay(250); 850 showTimesWithAnimation(); 851 showDateAndTime = false; 852 } 853 } 854 lastMenuButton = currentMenuButton; 855} 856 857unsigned long getDCFTime() 858{ 859 time_t DCFtime = DCF.getUTCTime(); // Convert from UTC 860 861 if (DCFtime!=0) { 862 return DCFtime; 863 } 864 return 0; 865} 866 867// Rotary encoder processor (bullet proof!!!) 868// A valid CW or CCW move returns 1 or -1, invalid returns 0 869int8_t read_rotary(int i) { 870 871 static uint8_t prevNextCode[1] = {0}; 872 static uint16_t store[1] = {0}; 873 static int8_t rot_enc_table[] = {0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0}; 874 prevNextCode[i] <<= 2; 875 if (digitalRead(DATA[i])) prevNextCode[i] |= 0x02; 876 if (digitalRead(CLK[i])) prevNextCode[i] |= 0x01; 877 prevNextCode[i] &= 0x0f; 878 879 // If valid then store as 16 bit data. 880 if (rot_enc_table[prevNextCode[i]] ) { 881 store[i] <<= 4; 882 store[i] |= prevNextCode[i]; 883 if ((store[i]&0xff)==0x2b) return -1; 884 if ((store[i]&0xff)==0x17) return 1; 885 } 886 return 0; 887} 888 889// Button debouncer 890boolean debounce(boolean last, int pin) { 891 boolean current = digitalRead(pin); 892 if (last != current) { 893 delay(5); 894 current = digitalRead(pin); 895 } 896 return current; 897}
Comments
Only logged in users can leave comments