Components and supplies
Adafruit FX Soundboard - 2mb with 2 x 2 watt amp
DHT22 temperature-humidity sensor
SPDT Center off toggle switch
Pushbutton Switch, Pushbutton
AdaFruit DS3231 RTC
Arduino Mega 2560
Speaker: 0.25W, 8 ohms
Adafruit SSD1306 OLED display
Project description
Code
Matrix Clock code
arduino
Arduino Mega code to power clock
1 2 3 4/* 5 Clock project using two MX7219 matrix displays and the AdaFruit FX Soundboard 6 Provides date, time, temp, humidity and event notifications in rotating order 7 Also can chime in multiple ways, including Westminster chimes 8 Chimes are provided by Adafruit FX Soundboard in GPIO mode 9 There are some conditional options: 10 Davids_Clock changes some of the events and notifications 11 Extra_Display uses a small Adafruit 1306 OLED to continuously show date and time while the main display 12 rolls through its iterations 13 DDT includes a couple of small debugging (get it?) routines and opens serial terminal 14 15*/ 16 17 18 19 20// Code to set up the clock 21// Pushbutton 1 is the toggler , cycle between year, month, day, hours, minutes, seconds 22// Pushbutton 2 is the increment up one 23// Pushbutton 3 is the decrement down one 24 25// Chime setup for toggle switch 26// Center = No chimes 27// Up = Westminster hours + quarterly 28// Down = Simple Bell (hours and 1/2 hour) 29 30// Comment out next line to leave out the little show 31 32#define showoffcode // include "show off" graphics and music for Easter Egg 33 34//#define Davids_Clock // if this is david's clock slightly different announcements 35 36#define Extra_Display // Enables constant date and time in OLED display so never have to wait 37 38#define DDT // simple debug tools (print string, print string and value, same with NL) 39 40 41// Clock "ID's" for temperature adjustment 42// Define only one of these! 43 44//#define Clock_1 // First wine box clock (mine) 45//#define Clock_2 // 2nd wine box clock (For David) 46#define Clock_3 // Acrylic long matrix clock (Mercer Island) 47 48 49// include the libraries for LCD Display, DHT-11 Temperature/humidity sensor and DS3231 RTC and 50// include libraries for MX7219 mult-unit matrix display 51#include "DHT.h" 52#include <Wire.h> 53#include "RTClib.h" 54#include <MD_MAX72xx.h> 55 56// uncomment next two lines to use UART for SoundBoard rather than GPIO 57//#include <Adafruit_Soundboard.h> 58//#include <SoftwareSerial.h> 59 60#ifdef Extra_Display 61#include <Adafruit_GFX.h> // for OLED need graphics and 62#include <Adafruit_SSD1306.h> // the specific driver 63#endif 64 65 66// Set up output functions 67 68#define DHTPIN 9 //Temp Humidity sensor on pin 9 69#define DHTTYPE DHT11 // DHT 11 because the sensor is that type 70 71// Initialize DHT sensor for normal 16mhz Arduino 72DHT dht(DHTPIN, DHTTYPE); 73 74// Initialize for Adafruit DS3231 RTC real time clock 75RTC_DS3231 rtc; 76 77#ifdef Extra_Display // used to include a small OLED date/time display 78// Initialize the Small OLED display 79#define SCREEN_WIDTH 128 // OLED display width, in pixels 80#define SCREEN_HEIGHT 64 // OLED display height, in pixels 81 82// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) 83//#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) 84#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) 85Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); 86#endif Extra_Display 87 88 89// uncomment next set of lines to use UART for SoundBoard 90/* 91 // Choose any two pins that can be used with SoftwareSerial to RX & TX for UART SoundBoard 92 //#define SFX_TX 5 93 //#define SFX_RX 6 94 // Connect to the RST pin on the Sound Board 95 #define SFX_RST 4 96 // You can also monitor the ACT pin for when audio is playing! 97 // we'll be using software serial 98 //SoftwareSerial ss = SoftwareSerial(SFX_TX, SFX_RX); 99 // pass the software serial to Adafruit_soundboard, the second 100 // argument is the debug port (not used really) and the third 101 // arg is the reset pin 102 //Adafruit_Soundboard sfx = Adafruit_Soundboard(&ss, NULL, SFX_RST); 103 // can also try hardware serial with 104 // Adafruit_Soundboard sfx = Adafruit_Soundboard(&Serial1, NULL, SFX_RST); 105*/ 106 107// Define the number of devices we have in the chain and the hardware interface 108// NOTE: These pin numbers will probably not work with your hardware and may 109// need to be adapted 110#define HARDWARE_TYPE MD_MAX72XX::FC16_HW // NOTE: This parameter differs by vendor 111#define MAX_DEVICES 8 // 2 x 4 matrices per unit 112 113//Pins for Arduino Uno 114//#define CLK_PIN 13 // or SCK (this is for the UNO, different for the Mega 115//#define DATA_PIN 11 // or MOSI 116//#define CS_PIN 10 // or SS 117 118 119//Pins for Arduino Mega 120#define CLK_PIN 52 // or SCK 13 121#define DATA_PIN 51 // or MOSI 11 122#define CS_PIN 53 // or SS 10 123 124 125// SPI hardware interface 126MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); 127 128// We always wait a bit between updates of the display 129#define DELAYTIME 100 // in milliseconds 130 131// Text parameters 132#define CHAR_SPACING 1 // pixels between characters 133//const int CHAR_PIXELS = 6; // Pixels taken by each character (including trailing space) 134//const int MAX_CHARS = (MAX_DEVICES*COL_SIZE)/ CHAR_PIXELS; // Maximum characters at once (happens to be 10 btw) 135//const int LEFT_OVER = (MAX_DEVICES*COL_SIZE)- (CHAR_PIXELS*MAX_CHARS); //Since its not 'even' additional pad from front on printText 136const int BufferStringLength = 31; 137char BufferString [BufferStringLength]; //used to construct printouts 138#define DecToAscii 48 // convert number to ascii (add as a constant) 139char DegreeSign = '~'; // for display of 'degrees' after temperature 140int TDigits[4] = {0, 0, 0, 0}; // used to convert four digits to four chacacters 141#define TDOnes 3 // positions in arrays and templates 142#define TDTens 2 143#define TDHundreds 1 144#define TDThousands 0 145#define sunday 0 // used for events, floating events and holidays 146#define monday 1 // just for readability 147#define tuesday 2 148#define wednesday 3 149#define thursday 4 150#define friday 5 151#define saturday 6 152#define january 1 153#define february 2 154#define march 3 155#define april 4 156#define may 5 157#define june 6 158#define july 7 159#define august 8 160#define september 9 161#define october 10 162#define november 11 163#define december 12 164 165//Template Strings and pointers 166 167const int LOTS = 13; // Length of TimeString (without terminator) 168char TimeString[LOTS + 1 ] = {' ', ' ', '1', '2', ':', '3', '1', ':', '4', '5', ' ', 'P', 'M', '\\0'}; 169char DateString[ ] = {'1', '2', '/', '3', '1', '/', '2', '0', '1', '9', '\\0'}; 170char TempString[ ] = {'T', 'e', 'm', 'p', ' ', '7', '2', '.','0', DegreeSign,'F', '\\0'}; 171char HumidString[ ] = {'H', 'u', 'm', ' ', '2', '5', '.', '0', '%', '\\0'}; 172const int THptr = 2; // Pointers within the strings 173const int TMptr = 5; // for loading values 174const int TSptr = 8; 175const int TAMPMptr = 11; 176const int DMptr = 0; 177const int DDptr = 3; 178const int DYptr = 6; 179const int TEMPptr = 5; 180const int HUMIDptr = 4; 181 182// Differentiated Displays 183 184const int DisplayTime = 0; 185const int DisplayDate = 2; 186const int DisplayDOW = 1; 187const int DisplayTemp = 3; 188const int DisplayHumid = 4; 189const int DisplayEvent = 5; 190const int DisplayEventRepeat = 2; //times to repeat the scrolling 'event' display 191const int DisplaySize = 6; // Nunber of different displays 192int DisplayIndex = DisplayTime; // display we are on at the time 193const int DisplayDelayArray[DisplaySize] = {100, 50, 30, 30, 30, 20}; // multiplier for each event 194const int DisplayDelay = 100; // milliseconds to leave each display * its multiplier 195unsigned long DisplayTimer; // used to time each matrix display 196 197 198 199char daysOfTheWeek[7][12 ] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; 200 201 202 #ifndef Davids_Clock 203 204const int eventnumber = 50; // number of events and remember: No strings longer than 30 characters! 205 206int eventmonth [eventnumber] = {june, april, november, february, july, december, november, june, december, june, december, january, 207 september, october, march, february, february, july,december, february, december, january, january, february, 208 march,april,april,april,april,may,may,may,may,june,june,june,july,july,october, september, may, april, 209 march,february,march,october,march,november,august,october 210 }; 211int eventday [eventnumber] = {11, 1, 23, 12, 4, 3, 12, 29, 25, 16, 31, 1, 3, 31, 17, 14, 2, 14, 30,12,21,5,18,18,6,7,12,15,24,1,5,20,30,14,14,21, 212 22,24,29,28,18,26,14,3,27,12,30,5,5,4}; 213 214char* eventdescription [eventnumber] = 215{ "Dana's Anniversary", "April Fool's Day", "Joan's Birthday", "Sydney's Birthday", "July 4th!", "Dana's Birthday", 216 "David's Birthday", "Our Anniversary", "Merry Christmas", "Leah's Birthday", "New Year's Eve", "New Year's Day", 217 "Forrest's Birthday", "Happy Halloween", "Saint Patrick's Day", "Valentine's Day", "Groundhog Day", "Bastille Day", "Linsey's Birthday", 218 "Irwin's Birthday", "Winter Solstice", "National Bird Day", "National Winnie the Pooh Day", "National Drink Wine Day","National Oreo Day", 219 "National Beer Day","Grilled Cheese Day", "Tax Day","National DNA Day", "May Day", "Cinco de Mayo", "World Bee Day", "World MS Day", 220 "National Bourbon Day", "Flag Day", "Summer Solstice","National Hot Dog Day", "National Tequila Day","National Cat Day", "National Drink Beer Day", 221 "National Whisky Day", "Nat'l Audubon Day", "National Pi Day", "Nat'l Women Physicians Day", "National Paella Day", "National Gumbo Day", 222 "National Doctors Day", "National Doughnut Day", "National Oyster Day", "National Taco Day" 223}; 224#endif 225#ifdef Davids_Clock 226const int eventnumber = 43; // number of events and remember: No strings longer than 30 characters! 227 228int eventmonth [eventnumber] = {june, april, november, february, july, december, november, june, december, june, december, january, 229 september, october, march, february, february, july,december, february, december, 230 march,april,april,april,may,may,may,june,june,june,july,july,september, 231 march,february,march,october,march,november,october,august 232 }; 233int eventday [eventnumber] = {11, 1, 23, 12, 4, 3, 12, 29, 25, 16, 31, 1, 3, 31, 17, 14, 2, 14, 30,12,21,18,6,7,12,15,1,5,20,14,14,21, 234 22,24,28,14,3,27,12,30,5,4,4}; 235 236char* eventdescription [eventnumber] = 237{ "Dana's Anniversary", "April Fool's Day", "Mom's Birthday", "Sydney's Birthday", "July 4th!", "Dana's Birthday", 238 "My Birthday", "Mom & Dad's Anniversary", "Merry Christmas", "Leah's Birthday", "New Year's Eve", "New Year's Day", 239 "Forrest's Birthday", "Happy Halloween", "Saint Patrick's Day", "Valentine's Day", "Groundhog Day", "Bastille Day", "Linsey's Birthday", 240 "Dad's Birthday", "Winter Solstice", "National Drink Wine Day","National Oreo Day", 241 "National Beer Day","Grilled Cheese Day", "Tax Day", "May Day", "Cinco de Mayo", "World Bee Day", 242 "National Bourbon Day", "Flag Day", "Summer Solstice","National Hot Dog Day", "National Tequila Day","National Drink Beer Day", 243 "National Pi Day", "Nat'l Women Physicians Day", "National Paella Day", "National Gumbo Day", "National Doctors Day", "National Doughnut Day", 244 "National Taco Day", "Nat'l Champagne Day" 245}; 246#endif 247 248 249bool event; // logic flag "on" = event found 250int eventindex; // and the event we found 251const int maxevents = 3; // maximum of three events (real and floating combined) 252char* eventstrings[maxevents] ; // used to store displayed events 253String floatstring = " "; // used for floating events (e.g., Thanksgiving) 254 255#define pb1pin 2 // Pin assignments for reset, increment and decrement 256#define pb2pin 3 257#define pb3pin 4 258 259#define showoffbuttonpin 7 // invoke showing off the matrix display 260 261#define DoWestminster 5 262#define DoHoursOnly 6 263#define silent 0 // no chiming at all 264#define hoursonly 1 // no prelude, just chime number of hours and half hour single chime 265#define westminster 2 // hours + Westminster prelude per quarter 266int ChimeValue = silent; // default to silent 267 268 269const int LEDpin = A0; // Pin assignment for analog reading LDR for LED brightness 270const int minbright = 0; // MAX_BRIGHT for this module is 15 271const int midbright = 2; // but these values "seem" to work 272const int maxbright = 4; 273 274 275 276 277int setupindex = 0; // first thing to set if required 278bool insetupmode = false; // and assume RTC is set, OK, etc., and no 'set' required 279bool setinsetup = false; // flag set to true at interrupt level to go back into setup mode 280 281// date time array for setting, reading, displaying 282 283#define setsize 6 // size of the setting array 284#define setyear 0 // index name for each element 285#define setmonth 1 286#define setday 2 287#define sethour 3 288#define setminute 4 289#define setsecond 5 290 291int setarray [setsize] = {2019, 1, 1, 1, 1, 0}; // set year, month, day, hour, minutes, seconds 292int lowlimit [setsize] = {2019, 1, 1, 0, 1, 0}; // lower limit for each 293int highlimit [setsize] = {2080, 12, 31, 23, 59, 59}; //high limit for each 294const int setdesclength = 4; // maximum length of 'set' descriptor 295char setdesc [setsize] [setdesclength] = {"Yr ", "Mon", "Day", "Hr ", "Min", "Sec"}; 296 297 298#define cqtr0 1 // Chime the hour 299#define cqtr1 2 // Chime the quarter hour 300#define cqtr2 3 // Chime the half hour 301#define cqtr3 4 // Chime the 3/4 hour 302 303// Definitions for the Sound Board 304#define SoundTruncatedSingle 0 // Truncated Single Westminster 305#define SoundQ1 1 // First Quarter 306#define SoundQ2 2 // Second Quarter 307#define SoundQ3 3 // Third 308#define SoundQ4 4 // Fourth (before hour chime) 309#define SoundTrailingSingle 5 // Finish Westminster string of chimes with longer sound tail 310#define SoundTruncatedBell 6 // Simple Bell 311#define SoundTrailingBell 7 // Final Bell (like single, has longer 'tail') 312#define SoundStartup 8 // startup sound 313#define SoundShowOff 9 // Play during show off of matrix 314#define FirstSoundPin 30 // first pin to use for soundboard (0-10 available on soundboard); 315#define SizeSoundPin 10 // number of pins used (must be consecutive) 316#define fxset 150 // Time soundboard must be held LOW to register (documentation says 125 ms) 317#define SoundBusyPin 8 // Soundboard pin that goes LOW when board is active 318 319const int SizeQueue = 15; // Assume Q4+12 bells is the maximum that'll be in the queue at any point 320int SoundQueue[SizeQueue]; // Circular Queue for waiting sounds 321int SoundQueuePtr = -1; //pointer for Queue fill Position 322int SoundPlayPtr = -1; //pointer for Queue play Position 323int SoundQueueCtr = 0; // number of items in the queue 324 325int setstrike = -1; // Chime/strike flag 326byte alreadychimed = false ; // Used to keep from chiming multiple times during the "hot" second 327const int BounceDelay = 250; // Not really 'bounce', its a change of state detection 328 329int i; // generic index variable 330 331int dayoftheweek; // stored day of the week (0-6, 0 = Sunday) 332 333int phours; // for print conversion of military time 334 335float temperaturef; // farenheit temperature back 336int temperature; // integer version of temperature for matrix 337float temperaturec; // centrigade temperature back (not used) 338 339float humidityf; // and humidity 340int humidity ; // and same as temp 341 342// The DHT sensors are cheap and inaccurate so these are 'custom' adjustments 343 344#ifdef Clock_1 // first wine box clock 345float tempadjust = -2.0; // temperature adjustment for sensor (I found it didn't read right against 'comps') 346float humidadjust = +8.0; // corresponding humidity adjustment 347#endif 348 349#ifdef Clock_2 // second wine box clock for David 350float tempadjust = -0.6; // temperature adjustment for sensor 351float humidadjust = +8.0; 352#endif 353 354#ifdef Clock_3 // clear clock 355float tempadjust = 0.0; 356float humidadjust = +8.0; 357#endif 358 359 360#ifdef DDT // debugging tools 361void DDTl(String st, int vt) { // Print descriptor and value and new line 362 DDTs(st, vt); 363 Serial.println(" "); 364} 365void DDTs(String st, int vt) { // Print descriptor and value 366 Serial.print(" "); 367 Serial.print(st); 368 Serial.print(" "); 369 Serial.print(vt); 370} 371void DDTt(String st) { // Print descriptor and value and new line 372 Serial.println(st); 373} 374#endif //DDT// 375 376 377void setup() 378// put your setup code here, to run once: 379 380{ 381 Wire.begin(); // initialize I2C interface 382 dht.begin(); // initialize the temp/humidity sensor 383 mx.begin(); // and MX7219 display 384 #ifdef DDT 385 Serial.begin (9600); //Terminal monitor printing for debugging 386 #endif 387 388 // softwareserial at 9600 baud (uncomment this line and next for UART control of sound board 389 // ss.begin(9600); 390 391#ifdef OLED_Display 392// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally 393 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64 394 scrollText("Small OLED display startup failed"); 395 delay(2000);} else //and wait 396 { display.clearDisplay(); // give a little startup msg 397 display.setTextSize(2); // Normal 1:1 pixel scale 398 display.setTextColor(SSD1306_WHITE); // Draw white text 399 display.setCursor(0,0); // Start at top-left corner 400 display.println("Hello"); 401 display.setTextSize(2); 402 display.println("Starting"); 403 display.setTextSize(3); // Draw 2X-scale text 404 display.setTextColor(SSD1306_WHITE); 405 display.print(" UP! "); 406 display.display(); 407 delay(1500); 408 } 409#endif OLED_Display 410 411 pinMode(pb1pin, INPUT_PULLUP); // The three pushbuttons - reset 412 pinMode(pb2pin, INPUT_PULLUP); // increment 413 pinMode(pb3pin, INPUT_PULLUP); // decrement 414 415 pinMode (DoWestminster, INPUT_PULLUP); // When pulled 'low' we want Westminster chimes 416 pinMode (DoHoursOnly , INPUT_PULLUP); // When pulled 'low' we want hours and 1/2 bell (neither is 'silent') 417 418 #ifdef showoffcode 419 pinMode(showoffbuttonpin, INPUT_PULLUP) ; // if show off matrix is there, initialize pin 420 #endif //showoffcode// 421 422 for (i = FirstSoundPin; i < FirstSoundPin + SizeSoundPin; i++) { // pins for sound board 423 pinMode(i, OUTPUT); // each an output 424 digitalWrite(i, HIGH); // and initialize high (off) 425 } 426 427 // set up interupt for PB1 // if PB1 is pushed, it'll pick up on next release from Matrix display 428 attachInterrupt(digitalPinToInterrupt(pb1pin), SetToSetup, FALLING); // and re-enter setup mode (after the end of current display) 429 430 QueueSound(SoundStartup); // play boot up sound 431 PlaySound(); 432 433 if (! rtc.begin()) { // check that clock is there 434 scrollText("Couldn't find RTC"); // clock missing is a fatal error 435 while (1); 436 } 437 438 if (rtc.lostPower()) { // if power lost force a setup, else load 'current' values and 439 scrollText("RTC lost power!"); 440 delay(1000); 441 scrollText("Replace battery?"); 442 delay(1000); 443 scrollText("Setup Mode: "); 444 setupindex = setyear - 1; // setup differently (becuase it will increment) 445 insetupmode = true; 446 for (i = 0; i < setsize; i++) { 447 setarray[i] = lowlimit[i]; 448 } 449 } else // else reload from RTC 450 { DateTime (setarray[setyear], setarray[setmonth], setarray[setday], setarray[sethour], setarray[setminute], setarray[setsecond]) = rtc.now(); 451 insetupmode = false; // not in setup mode, and 452 setinsetup = false; //no interrupt 453 } 454 455 // Two alternative modes of setting date and time (for debugging, just un-comment): 456 // This line sets the RTC to the date & time this sketch was compiled 457 // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); 458 // This line sets the RTC with an explicit date & time, for example to set 459 // January 21, 2014 at 3am you would call: 460 // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); 461 LEDBrightness(); // Do an initial check for room brightness 462} // end of setup 463 464void loop() { // Main code divided into setup mode and non-setup mode 465 466 while (insetupmode) { // code for setting time, date set, etc. 467 468 // Read the increment button 469 470 if (digitalRead(pb2pin) == LOW) { 471 setarray[setupindex]++; // read increment of current item 472 delay(BounceDelay); 473 if (setarray[setupindex] > highlimit[setupindex]) { 474 setarray[setupindex] = lowlimit[setupindex]; 475 } 476 ShowValue(setupindex, setdesc[setupindex]); // and display description and value 477 } 478 479 // Read the decrement Button 480 if (digitalRead(pb3pin) == LOW) { 481 setarray[setupindex]--; // read decrement of value 482 delay(BounceDelay); 483 if (setarray[setupindex] < lowlimit[setupindex]) { 484 setarray[setupindex] = highlimit[setupindex]; 485 } 486 ShowValue(setupindex, setdesc[setupindex]); // and display description and value 487 } 488 489 // Read for another increment of index 490 491 if (digitalRead(pb1pin) == LOW) { // Rolling through Chime, Year, Month, Day, Hour, Minutes, Seconds 492 setupindex++ ; // increment index 493 delay(BounceDelay); 494 if (setupindex < setsize) { 495 ShowValue(setupindex, setdesc[setupindex]); // show description and value if in bounds 496 } 497 if (setupindex >= setsize) { // and finally exiting setup mode after setting chime, date and time 498 rtc.adjust(DateTime(setarray[setyear], setarray[setmonth], setarray[setday], setarray[sethour], setarray[setminute], setarray[setsecond])); 499 insetupmode = false; 500 DisplayIndex = DisplayTime; 501 } //exit setup mode when done 502 } 503 504 } // End of "While" for setup 505 506 // Begin regular loop for date, time, temp humidity and event display 507 508 while (!insetupmode) { 509 510 GetTempandHumid(); // read temperature and humidity from sensor 511 512 ChimeValue = ReadChimeSetting(); // read SPDT Center off switch for silent, westminster or hours only (and 1/2) 513 514 GetTheDate(); // load date array from RTC for a chime/bell check 515 516 CheckForChime(); // Check for a chime event between displays 517 518 CheckForEvent(setarray[setmonth], setarray[setday], dayoftheweek); // check for static and floating events 519 520 MatrixDisplay(); // Do whatever display is required on the MX7219 521 522 #ifdef OLED_Display // if the little OLED display is used 523 OLED_Update(); // update it 524 #endif Extra display 525 526 LEDBrightness(); // Good place to check for change in room brightness 527 528 // end of display update logic 529 530 // Read a potential request for an entry into setup from PB 1 531 if ((digitalRead(pb1pin) == LOW) || setinsetup) { // to see if we go back to setup mode 532 insetupmode = true; // via a pushbutton or the interrupt 533 setinsetup = false; // clear the interrupt flag 534 setupindex = 0; // re-initialize to 'Year' in setup 535 DisplayIndex = DisplayTime; // and go back to time display when exit setup 536 scrollText("Setup Mode: "); 537 delay(2000); 538 ShowValue(setupindex, setdesc[setupindex]); // show the first setup item (Year) 539 delay(BounceDelay); 540 } 541 #ifdef showoffcode 542 if (digitalRead(showoffbuttonpin) == LOW) {ShowOff();} // check for want to show off matrix 543 #endif //showoffcode// 544 545 } // end of not in setup 546} // end of sketch 547 548void MatrixDisplay() { // Main display routine for display sequences 549 550 Scrollup(); // Clear last display 551 if (DisplayIndex >= DisplaySize) { 552 DisplayIndex = DisplayTime; // reset if at the end 553 } 554 DisplayTimer = DisplayDelayArray[DisplayIndex] * DisplayDelay; // set individual display time 555 556 switch (DisplayIndex) { // and do next display 557 558 case DisplayTime: // Dislay the time 559 do // time is different in that there's a constant 560 { // update of time during display and also play chime, bell etc. 561 GetTheDate(); // Get Current Time 562 #ifdef OLED_Display // If the OLED is there, 563 OLED_Update(); 564 #endif OLED_Display 565 CheckForChime(); // Check for a chime event 566 LoadNumber(phours); // Load the 4 digit character array from number 567 TimeString[THptr] = TDigits[TDTens]; // left digit of the two digit hour 568 TimeString[THptr + 1] = TDigits[TDOnes]; // rightmost digit 569 if (TimeString[THptr] == '0') { 570 TimeString[THptr] = ' '; // eliminate leading zero 571 } 572 LoadNumber(setarray[setminute]); // do same for minutes 573 TimeString[TMptr] = TDigits[TDTens]; // except no need to do space for zero 574 TimeString[TMptr + 1] = TDigits[TDOnes]; 575 LoadNumber(setarray[setsecond]); // seconds then 576 TimeString[TSptr] = TDigits[TDTens]; 577 TimeString[TSptr + 1] = TDigits[TDOnes]; 578 if (setarray[sethour] < 12) // and AM vs PM 579 { 580 TimeString[TAMPMptr] = 'A'; 581 } 582 else 583 { 584 TimeString[TAMPMptr] = 'P'; 585 } 586 printText(TimeString, LOTS, false); // Keep position (no centering) because time keeps updating during display and chimes 587 PlaySound(); // Play any chimes or bells 588 if (SoundQueueCtr == 0) { 589 DisplayTimer = DisplayTimer - DisplayDelay; // if no sound, use display delay logic 590 delay(DisplayDelay); 591 } 592 } while ((DisplayTimer > 0) || (SoundQueueCtr > 0)); // leave this display when no sound or no delay left 593 DisplayIndex++ ; // then move on to next display item 594 break; 595 596 case DisplayDate: // Month, Day and Year Display 597 LoadNumber(setarray[setmonth]); 598 DateString[DMptr] = TDigits[TDTens]; 599 if (DateString[DMptr] == '0') { 600 DateString[DMptr] = ' '; 601 } 602 DateString[DMptr + 1] = TDigits[TDOnes]; 603 LoadNumber(setarray[setday]); 604 DateString[DDptr] = TDigits[TDTens]; 605 DateString[DDptr + 1] = TDigits[TDOnes]; 606 LoadNumber(setarray[setyear]); 607 for (int i = 0; i < 4; i++) { 608 DateString[DYptr + i] = TDigits[i]; 609 } 610 printCenter(DateString); 611 CommonDelay(DisplayTimer); 612 DisplayIndex++ ; 613 break; 614 615 case DisplayDOW: // Just display the day of week string 616 printCenter(daysOfTheWeek[dayoftheweek]); 617 CommonDelay(DisplayTimer); 618 DisplayIndex++ ; 619 break; 620 621 case DisplayTemp: // Temperature display 622 LoadNumber(temperature); 623 TempString[TEMPptr] = TDigits[TDHundreds]; 624 TempString[TEMPptr + 1] = TDigits[TDTens]; 625 TempString[TEMPptr + 3] = TDigits[TDOnes]; 626 printCenter(TempString); 627 CommonDelay(DisplayTimer); 628 DisplayIndex++ ; 629 break; 630 631 case DisplayHumid: // Humidity display 632 LoadNumber(humidity); 633 HumidString[HUMIDptr] = TDigits[TDHundreds]; 634 HumidString[HUMIDptr + 1] = TDigits[TDTens]; 635 HumidString[HUMIDptr + 3] = TDigits[TDOnes]; 636 printCenter(HumidString); 637 CommonDelay(DisplayTimer); 638 DisplayIndex++ ; 639 break; 640 641 case DisplayEvent: // Event Display 642 if (event) { // "Real" events repeat 3 times 643 if (eventindex > 0 ) { // for case where there is more than one event 644 while (eventindex >= 0) 645 {scrollText(eventstrings[eventindex]); // scroll each of them once 646 if (eventindex > 0) { 647 delay(DisplayDelay * 6); 648 printCenter("and"); 649 delay(DisplayDelay * 6);} 650 eventindex--; // and decrement 651 delay(DisplayDelay * 2); 652 } 653 } 654 else { // case of only one event -- repeat it for readability 655 for (int i = 1; i <= DisplayEventRepeat; i++) { // scroll either static or floating event 656 scrollText(eventstrings[eventindex]); // the index is 0 (greater than zero if multiple events) 657 } 658 delay(DisplayDelay * 2); 659 } 660 } // end of "if event" logic 661 if (!event){ // Scroll Default message only once 662 #ifndef Davids_Clock 663 if (setarray[sethour] >= 6 && setarray[sethour] <= 11) { 664 scrollText("Good Morning ");} 665 else if (setarray[sethour] >= 12 && setarray[sethour] <= 16) { 666 scrollText("Good Afternoon");} 667 else if (setarray[sethour] >= 17 && setarray[sethour] <= 21) { 668 scrollText("Good Evening");} else{scrollText("Good Night"); } 669 #endif 670 #ifdef Davids_Clock 671 if (setarray[sethour] >= 6 && setarray[sethour] <= 11) { 672 scrollText("Good Morning, David");} 673 else if (setarray[sethour] >= 12 && setarray[sethour] <= 16) { 674 scrollText("Good Afternoon, David");} 675 else if (setarray[sethour] >= 17 && setarray[sethour] <= 21) { 676 scrollText("Good Evening, David");} else{scrollText("Good Night, David"); } 677 #endif 678 } // end of "if not event" above 679 CommonDelay(DisplayTimer); 680 DisplayIndex++ ; 681 break; 682 683 default: 684 scrollText("Should never get here"); 685 while (1); 686 687 } // End of Switch 688 689} // End of MatrixDisplay 690 691void GetTheDate() { // Read RTC into array 692 DateTime now = rtc.now(); 693 setarray[setyear] = now.year(); 694 setarray[setmonth] = now.month(); 695 setarray[setday] = now.day(); 696 setarray[sethour] = now.hour(); 697 setarray[setminute] = now.minute(); 698 setarray[setsecond] = now.second(); 699 dayoftheweek = now.dayOfTheWeek(); 700 if (setarray[sethour] <= 12) // convert military time to am pm 701 { 702 phours = setarray[sethour]; 703 } 704 else 705 {phours = setarray[sethour] - 12;} 706 if (phours <= 0) { phours = 12; } // don't print 0 for midnite, print "12" 707 708} // End of Get The Date 709 710void GetTempandHumid(){ // Temperature and humidity update 711 712 // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) 713 humidityf = dht.readHumidity()+humidadjust; // read humidity and adjust for any error 714 715 // Read temperature as Fahrenheit 716 temperaturef = (dht.readTemperature(true)) + tempadjust; // read and correct for inaccuracy in sensor 717 718 // Check if any reads failed and exit early (to try again). 719 if (isnan(humidityf) || isnan(temperaturec) || isnan(temperaturef)) { 720 temperaturef = 0; // use 0 as a no-read 721 humidityf = 0; // error indication 722 } 723 temperature = temperaturef * 10.0; // get read for matrix printout 724 humidity = humidityf * 10.0; 725} // end of Temperature and Humidity update 726 727void SetToSetup() { // interrupt level back to setup 728 if ((!insetupmode) && (!setinsetup)) { // skip if we are already in Setup mode or have seen the interrupt 729 setinsetup = true; // Go back into setup at next loop 730 DisplayTimer = 0; 731 } // and zero the timer to speed that up if you can 732} // SetToSetup 733 734void CommonDelay(long int MyDelay) { // Common delay routine 735 DisplayTimer = MyDelay; 736 do { 737 DisplayTimer = DisplayTimer - DisplayDelay; 738 delay(DisplayDelay); 739 GetTheDate(); // load date array from RTC for a chime/bell check 740 CheckForChime(); // if a chime happens, setup to end delay 741 } while ((DisplayTimer > 0) && (!setinsetup) && (SoundQueueCtr <= 0)); // repeat while still time and no interrupt and no sound waiting 742 if (SoundQueueCtr > 0){DisplayIndex = DisplayTime-1;} // if leaving due to chime, go directly to time 743} // End of CommonDelay 744 745 746int ReadChimeSetting() { 747 int returnvalue = silent; // Assume 'silent' (Center position) 748 if (digitalRead(DoWestminster) == LOW) { 749 returnvalue = westminster; 750 } else if (digitalRead(DoHoursOnly) == LOW) { 751 returnvalue = hoursonly; 752 } 753 return returnvalue; 754} 755 756// Routine to deal with combinations of chime type, time of day and the current hour 757// called parameters are chime type (e.g., Westminster), Strike type (hour, qtr, half, 3/4) and the hour in military time 758// Note for delays and print cycles: You need to call this at least once during the 'golden' minute (0,15,30,45) 759 760void CheckForChime() { // Logic for chiming 761 762 setstrike = -1; // initialize 'strike' flag to "no strike" 763 764 if (ChimeValue != silent) // if silenced due to setting silent, skip the whole thing 765 { 766 if (setarray[setminute] == 0) { // Look for 'on the hour' 767 setstrike = cqtr0; 768 } else if (setarray[setminute] == 15 ) { // Look for on the quarter hour 769 setstrike = cqtr1; 770 } else if (setarray[setminute] == 30 ) { // Look for on the 1/2 hour 771 setstrike = cqtr2; 772 } 773 else if (setarray[setminute] == 45 ) { // Look for on the three-quarter hour 774 setstrike = cqtr3; 775 } 776 else { 777 alreadychimed = false; // none of the above, reset ability to chime 778 } 779 780 if (setstrike > 0 && !alreadychimed ) { 781 chime(ChimeValue, setstrike, setarray[sethour]); // call chiming with 'type of chime'; 0,15,30,45 ; and # hours 782 DisplayIndex = DisplayTime; // force display of time while chiming 783 alreadychimed = true; // we will be here multiple times but only chime once 784 } 785 } // end of logic for chiming 786} // end of CheckForChime 787 788void chime (int chimetype, int strikeflag, int chour) { 789 790 int chour1; // am pm variable\\ 791 792 if (chour <= 12) { 793 chour1 = chour; // convert military time to am pm 794 } else { 795 chour1 = chour - 12; 796 } 797 798 799 if (chour1 <= 0) { 800 chour1 = 12; // don't chime 0 for midnite, chime 12 801 } 802 if (chimetype == hoursonly && strikeflag == cqtr2) { 803 QueueSound(SoundTrailingBell); // 1/2 hour only, do single 'ding' 804 } 805 else if ( chimetype == hoursonly && strikeflag == cqtr0) { 806 for (int i = 1; i < chour1; i++) { 807 QueueSound(SoundTruncatedBell); // Ding once less than hours 808 } 809 QueueSound(SoundTrailingBell); 810 } // and follow by trailing bell as last 'ding' (if used) 811 else if (((chimetype == westminster) ) && strikeflag == cqtr1) { 812 QueueSound(SoundQ1); // First Quarter 813 } 814 else if (((chimetype == westminster) ) && strikeflag == cqtr2) { 815 QueueSound(SoundQ2); // Second Quarter 816 } 817 else if (((chimetype == westminster)) && strikeflag == cqtr3) { 818 QueueSound(SoundQ3); // Third Quarter 819 } 820 else if (((chimetype == westminster)) && strikeflag == cqtr0) { 821 QueueSound(SoundQ4); 822 for (int i = 1; i < chour1; i++) { 823 QueueSound(SoundTruncatedSingle); // Chime once less than hours 824 } 825 QueueSound(SoundTrailingSingle); 826 } // Chime Westminster final hours 827 828 829} // end of Chime 830 831 832// Routine to check for 'real' (static) events and floating events (e.g., Memorial Day, Thanksgiving, Mother's Day and Fathers Day) 833 834/* Memorial Day is Last Monday in May 835 Thanksgiving is 4th Thursday in November 836 Mother's Day is 2nd Sunday in May 837 Father's Day is 3rd Sunday in June 838 MLK Day is 3rd Monday in February 839 Memorial Day is last Monday in May 840 Labor Day is first Monday in September 841 Daylight Savings Times starts 2nd Sunday in March 842 Daylight Savings Times ends first Sunday in November 843 Indigenous People's Day is second Monday in October (also its Columbus Day) 844 Oktoberfest is 3rd Saturday in September 845 Election Day is first Tuesday in November 846 National Ice Cream Day is 3rd Sunday in July 847 848*/ 849 850void CheckForEvent(int m, int d, int dow) { // called with Month, Day within month and day of the week (0 = Sunday) 851 852 event = false; // assume neither static or floating 853 eventindex = -1; // initial index to eventstrings 854 855 // Static event check 856 857 for (int i = 0; i < eventnumber; i++) { // then check the static events 858 if ((setarray[setmonth] == eventmonth[i]) && (setarray[setday] == eventday[i])) 859 { event = true; // set if match on month and day 860 eventindex++; // found one! 861 eventstrings[eventindex] = eventdescription[i]; // store pointer to static event description 862 } 863 } 864 865 // Floating event check 866 867 floatstring = "none"; // initialize string 868 869 if ((dow == thursday) && (m == november) && (d >= 22) && (d <= 28)) { // Thanksgiving 870 floatstring = "Thanksgiving"; 871 } 872 else if ((dow == sunday) && (m == may) && (d >= 8) && (d <= 14)) { // Mother's Day 873 floatstring = "Mother's Day"; 874 } 875 else if ((dow == sunday) && (m == june) && (d >= 15) && (d <= 21)) { //Father's Day 876 floatstring = "Father's Day"; 877 } 878 else if ((dow == monday) && (m == january) && (d >= 15) && (d <= 21)) { //MLK Day 879 floatstring = "Martin Luther King Day"; 880 } 881 else if ((dow == saturday) && (m == october) && (d >= 15) && (d <= 21)) { //Oktoberfest starts 882 floatstring = "Oktoberfest Begins"; 883 } 884 else if ((dow == sunday) && (m == july) && (d >= 15) && (d <= 21)) { //Oktoberfest starts 885 floatstring = "National Ice Cream Day"; 886 } 887 else if ((dow == monday) && (m == february) && (d >= 15) && (d <= 21)) { //President's Day 888 floatstring = "President's Day!"; 889 } 890 else if ((dow == monday) && (m == september) && (d <= 7) ) { //Labor Day 891 floatstring = "Labor Day"; 892 } 893 else if ((dow == tuesday) && (m == november) && (d <= 7) ) { //Election Day 894 floatstring = "Election Day"; 895 } 896 else if ((dow == monday) && (m == october) && (d >= 8) && (d <= 14) ) { //Indigenous People's Day 897 floatstring = "Indigenous People's Day"; 898 } 899 else if ((dow == monday) && (m == may) && (d + 7 > 31) ) { //Memorial Day 900 floatstring = "Memorial Day"; 901 } 902 else if ((dow == sunday) && (m == march) && (d >= 8) && (d <= 14)) { // DST begins 903 floatstring = "DST Begins"; 904 } 905 else if ((dow == sunday) && (m == november) && (d <= 7) ) { //DST ends 906 floatstring = "DST Ends"; 907 } 908 909 if (floatstring != "none" ) { // did we load anything? 910 event = true; // if so, then we have one 911 eventindex++; // so load it into event display queue 912 eventstrings[eventindex] = floatstring.c_str(); // store pointer to static event description 913 } 914 915} // end of floatingevent 916 917void LEDBrightness() { 918 int LEDvalue; 919 LEDvalue = analogRead(LEDpin); // Read LDR value (may need to play with values) 920 DDTl("LEDvalue",LEDvalue); 921 //DDTs("LED value",LEDvalue); 922 // LEDvalue = map(LEDvalue, 0, 1023, 0, MAX_INTENSITY); //map to valid value for brightness (max intensity is 15 btw) 923 // if (LEDvalue <= 12) { 924 // i = minbright;} 925 // else if (LEDvalue > 13 ) { 926 // i=maxbright; } 927 // else {i = midbright;} 928 if (LEDvalue >=950) {i=maxbright;} 929 else if (LEDvalue >=700) {i=midbright;} 930 else {i=minbright;} 931 932 mx.control(MD_MAX72XX::INTENSITY, i); 933 // DDTl("Mapped LED value", i); 934 935 936 937} //end of LEDbrightness 938 939void Scrollup() { // used to 'wipe' previous display 940 for (uint8_t i = 0; i < 8; i++) { 941 mx.transform(MD_MAX72XX::TSU); delay(2 * DELAYTIME); 942 delay(DELAYTIME); 943 } 944} // End of Scrollup 945 946#ifdef OLED_Display 947 948void OLED_Update(){ 949 display.clearDisplay(); 950 display.setTextSize(2); // Normal 1:1 pixel scale 951 display.setTextColor(SSD1306_WHITE); // Draw white text 952 display.setCursor(0,0); // Start at top-left corner 953 display.println(" Date/Time"); // header line 954 display.print(setarray[setmonth]); // use mm/dd/yyyy 955 display.print("/"); 956 display.print(setarray[setday]); 957 display.print("/"); 958 display.println(setarray[setyear]); 959 display.setTextSize(3); // Draw 3X-scale text so time is somewhat readable 960 if (setarray[sethour] <=12) // convert military time to am pm 961 {phours = setarray[sethour];} 962 else 963 {phours = setarray[sethour]-12;} 964 if (phours <= 0){phours = 12;} // don't print 0 for midnite, print "12" 965 printtwo(phours," "); // print a standard hh:mm am/pm time on the oled 966 display.print(":"); 967 printtwo(setarray[setminute],"0"); 968 if (setarray[sethour] < 12) 969 {display.print("am");} 970 else 971 {display.print("pm");} 972 display.display(); // update display 973 974} // end of OLED display 975 976void printtwo(int value, String fillchar){ // print two always and use fill to make it so 977 if (value <10) {display.print(fillchar);} // blank space for hours, 0 for minutes, seconds 978 display.print(value); 979 } 980#endif 981 982void scrollText(char *p) // copied from library 983{ 984 uint8_t charWidth; 985 uint8_t cBuf[8]; // this should be ok for all built-in fonts 986 mx.clear(); 987 while (*p != '\\0') 988 { 989 charWidth = mx.getChar(*p++, sizeof(cBuf) / sizeof(cBuf[0]), cBuf); 990 991 for (uint8_t i = 0; i <= charWidth; i++) // allow space between characters 992 {mx.transform(MD_MAX72XX::TSL); 993 if (i < charWidth) 994 mx.setColumn(0, cBuf[i]); 995 delay(DELAYTIME);} 996 } 997} // End of Scroll Text 998 999void printText(char *pMsg, int LOS, bool CenterJustify) // copied and modified from library 1000// Print the text string to the LED matrix modules specified. 1001// Message area is padded with blank columns after printing. 1002// And center justified if third argument is "true" 1003{ 1004 uint8_t modStart = 0; 1005 uint8_t modEnd = MAX_DEVICES - 1; 1006 uint8_t state = 0; 1007 uint8_t curLen; 1008 uint16_t showLen; 1009 uint8_t cBuf[8]; 1010 int16_t col = ((modEnd + 1) * COL_SIZE) - 1; 1011 int pixelcount = 0; 1012 int ccounter = LOS; 1013 1014 mx.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); 1015 1016 do // finite state machine to print the characters in the space available 1017 { switch (state) 1018 { 1019 case 0: // Load the next character from the font table 1020 // if we reached end of message, reset the message pointer 1021 if (*pMsg == '\\0') 1022 { 1023 showLen = col - (modEnd * COL_SIZE); // padding characters 1024 state = 2; 1025 break; 1026 } 1027 1028 // retrieve the next character form the font file 1029 1030 showLen = mx.getChar(*pMsg++, sizeof(cBuf) / sizeof(cBuf[0]), cBuf); 1031 if (ccounter > 0) { 1032 pixelcount = (pixelcount + showLen) + CHAR_SPACING; 1033 ccounter--; 1034 } 1035 curLen = 0; 1036 state++; 1037 // !! deliberately fall through to next state to start displaying 1038 1039 case 1: // display the next part of the character 1040 mx.setColumn(col--, cBuf[curLen++]); 1041 1042 // done with font character, now display the space between chars 1043 if (curLen == showLen) 1044 { 1045 showLen = CHAR_SPACING; 1046 state = 2; 1047 } 1048 break; 1049 1050 case 2: // initialize state for displaying empty columns 1051 1052 curLen = 0; 1053 1054 state++; 1055 // fall through 1056 1057 case 3: // display inter-character spacing or end of message padding (blank columns) 1058 mx.setColumn(col--, 0); 1059 curLen++; 1060 if (curLen == showLen) 1061 state = 0; 1062 break; 1063 1064 default: 1065 col = -1; // this definitely ends the do loop 1066 } 1067 } while (col >= (modStart * COL_SIZE)); 1068 1069 if (CenterJustify) { 1070 for (int i = 1; i <= (((MAX_DEVICES * COL_SIZE) - pixelcount) / 2); i++) { 1071 mx.transform( MD_MAX72XX::TSR); 1072 } 1073 } 1074 mx.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::ON); 1075} 1076void printCenter(String StringtoPrint) // Loads the print buffer and centers it 1077{ 1078 int LOS = StringtoPrint.length(); // Get the string's length 1079 for (int i = 0; i < BufferStringLength; i++) { 1080 BufferString[i] = ' '; // clear buffer 1081 } 1082 for (int i = 0; i < LOS; i++) { 1083 BufferString[i] = StringtoPrint[i]; // transfer 1084 printText(BufferString, LOS, true); // Print, providing length of string and "Yes, center" 1085 1086 } 1087} // End of Center and Load 1088 1089void ShowValue(int myindex, String setdescription) // Display while in setup mode 1090{ // shows what item is setting and its value 1091 1092 for (int i = 0; i < BufferStringLength; i++) {BufferString[i] = ' ';} // clear buffer 1093 setdescription.toCharArray(BufferString, setdesclength); // move description to output buffer 1094 LoadNumber(setarray[setupindex]); 1095 BufferString[setdesclength-1] = ' '; 1096 for (int i = 0; i <setdesclength; i++) {BufferString[i + setdesclength] = TDigits[i];} 1097 if (myindex != setyear) { // only the year is 4 digits, else 2 1098 BufferString[4] = ' '; 1099 BufferString[5] = ' '; 1100 if (BufferString[6] == '0') {BufferString[6] = ' ';} // and suppress leading 0 for 2 digit numbers too 1101 } 1102 BufferString[8] = '\\0'; // and terminate 1103 printText(BufferString, 7, false); 1104 1105 1106} // End of Show Value 1107 1108void LoadNumber(int numtoload) { // Converts number to four digit Ascii array 1109 int tempnum = numtoload; 1110 for (int i = 3; i >= 0; i--) { 1111 TDigits[i] = (tempnum % 10) + DecToAscii;; 1112 tempnum = tempnum / 10; 1113 } 1114} // end of LoadNumber 1115 1116void QueueSound(int SoundFile) { // place a sound to be played into sequence 1117 1118 if ((SoundQueuePtr == SizeQueue) || (SoundQueueCtr <= 0)) { 1119 SoundQueuePtr = -1; // Dont overflow the queue 1120 } 1121 SoundQueuePtr++; // Increment pointer 1122 SoundQueue[SoundQueuePtr] = SoundFile; // and store the sound chosen 1123 SoundQueueCtr++; // and increment # sounds in queue 1124} // end of Queue Sound File 1125 1126void PlaySound() { // Plays appropriate Sound file 1127 if (SoundQueueCtr > 0) { // if nothing in the queue, just return 1128 if (digitalRead(SoundBusyPin) == LOW) { //or if sound card busy, just return 1129 return; 1130 } //until idle 1131 if (SoundPlayPtr == SizeQueue) { 1132 SoundPlayPtr = -1; // Dont go past the queue 1133 } 1134 SoundPlayPtr++; // Increment pointer 1135 digitalWrite(FirstSoundPin + SoundQueue[SoundPlayPtr], LOW); //Play the sound 1136 delay(fxset); // hold to start the sound 1137 digitalWrite(FirstSoundPin + SoundQueue[SoundPlayPtr], HIGH); //Turn off the sound 1138 delay(fxset); // Ensure its set 1139 SoundQueueCtr--; // and decrement sounds yet to be played 1140 if (SoundQueueCtr <= 0) { 1141 SoundPlayPtr = -1; 1142 }; // reset play pointer when queue is empty 1143 } 1144 1145} // end of PlaySound 1146/* 1147 void PlaySoundUART() // uncomment for FX sound board UART use 1148 1149 // sfx.playTrack(name); } 1150 { 1151 Serial.print("\ 1152Playing track \\""); Serial.print(name); Serial.print("\\""); 1153 if (! sfx.playTrack(name) ) { 1154 Serial.println("Failed to play track?"); 1155 } 1156 } 1157*/ 1158 1159 1160#ifdef showoffcode 1161 1162void ShowOff() { // Played at startup -- just the MX Panel Test Graphics 1163 QueueSound(SoundShowOff); // play Game of Thones theme as sound 1164 PlaySound(); 1165 scrollText("Showing Off"); 1166 rows(); 1167 columns(); 1168 cross(); 1169 checkboard(); 1170 bullseye(); 1171 bounce(); 1172 stripe(); 1173 stripe(); 1174 transformation1(); 1175 transformation2(); 1176 bullseye(); 1177 spiral(); 1178 delay(2000); 1179 DisplayIndex = DisplayTime; // force display of time after showing off display 1180} // end of Show Off 1181 1182void rows() // these routines are all 1183// Demonstrates the use of setRow() // from the MX library 1184{mx.clear(); 1185 for (uint8_t row = 0; row < ROW_SIZE; row++) 1186 { 1187 mx.setRow(row, 0xff); 1188 delay(2 * DELAYTIME); 1189 mx.setRow(row, 0x00); 1190 } 1191} 1192 1193void checkboard() 1194// nested rectangles spanning the entire display 1195{ 1196 uint8_t chkCols[][2] = { { 0x55, 0xaa }, { 0x33, 0xcc }, { 0x0f, 0xf0 }, { 0xff, 0x00 } }; 1197 mx.clear(); 1198 for (uint8_t pattern = 0; pattern < sizeof(chkCols) / sizeof(chkCols[0]); pattern++) 1199 { 1200 uint8_t col = 0; 1201 uint8_t idx = 0; 1202 uint8_t rep = 1 << pattern; 1203 1204 while (col < mx.getColumnCount()) 1205 { 1206 for (uint8_t r = 0; r < rep; r++) 1207 mx.setColumn(col++, chkCols[pattern][idx]); // use odd/even column masks 1208 idx++; 1209 if (idx > 1) idx = 0; 1210 } 1211 delay(10 * DELAYTIME); 1212 } 1213} // end of Checkboard 1214 1215void columns() 1216// Demonstrates the use of setColumn() 1217{ 1218 1219 mx.clear(); 1220 1221 for (uint8_t col = 0; col < mx.getColumnCount(); col++) 1222 { 1223 mx.setColumn(col, 0xff); 1224 delay(DELAYTIME / MAX_DEVICES); 1225 mx.setColumn(col, 0x00); 1226 } 1227} 1228 1229void cross() 1230// Combination of setRow() and setColumn() with user controlled 1231// display updates to ensure concurrent changes. 1232{ 1233 1234 mx.clear(); 1235 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); 1236 1237 // diagonally down the display R to L 1238 for (uint8_t i = 0; i < ROW_SIZE; i++) 1239 { 1240 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1241 { 1242 mx.setColumn(j, i, 0xff); 1243 mx.setRow(j, i, 0xff); 1244 } 1245 mx.update(); 1246 delay(DELAYTIME); 1247 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1248 { 1249 mx.setColumn(j, i, 0x00); 1250 mx.setRow(j, i, 0x00); 1251 } 1252 } 1253 1254 // moving up the display on the R 1255 for (int8_t i = ROW_SIZE - 1; i >= 0; i--) 1256 { 1257 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1258 { 1259 mx.setColumn(j, i, 0xff); 1260 mx.setRow(j, ROW_SIZE - 1, 0xff); 1261 } 1262 mx.update(); 1263 delay(DELAYTIME); 1264 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1265 { 1266 mx.setColumn(j, i, 0x00); 1267 mx.setRow(j, ROW_SIZE - 1, 0x00); 1268 } 1269 } 1270 1271 // diagonally up the display L to R 1272 for (uint8_t i = 0; i < ROW_SIZE; i++) 1273 { 1274 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1275 { 1276 mx.setColumn(j, i, 0xff); 1277 mx.setRow(j, ROW_SIZE - 1 - i, 0xff); 1278 } 1279 mx.update(); 1280 delay(DELAYTIME); 1281 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1282 { 1283 mx.setColumn(j, i, 0x00); 1284 mx.setRow(j, ROW_SIZE - 1 - i, 0x00); 1285 } 1286 } 1287 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); 1288} 1289 1290void bullseye() 1291// Demonstrate the use of buffer based repeated patterns 1292// across all devices. 1293{ 1294 1295 mx.clear(); 1296 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); 1297 1298 for (uint8_t n = 0; n < 3; n++) 1299 { 1300 byte b = 0xff; 1301 int i = 0; 1302 1303 while (b != 0x00) 1304 { 1305 for (uint8_t j = 0; j < MAX_DEVICES + 1; j++) 1306 { 1307 mx.setRow(j, i, b); 1308 mx.setColumn(j, i, b); 1309 mx.setRow(j, ROW_SIZE - 1 - i, b); 1310 mx.setColumn(j, COL_SIZE - 1 - i, b); 1311 } 1312 mx.update(); 1313 delay(3 * DELAYTIME); 1314 for (uint8_t j = 0; j < MAX_DEVICES + 1; j++) 1315 { 1316 mx.setRow(j, i, 0); 1317 mx.setColumn(j, i, 0); 1318 mx.setRow(j, ROW_SIZE - 1 - i, 0); 1319 mx.setColumn(j, COL_SIZE - 1 - i, 0); 1320 } 1321 1322 bitClear(b, i); 1323 bitClear(b, 7 - i); 1324 i++; 1325 } 1326 1327 while (b != 0xff) 1328 { 1329 for (uint8_t j = 0; j < MAX_DEVICES + 1; j++) 1330 { 1331 mx.setRow(j, i, b); 1332 mx.setColumn(j, i, b); 1333 mx.setRow(j, ROW_SIZE - 1 - i, b); 1334 mx.setColumn(j, COL_SIZE - 1 - i, b); 1335 } 1336 mx.update(); 1337 delay(3 * DELAYTIME); 1338 for (uint8_t j = 0; j < MAX_DEVICES + 1; j++) 1339 { 1340 mx.setRow(j, i, 0); 1341 mx.setColumn(j, i, 0); 1342 mx.setRow(j, ROW_SIZE - 1 - i, 0); 1343 mx.setColumn(j, COL_SIZE - 1 - i, 0); 1344 } 1345 1346 i--; 1347 bitSet(b, i); 1348 bitSet(b, 7 - i); 1349 } 1350 } 1351 1352 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); 1353} 1354 1355void spiral() 1356// setPoint() used to draw a spiral across the whole display 1357{ 1358 1359 int rmin = 0, rmax = ROW_SIZE - 1; 1360 int cmin = 0, cmax = (COL_SIZE * MAX_DEVICES) - 1; 1361 1362 mx.clear(); 1363 while ((rmax > rmin) && (cmax > cmin)) 1364 { 1365 // do row 1366 for (int i = cmin; i <= cmax; i++) 1367 { 1368 mx.setPoint(rmin, i, true); 1369 delay(DELAYTIME / MAX_DEVICES); 1370 } 1371 rmin++; 1372 1373 // do column 1374 for (uint8_t i = rmin; i <= rmax; i++) 1375 { 1376 mx.setPoint(i, cmax, true); 1377 delay(DELAYTIME / MAX_DEVICES); 1378 } 1379 cmax--; 1380 1381 // do row 1382 for (int i = cmax; i >= cmin; i--) 1383 { 1384 mx.setPoint(rmax, i, true); 1385 delay(DELAYTIME / MAX_DEVICES); 1386 } 1387 rmax--; 1388 1389 // do column 1390 for (uint8_t i = rmax; i >= rmin; i--) 1391 { 1392 mx.setPoint(i, cmin, true); 1393 delay(DELAYTIME / MAX_DEVICES); 1394 } 1395 cmin++; 1396 } 1397} 1398 1399void bounce() 1400// Animation of a bouncing ball 1401{ 1402 const int minC = 0; 1403 const int maxC = mx.getColumnCount() - 1; 1404 const int minR = 0; 1405 const int maxR = ROW_SIZE - 1; 1406 1407 int nCounter = 0; 1408 1409 int r = 0, c = 2; 1410 int8_t dR = 1, dC = 1; // delta row and column 1411 1412 1413 mx.clear(); 1414 1415 while (nCounter++ < 200) 1416 { 1417 mx.setPoint(r, c, false); 1418 r += dR; 1419 c += dC; 1420 mx.setPoint(r, c, true); 1421 delay(DELAYTIME / 2); 1422 1423 if ((r == minR) || (r == maxR)) 1424 dR = -dR; 1425 if ((c == minC) || (c == maxC)) 1426 dC = -dC; 1427 } 1428} 1429void stripe() 1430// Demonstrates animation of a diagonal stripe moving across the display 1431// with points plotted outside the display region ignored. 1432{ 1433 const uint16_t maxCol = MAX_DEVICES*ROW_SIZE; 1434 const uint8_t stripeWidth = 10; 1435 1436 mx.clear(); 1437 1438 for (uint16_t col=0; col<maxCol + ROW_SIZE + stripeWidth; col++) 1439 { 1440 for (uint8_t row=0; row < ROW_SIZE; row++) 1441 { 1442 mx.setPoint(row, col-row, true); 1443 mx.setPoint(row, col-row - stripeWidth, false); 1444 } 1445 delay(DELAYTIME); 1446 } 1447} 1448 1449void transformation1() 1450// Demonstrates the use of transform() to move bitmaps on the display 1451// In this case a user defined bitmap is created and animated. 1452{ 1453 uint8_t arrow[COL_SIZE] = 1454 { 1455 0b00001000, 1456 0b00011100, 1457 0b00111110, 1458 0b01111111, 1459 0b00011100, 1460 0b00011100, 1461 0b00111110, 1462 0b00000000 1463 }; 1464 1465 MD_MAX72XX::transformType_t t[] = 1466 { 1467 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1468 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1469 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1470 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1471 MD_MAX72XX::TFLR, 1472 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1473 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1474 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1475 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1476 MD_MAX72XX::TRC, 1477 MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, 1478 MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, 1479 MD_MAX72XX::TFUD, 1480 MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, 1481 MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, 1482 MD_MAX72XX::TINV, 1483 MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, 1484 MD_MAX72XX::TINV 1485 }; 1486 1487 // transformation 1488 1489 mx.clear(); 1490 1491 // use the arrow bitmap 1492 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); 1493 for (uint8_t j=0; j<mx.getDeviceCount(); j++) 1494 mx.setBuffer(((j+1)*COL_SIZE)-1, COL_SIZE, arrow); 1495 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); 1496 delay(DELAYTIME); 1497 1498 // run through the transformations 1499 mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::ON); 1500 for (uint8_t i=0; i<(sizeof(t)/sizeof(t[0])); i++) 1501 { 1502 mx.transform(t[i]); 1503 delay(DELAYTIME*4); 1504 } 1505 mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::OFF); 1506} 1507 1508void transformation2() 1509// Demonstrates the use of transform() to move bitmaps on the display 1510// In this case font characters are loaded into the display for animation. 1511{ 1512 MD_MAX72XX::transformType_t t[] = 1513 { 1514 MD_MAX72XX::TINV, 1515 MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, 1516 MD_MAX72XX::TINV, 1517 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1518 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1519 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1520 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1521 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1522 MD_MAX72XX::TSD, MD_MAX72XX::TSU, MD_MAX72XX::TSD, MD_MAX72XX::TSU, 1523 MD_MAX72XX::TFLR, MD_MAX72XX::TFLR, MD_MAX72XX::TFUD, MD_MAX72XX::TFUD 1524 };} 1525#endif //showoffcode// 1526
Matrix Clock code
arduino
Arduino Mega code to power clock
1 2 3 4/* 5 Clock project using two MX7219 matrix displays and the AdaFruit FX Soundboard 6 Provides date, time, temp, humidity and event notifications in rotating order 7 Also can chime in multiple ways, including Westminster chimes 8 Chimes are provided by Adafruit FX Soundboard in GPIO mode 9 There are some conditional options: 10 Davids_Clock changes some of the events and notifications 11 Extra_Display uses a small Adafruit 1306 OLED to continuously show date and time while the main display 12 rolls through its iterations 13 DDT includes a couple of small debugging (get it?) routines and opens serial terminal 14 15*/ 16 17 18 19 20// Code to set up the clock 21// Pushbutton 1 is the toggler , cycle between year, month, day, hours, minutes, seconds 22// Pushbutton 2 is the increment up one 23// Pushbutton 3 is the decrement down one 24 25// Chime setup for toggle switch 26// Center = No chimes 27// Up = Westminster hours + quarterly 28// Down = Simple Bell (hours and 1/2 hour) 29 30// Comment out next line to leave out the little show 31 32#define showoffcode // include "show off" graphics and music for Easter Egg 33 34//#define Davids_Clock // if this is david's clock slightly different announcements 35 36#define Extra_Display // Enables constant date and time in OLED display so never have to wait 37 38#define DDT // simple debug tools (print string, print string and value, same with NL) 39 40 41// Clock "ID's" for temperature adjustment 42// Define only one of these! 43 44//#define Clock_1 // First wine box clock (mine) 45//#define Clock_2 // 2nd wine box clock (For David) 46#define Clock_3 // Acrylic long matrix clock (Mercer Island) 47 48 49// include the libraries for LCD Display, DHT-11 Temperature/humidity sensor and DS3231 RTC and 50// include libraries for MX7219 mult-unit matrix display 51#include "DHT.h" 52#include <Wire.h> 53#include "RTClib.h" 54#include <MD_MAX72xx.h> 55 56// uncomment next two lines to use UART for SoundBoard rather than GPIO 57//#include <Adafruit_Soundboard.h> 58//#include <SoftwareSerial.h> 59 60#ifdef Extra_Display 61#include <Adafruit_GFX.h> // for OLED need graphics and 62#include <Adafruit_SSD1306.h> // the specific driver 63#endif 64 65 66// Set up output functions 67 68#define DHTPIN 9 //Temp Humidity sensor on pin 9 69#define DHTTYPE DHT11 // DHT 11 because the sensor is that type 70 71// Initialize DHT sensor for normal 16mhz Arduino 72DHT dht(DHTPIN, DHTTYPE); 73 74// Initialize for Adafruit DS3231 RTC real time clock 75RTC_DS3231 rtc; 76 77#ifdef Extra_Display // used to include a small OLED date/time display 78// Initialize the Small OLED display 79#define SCREEN_WIDTH 128 // OLED display width, in pixels 80#define SCREEN_HEIGHT 64 // OLED display height, in pixels 81 82// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) 83//#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) 84#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) 85Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); 86#endif Extra_Display 87 88 89// uncomment next set of lines to use UART for SoundBoard 90/* 91 // Choose any two pins that can be used with SoftwareSerial to RX & TX for UART SoundBoard 92 //#define SFX_TX 5 93 //#define SFX_RX 6 94 // Connect to the RST pin on the Sound Board 95 #define SFX_RST 4 96 // You can also monitor the ACT pin for when audio is playing! 97 // we'll be using software serial 98 //SoftwareSerial ss = SoftwareSerial(SFX_TX, SFX_RX); 99 // pass the software serial to Adafruit_soundboard, the second 100 // argument is the debug port (not used really) and the third 101 // arg is the reset pin 102 //Adafruit_Soundboard sfx = Adafruit_Soundboard(&ss, NULL, SFX_RST); 103 // can also try hardware serial with 104 // Adafruit_Soundboard sfx = Adafruit_Soundboard(&Serial1, NULL, SFX_RST); 105*/ 106 107// Define the number of devices we have in the chain and the hardware interface 108// NOTE: These pin numbers will probably not work with your hardware and may 109// need to be adapted 110#define HARDWARE_TYPE MD_MAX72XX::FC16_HW // NOTE: This parameter differs by vendor 111#define MAX_DEVICES 8 // 2 x 4 matrices per unit 112 113//Pins for Arduino Uno 114//#define CLK_PIN 13 // or SCK (this is for the UNO, different for the Mega 115//#define DATA_PIN 11 // or MOSI 116//#define CS_PIN 10 // or SS 117 118 119//Pins for Arduino Mega 120#define CLK_PIN 52 // or SCK 13 121#define DATA_PIN 51 // or MOSI 11 122#define CS_PIN 53 // or SS 10 123 124 125// SPI hardware interface 126MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); 127 128// We always wait a bit between updates of the display 129#define DELAYTIME 100 // in milliseconds 130 131// Text parameters 132#define CHAR_SPACING 1 // pixels between characters 133//const int CHAR_PIXELS = 6; // Pixels taken by each character (including trailing space) 134//const int MAX_CHARS = (MAX_DEVICES*COL_SIZE)/ CHAR_PIXELS; // Maximum characters at once (happens to be 10 btw) 135//const int LEFT_OVER = (MAX_DEVICES*COL_SIZE)- (CHAR_PIXELS*MAX_CHARS); //Since its not 'even' additional pad from front on printText 136const int BufferStringLength = 31; 137char BufferString [BufferStringLength]; //used to construct printouts 138#define DecToAscii 48 // convert number to ascii (add as a constant) 139char DegreeSign = '~'; // for display of 'degrees' after temperature 140int TDigits[4] = {0, 0, 0, 0}; // used to convert four digits to four chacacters 141#define TDOnes 3 // positions in arrays and templates 142#define TDTens 2 143#define TDHundreds 1 144#define TDThousands 0 145#define sunday 0 // used for events, floating events and holidays 146#define monday 1 // just for readability 147#define tuesday 2 148#define wednesday 3 149#define thursday 4 150#define friday 5 151#define saturday 6 152#define january 1 153#define february 2 154#define march 3 155#define april 4 156#define may 5 157#define june 6 158#define july 7 159#define august 8 160#define september 9 161#define october 10 162#define november 11 163#define december 12 164 165//Template Strings and pointers 166 167const int LOTS = 13; // Length of TimeString (without terminator) 168char TimeString[LOTS + 1 ] = {' ', ' ', '1', '2', ':', '3', '1', ':', '4', '5', ' ', 'P', 'M', '\\0'}; 169char DateString[ ] = {'1', '2', '/', '3', '1', '/', '2', '0', '1', '9', '\\0'}; 170char TempString[ ] = {'T', 'e', 'm', 'p', ' ', '7', '2', '.','0', DegreeSign,'F', '\\0'}; 171char HumidString[ ] = {'H', 'u', 'm', ' ', '2', '5', '.', '0', '%', '\\0'}; 172const int THptr = 2; // Pointers within the strings 173const int TMptr = 5; // for loading values 174const int TSptr = 8; 175const int TAMPMptr = 11; 176const int DMptr = 0; 177const int DDptr = 3; 178const int DYptr = 6; 179const int TEMPptr = 5; 180const int HUMIDptr = 4; 181 182// Differentiated Displays 183 184const int DisplayTime = 0; 185const int DisplayDate = 2; 186const int DisplayDOW = 1; 187const int DisplayTemp = 3; 188const int DisplayHumid = 4; 189const int DisplayEvent = 5; 190const int DisplayEventRepeat = 2; //times to repeat the scrolling 'event' display 191const int DisplaySize = 6; // Nunber of different displays 192int DisplayIndex = DisplayTime; // display we are on at the time 193const int DisplayDelayArray[DisplaySize] = {100, 50, 30, 30, 30, 20}; // multiplier for each event 194const int DisplayDelay = 100; // milliseconds to leave each display * its multiplier 195unsigned long DisplayTimer; // used to time each matrix display 196 197 198 199char daysOfTheWeek[7][12 ] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; 200 201 202 #ifndef Davids_Clock 203 204const int eventnumber = 50; // number of events and remember: No strings longer than 30 characters! 205 206int eventmonth [eventnumber] = {june, april, november, february, july, december, november, june, december, june, december, january, 207 september, october, march, february, february, july,december, february, december, january, january, february, 208 march,april,april,april,april,may,may,may,may,june,june,june,july,july,october, september, may, april, 209 march,february,march,october,march,november,august,october 210 }; 211int eventday [eventnumber] = {11, 1, 23, 12, 4, 3, 12, 29, 25, 16, 31, 1, 3, 31, 17, 14, 2, 14, 30,12,21,5,18,18,6,7,12,15,24,1,5,20,30,14,14,21, 212 22,24,29,28,18,26,14,3,27,12,30,5,5,4}; 213 214char* eventdescription [eventnumber] = 215{ "Dana's Anniversary", "April Fool's Day", "Joan's Birthday", "Sydney's Birthday", "July 4th!", "Dana's Birthday", 216 "David's Birthday", "Our Anniversary", "Merry Christmas", "Leah's Birthday", "New Year's Eve", "New Year's Day", 217 "Forrest's Birthday", "Happy Halloween", "Saint Patrick's Day", "Valentine's Day", "Groundhog Day", "Bastille Day", "Linsey's Birthday", 218 "Irwin's Birthday", "Winter Solstice", "National Bird Day", "National Winnie the Pooh Day", "National Drink Wine Day","National Oreo Day", 219 "National Beer Day","Grilled Cheese Day", "Tax Day","National DNA Day", "May Day", "Cinco de Mayo", "World Bee Day", "World MS Day", 220 "National Bourbon Day", "Flag Day", "Summer Solstice","National Hot Dog Day", "National Tequila Day","National Cat Day", "National Drink Beer Day", 221 "National Whisky Day", "Nat'l Audubon Day", "National Pi Day", "Nat'l Women Physicians Day", "National Paella Day", "National Gumbo Day", 222 "National Doctors Day", "National Doughnut Day", "National Oyster Day", "National Taco Day" 223}; 224#endif 225#ifdef Davids_Clock 226const int eventnumber = 43; // number of events and remember: No strings longer than 30 characters! 227 228int eventmonth [eventnumber] = {june, april, november, february, july, december, november, june, december, june, december, january, 229 september, october, march, february, february, july,december, february, december, 230 march,april,april,april,may,may,may,june,june,june,july,july,september, 231 march,february,march,october,march,november,october,august 232 }; 233int eventday [eventnumber] = {11, 1, 23, 12, 4, 3, 12, 29, 25, 16, 31, 1, 3, 31, 17, 14, 2, 14, 30,12,21,18,6,7,12,15,1,5,20,14,14,21, 234 22,24,28,14,3,27,12,30,5,4,4}; 235 236char* eventdescription [eventnumber] = 237{ "Dana's Anniversary", "April Fool's Day", "Mom's Birthday", "Sydney's Birthday", "July 4th!", "Dana's Birthday", 238 "My Birthday", "Mom & Dad's Anniversary", "Merry Christmas", "Leah's Birthday", "New Year's Eve", "New Year's Day", 239 "Forrest's Birthday", "Happy Halloween", "Saint Patrick's Day", "Valentine's Day", "Groundhog Day", "Bastille Day", "Linsey's Birthday", 240 "Dad's Birthday", "Winter Solstice", "National Drink Wine Day","National Oreo Day", 241 "National Beer Day","Grilled Cheese Day", "Tax Day", "May Day", "Cinco de Mayo", "World Bee Day", 242 "National Bourbon Day", "Flag Day", "Summer Solstice","National Hot Dog Day", "National Tequila Day","National Drink Beer Day", 243 "National Pi Day", "Nat'l Women Physicians Day", "National Paella Day", "National Gumbo Day", "National Doctors Day", "National Doughnut Day", 244 "National Taco Day", "Nat'l Champagne Day" 245}; 246#endif 247 248 249bool event; // logic flag "on" = event found 250int eventindex; // and the event we found 251const int maxevents = 3; // maximum of three events (real and floating combined) 252char* eventstrings[maxevents] ; // used to store displayed events 253String floatstring = " "; // used for floating events (e.g., Thanksgiving) 254 255#define pb1pin 2 // Pin assignments for reset, increment and decrement 256#define pb2pin 3 257#define pb3pin 4 258 259#define showoffbuttonpin 7 // invoke showing off the matrix display 260 261#define DoWestminster 5 262#define DoHoursOnly 6 263#define silent 0 // no chiming at all 264#define hoursonly 1 // no prelude, just chime number of hours and half hour single chime 265#define westminster 2 // hours + Westminster prelude per quarter 266int ChimeValue = silent; // default to silent 267 268 269const int LEDpin = A0; // Pin assignment for analog reading LDR for LED brightness 270const int minbright = 0; // MAX_BRIGHT for this module is 15 271const int midbright = 2; // but these values "seem" to work 272const int maxbright = 4; 273 274 275 276 277int setupindex = 0; // first thing to set if required 278bool insetupmode = false; // and assume RTC is set, OK, etc., and no 'set' required 279bool setinsetup = false; // flag set to true at interrupt level to go back into setup mode 280 281// date time array for setting, reading, displaying 282 283#define setsize 6 // size of the setting array 284#define setyear 0 // index name for each element 285#define setmonth 1 286#define setday 2 287#define sethour 3 288#define setminute 4 289#define setsecond 5 290 291int setarray [setsize] = {2019, 1, 1, 1, 1, 0}; // set year, month, day, hour, minutes, seconds 292int lowlimit [setsize] = {2019, 1, 1, 0, 1, 0}; // lower limit for each 293int highlimit [setsize] = {2080, 12, 31, 23, 59, 59}; //high limit for each 294const int setdesclength = 4; // maximum length of 'set' descriptor 295char setdesc [setsize] [setdesclength] = {"Yr ", "Mon", "Day", "Hr ", "Min", "Sec"}; 296 297 298#define cqtr0 1 // Chime the hour 299#define cqtr1 2 // Chime the quarter hour 300#define cqtr2 3 // Chime the half hour 301#define cqtr3 4 // Chime the 3/4 hour 302 303// Definitions for the Sound Board 304#define SoundTruncatedSingle 0 // Truncated Single Westminster 305#define SoundQ1 1 // First Quarter 306#define SoundQ2 2 // Second Quarter 307#define SoundQ3 3 // Third 308#define SoundQ4 4 // Fourth (before hour chime) 309#define SoundTrailingSingle 5 // Finish Westminster string of chimes with longer sound tail 310#define SoundTruncatedBell 6 // Simple Bell 311#define SoundTrailingBell 7 // Final Bell (like single, has longer 'tail') 312#define SoundStartup 8 // startup sound 313#define SoundShowOff 9 // Play during show off of matrix 314#define FirstSoundPin 30 // first pin to use for soundboard (0-10 available on soundboard); 315#define SizeSoundPin 10 // number of pins used (must be consecutive) 316#define fxset 150 // Time soundboard must be held LOW to register (documentation says 125 ms) 317#define SoundBusyPin 8 // Soundboard pin that goes LOW when board is active 318 319const int SizeQueue = 15; // Assume Q4+12 bells is the maximum that'll be in the queue at any point 320int SoundQueue[SizeQueue]; // Circular Queue for waiting sounds 321int SoundQueuePtr = -1; //pointer for Queue fill Position 322int SoundPlayPtr = -1; //pointer for Queue play Position 323int SoundQueueCtr = 0; // number of items in the queue 324 325int setstrike = -1; // Chime/strike flag 326byte alreadychimed = false ; // Used to keep from chiming multiple times during the "hot" second 327const int BounceDelay = 250; // Not really 'bounce', its a change of state detection 328 329int i; // generic index variable 330 331int dayoftheweek; // stored day of the week (0-6, 0 = Sunday) 332 333int phours; // for print conversion of military time 334 335float temperaturef; // farenheit temperature back 336int temperature; // integer version of temperature for matrix 337float temperaturec; // centrigade temperature back (not used) 338 339float humidityf; // and humidity 340int humidity ; // and same as temp 341 342// The DHT sensors are cheap and inaccurate so these are 'custom' adjustments 343 344#ifdef Clock_1 // first wine box clock 345float tempadjust = -2.0; // temperature adjustment for sensor (I found it didn't read right against 'comps') 346float humidadjust = +8.0; // corresponding humidity adjustment 347#endif 348 349#ifdef Clock_2 // second wine box clock for David 350float tempadjust = -0.6; // temperature adjustment for sensor 351float humidadjust = +8.0; 352#endif 353 354#ifdef Clock_3 // clear clock 355float tempadjust = 0.0; 356float humidadjust = +8.0; 357#endif 358 359 360#ifdef DDT // debugging tools 361void DDTl(String st, int vt) { // Print descriptor and value and new line 362 DDTs(st, vt); 363 Serial.println(" "); 364} 365void DDTs(String st, int vt) { // Print descriptor and value 366 Serial.print(" "); 367 Serial.print(st); 368 Serial.print(" "); 369 Serial.print(vt); 370} 371void DDTt(String st) { // Print descriptor and value and new line 372 Serial.println(st); 373} 374#endif //DDT// 375 376 377void setup() 378// put your setup code here, to run once: 379 380{ 381 Wire.begin(); // initialize I2C interface 382 dht.begin(); // initialize the temp/humidity sensor 383 mx.begin(); // and MX7219 display 384 #ifdef DDT 385 Serial.begin (9600); //Terminal monitor printing for debugging 386 #endif 387 388 // softwareserial at 9600 baud (uncomment this line and next for UART control of sound board 389 // ss.begin(9600); 390 391#ifdef OLED_Display 392// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally 393 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64 394 scrollText("Small OLED display startup failed"); 395 delay(2000);} else //and wait 396 { display.clearDisplay(); // give a little startup msg 397 display.setTextSize(2); // Normal 1:1 pixel scale 398 display.setTextColor(SSD1306_WHITE); // Draw white text 399 display.setCursor(0,0); // Start at top-left corner 400 display.println("Hello"); 401 display.setTextSize(2); 402 display.println("Starting"); 403 display.setTextSize(3); // Draw 2X-scale text 404 display.setTextColor(SSD1306_WHITE); 405 display.print(" UP! "); 406 display.display(); 407 delay(1500); 408 } 409#endif OLED_Display 410 411 pinMode(pb1pin, INPUT_PULLUP); // The three pushbuttons - reset 412 pinMode(pb2pin, INPUT_PULLUP); // increment 413 pinMode(pb3pin, INPUT_PULLUP); // decrement 414 415 pinMode (DoWestminster, INPUT_PULLUP); // When pulled 'low' we want Westminster chimes 416 pinMode (DoHoursOnly , INPUT_PULLUP); // When pulled 'low' we want hours and 1/2 bell (neither is 'silent') 417 418 #ifdef showoffcode 419 pinMode(showoffbuttonpin, INPUT_PULLUP) ; // if show off matrix is there, initialize pin 420 #endif //showoffcode// 421 422 for (i = FirstSoundPin; i < FirstSoundPin + SizeSoundPin; i++) { // pins for sound board 423 pinMode(i, OUTPUT); // each an output 424 digitalWrite(i, HIGH); // and initialize high (off) 425 } 426 427 // set up interupt for PB1 // if PB1 is pushed, it'll pick up on next release from Matrix display 428 attachInterrupt(digitalPinToInterrupt(pb1pin), SetToSetup, FALLING); // and re-enter setup mode (after the end of current display) 429 430 QueueSound(SoundStartup); // play boot up sound 431 PlaySound(); 432 433 if (! rtc.begin()) { // check that clock is there 434 scrollText("Couldn't find RTC"); // clock missing is a fatal error 435 while (1); 436 } 437 438 if (rtc.lostPower()) { // if power lost force a setup, else load 'current' values and 439 scrollText("RTC lost power!"); 440 delay(1000); 441 scrollText("Replace battery?"); 442 delay(1000); 443 scrollText("Setup Mode: "); 444 setupindex = setyear - 1; // setup differently (becuase it will increment) 445 insetupmode = true; 446 for (i = 0; i < setsize; i++) { 447 setarray[i] = lowlimit[i]; 448 } 449 } else // else reload from RTC 450 { DateTime (setarray[setyear], setarray[setmonth], setarray[setday], setarray[sethour], setarray[setminute], setarray[setsecond]) = rtc.now(); 451 insetupmode = false; // not in setup mode, and 452 setinsetup = false; //no interrupt 453 } 454 455 // Two alternative modes of setting date and time (for debugging, just un-comment): 456 // This line sets the RTC to the date & time this sketch was compiled 457 // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); 458 // This line sets the RTC with an explicit date & time, for example to set 459 // January 21, 2014 at 3am you would call: 460 // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); 461 LEDBrightness(); // Do an initial check for room brightness 462} // end of setup 463 464void loop() { // Main code divided into setup mode and non-setup mode 465 466 while (insetupmode) { // code for setting time, date set, etc. 467 468 // Read the increment button 469 470 if (digitalRead(pb2pin) == LOW) { 471 setarray[setupindex]++; // read increment of current item 472 delay(BounceDelay); 473 if (setarray[setupindex] > highlimit[setupindex]) { 474 setarray[setupindex] = lowlimit[setupindex]; 475 } 476 ShowValue(setupindex, setdesc[setupindex]); // and display description and value 477 } 478 479 // Read the decrement Button 480 if (digitalRead(pb3pin) == LOW) { 481 setarray[setupindex]--; // read decrement of value 482 delay(BounceDelay); 483 if (setarray[setupindex] < lowlimit[setupindex]) { 484 setarray[setupindex] = highlimit[setupindex]; 485 } 486 ShowValue(setupindex, setdesc[setupindex]); // and display description and value 487 } 488 489 // Read for another increment of index 490 491 if (digitalRead(pb1pin) == LOW) { // Rolling through Chime, Year, Month, Day, Hour, Minutes, Seconds 492 setupindex++ ; // increment index 493 delay(BounceDelay); 494 if (setupindex < setsize) { 495 ShowValue(setupindex, setdesc[setupindex]); // show description and value if in bounds 496 } 497 if (setupindex >= setsize) { // and finally exiting setup mode after setting chime, date and time 498 rtc.adjust(DateTime(setarray[setyear], setarray[setmonth], setarray[setday], setarray[sethour], setarray[setminute], setarray[setsecond])); 499 insetupmode = false; 500 DisplayIndex = DisplayTime; 501 } //exit setup mode when done 502 } 503 504 } // End of "While" for setup 505 506 // Begin regular loop for date, time, temp humidity and event display 507 508 while (!insetupmode) { 509 510 GetTempandHumid(); // read temperature and humidity from sensor 511 512 ChimeValue = ReadChimeSetting(); // read SPDT Center off switch for silent, westminster or hours only (and 1/2) 513 514 GetTheDate(); // load date array from RTC for a chime/bell check 515 516 CheckForChime(); // Check for a chime event between displays 517 518 CheckForEvent(setarray[setmonth], setarray[setday], dayoftheweek); // check for static and floating events 519 520 MatrixDisplay(); // Do whatever display is required on the MX7219 521 522 #ifdef OLED_Display // if the little OLED display is used 523 OLED_Update(); // update it 524 #endif Extra display 525 526 LEDBrightness(); // Good place to check for change in room brightness 527 528 // end of display update logic 529 530 // Read a potential request for an entry into setup from PB 1 531 if ((digitalRead(pb1pin) == LOW) || setinsetup) { // to see if we go back to setup mode 532 insetupmode = true; // via a pushbutton or the interrupt 533 setinsetup = false; // clear the interrupt flag 534 setupindex = 0; // re-initialize to 'Year' in setup 535 DisplayIndex = DisplayTime; // and go back to time display when exit setup 536 scrollText("Setup Mode: "); 537 delay(2000); 538 ShowValue(setupindex, setdesc[setupindex]); // show the first setup item (Year) 539 delay(BounceDelay); 540 } 541 #ifdef showoffcode 542 if (digitalRead(showoffbuttonpin) == LOW) {ShowOff();} // check for want to show off matrix 543 #endif //showoffcode// 544 545 } // end of not in setup 546} // end of sketch 547 548void MatrixDisplay() { // Main display routine for display sequences 549 550 Scrollup(); // Clear last display 551 if (DisplayIndex >= DisplaySize) { 552 DisplayIndex = DisplayTime; // reset if at the end 553 } 554 DisplayTimer = DisplayDelayArray[DisplayIndex] * DisplayDelay; // set individual display time 555 556 switch (DisplayIndex) { // and do next display 557 558 case DisplayTime: // Dislay the time 559 do // time is different in that there's a constant 560 { // update of time during display and also play chime, bell etc. 561 GetTheDate(); // Get Current Time 562 #ifdef OLED_Display // If the OLED is there, 563 OLED_Update(); 564 #endif OLED_Display 565 CheckForChime(); // Check for a chime event 566 LoadNumber(phours); // Load the 4 digit character array from number 567 TimeString[THptr] = TDigits[TDTens]; // left digit of the two digit hour 568 TimeString[THptr + 1] = TDigits[TDOnes]; // rightmost digit 569 if (TimeString[THptr] == '0') { 570 TimeString[THptr] = ' '; // eliminate leading zero 571 } 572 LoadNumber(setarray[setminute]); // do same for minutes 573 TimeString[TMptr] = TDigits[TDTens]; // except no need to do space for zero 574 TimeString[TMptr + 1] = TDigits[TDOnes]; 575 LoadNumber(setarray[setsecond]); // seconds then 576 TimeString[TSptr] = TDigits[TDTens]; 577 TimeString[TSptr + 1] = TDigits[TDOnes]; 578 if (setarray[sethour] < 12) // and AM vs PM 579 { 580 TimeString[TAMPMptr] = 'A'; 581 } 582 else 583 { 584 TimeString[TAMPMptr] = 'P'; 585 } 586 printText(TimeString, LOTS, false); // Keep position (no centering) because time keeps updating during display and chimes 587 PlaySound(); // Play any chimes or bells 588 if (SoundQueueCtr == 0) { 589 DisplayTimer = DisplayTimer - DisplayDelay; // if no sound, use display delay logic 590 delay(DisplayDelay); 591 } 592 } while ((DisplayTimer > 0) || (SoundQueueCtr > 0)); // leave this display when no sound or no delay left 593 DisplayIndex++ ; // then move on to next display item 594 break; 595 596 case DisplayDate: // Month, Day and Year Display 597 LoadNumber(setarray[setmonth]); 598 DateString[DMptr] = TDigits[TDTens]; 599 if (DateString[DMptr] == '0') { 600 DateString[DMptr] = ' '; 601 } 602 DateString[DMptr + 1] = TDigits[TDOnes]; 603 LoadNumber(setarray[setday]); 604 DateString[DDptr] = TDigits[TDTens]; 605 DateString[DDptr + 1] = TDigits[TDOnes]; 606 LoadNumber(setarray[setyear]); 607 for (int i = 0; i < 4; i++) { 608 DateString[DYptr + i] = TDigits[i]; 609 } 610 printCenter(DateString); 611 CommonDelay(DisplayTimer); 612 DisplayIndex++ ; 613 break; 614 615 case DisplayDOW: // Just display the day of week string 616 printCenter(daysOfTheWeek[dayoftheweek]); 617 CommonDelay(DisplayTimer); 618 DisplayIndex++ ; 619 break; 620 621 case DisplayTemp: // Temperature display 622 LoadNumber(temperature); 623 TempString[TEMPptr] = TDigits[TDHundreds]; 624 TempString[TEMPptr + 1] = TDigits[TDTens]; 625 TempString[TEMPptr + 3] = TDigits[TDOnes]; 626 printCenter(TempString); 627 CommonDelay(DisplayTimer); 628 DisplayIndex++ ; 629 break; 630 631 case DisplayHumid: // Humidity display 632 LoadNumber(humidity); 633 HumidString[HUMIDptr] = TDigits[TDHundreds]; 634 HumidString[HUMIDptr + 1] = TDigits[TDTens]; 635 HumidString[HUMIDptr + 3] = TDigits[TDOnes]; 636 printCenter(HumidString); 637 CommonDelay(DisplayTimer); 638 DisplayIndex++ ; 639 break; 640 641 case DisplayEvent: // Event Display 642 if (event) { // "Real" events repeat 3 times 643 if (eventindex > 0 ) { // for case where there is more than one event 644 while (eventindex >= 0) 645 {scrollText(eventstrings[eventindex]); // scroll each of them once 646 if (eventindex > 0) { 647 delay(DisplayDelay * 6); 648 printCenter("and"); 649 delay(DisplayDelay * 6);} 650 eventindex--; // and decrement 651 delay(DisplayDelay * 2); 652 } 653 } 654 else { // case of only one event -- repeat it for readability 655 for (int i = 1; i <= DisplayEventRepeat; i++) { // scroll either static or floating event 656 scrollText(eventstrings[eventindex]); // the index is 0 (greater than zero if multiple events) 657 } 658 delay(DisplayDelay * 2); 659 } 660 } // end of "if event" logic 661 if (!event){ // Scroll Default message only once 662 #ifndef Davids_Clock 663 if (setarray[sethour] >= 6 && setarray[sethour] <= 11) { 664 scrollText("Good Morning ");} 665 else if (setarray[sethour] >= 12 && setarray[sethour] <= 16) { 666 scrollText("Good Afternoon");} 667 else if (setarray[sethour] >= 17 && setarray[sethour] <= 21) { 668 scrollText("Good Evening");} else{scrollText("Good Night"); } 669 #endif 670 #ifdef Davids_Clock 671 if (setarray[sethour] >= 6 && setarray[sethour] <= 11) { 672 scrollText("Good Morning, David");} 673 else if (setarray[sethour] >= 12 && setarray[sethour] <= 16) { 674 scrollText("Good Afternoon, David");} 675 else if (setarray[sethour] >= 17 && setarray[sethour] <= 21) { 676 scrollText("Good Evening, David");} else{scrollText("Good Night, David"); } 677 #endif 678 } // end of "if not event" above 679 CommonDelay(DisplayTimer); 680 DisplayIndex++ ; 681 break; 682 683 default: 684 scrollText("Should never get here"); 685 while (1); 686 687 } // End of Switch 688 689} // End of MatrixDisplay 690 691void GetTheDate() { // Read RTC into array 692 DateTime now = rtc.now(); 693 setarray[setyear] = now.year(); 694 setarray[setmonth] = now.month(); 695 setarray[setday] = now.day(); 696 setarray[sethour] = now.hour(); 697 setarray[setminute] = now.minute(); 698 setarray[setsecond] = now.second(); 699 dayoftheweek = now.dayOfTheWeek(); 700 if (setarray[sethour] <= 12) // convert military time to am pm 701 { 702 phours = setarray[sethour]; 703 } 704 else 705 {phours = setarray[sethour] - 12;} 706 if (phours <= 0) { phours = 12; } // don't print 0 for midnite, print "12" 707 708} // End of Get The Date 709 710void GetTempandHumid(){ // Temperature and humidity update 711 712 // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) 713 humidityf = dht.readHumidity()+humidadjust; // read humidity and adjust for any error 714 715 // Read temperature as Fahrenheit 716 temperaturef = (dht.readTemperature(true)) + tempadjust; // read and correct for inaccuracy in sensor 717 718 // Check if any reads failed and exit early (to try again). 719 if (isnan(humidityf) || isnan(temperaturec) || isnan(temperaturef)) { 720 temperaturef = 0; // use 0 as a no-read 721 humidityf = 0; // error indication 722 } 723 temperature = temperaturef * 10.0; // get read for matrix printout 724 humidity = humidityf * 10.0; 725} // end of Temperature and Humidity update 726 727void SetToSetup() { // interrupt level back to setup 728 if ((!insetupmode) && (!setinsetup)) { // skip if we are already in Setup mode or have seen the interrupt 729 setinsetup = true; // Go back into setup at next loop 730 DisplayTimer = 0; 731 } // and zero the timer to speed that up if you can 732} // SetToSetup 733 734void CommonDelay(long int MyDelay) { // Common delay routine 735 DisplayTimer = MyDelay; 736 do { 737 DisplayTimer = DisplayTimer - DisplayDelay; 738 delay(DisplayDelay); 739 GetTheDate(); // load date array from RTC for a chime/bell check 740 CheckForChime(); // if a chime happens, setup to end delay 741 } while ((DisplayTimer > 0) && (!setinsetup) && (SoundQueueCtr <= 0)); // repeat while still time and no interrupt and no sound waiting 742 if (SoundQueueCtr > 0){DisplayIndex = DisplayTime-1;} // if leaving due to chime, go directly to time 743} // End of CommonDelay 744 745 746int ReadChimeSetting() { 747 int returnvalue = silent; // Assume 'silent' (Center position) 748 if (digitalRead(DoWestminster) == LOW) { 749 returnvalue = westminster; 750 } else if (digitalRead(DoHoursOnly) == LOW) { 751 returnvalue = hoursonly; 752 } 753 return returnvalue; 754} 755 756// Routine to deal with combinations of chime type, time of day and the current hour 757// called parameters are chime type (e.g., Westminster), Strike type (hour, qtr, half, 3/4) and the hour in military time 758// Note for delays and print cycles: You need to call this at least once during the 'golden' minute (0,15,30,45) 759 760void CheckForChime() { // Logic for chiming 761 762 setstrike = -1; // initialize 'strike' flag to "no strike" 763 764 if (ChimeValue != silent) // if silenced due to setting silent, skip the whole thing 765 { 766 if (setarray[setminute] == 0) { // Look for 'on the hour' 767 setstrike = cqtr0; 768 } else if (setarray[setminute] == 15 ) { // Look for on the quarter hour 769 setstrike = cqtr1; 770 } else if (setarray[setminute] == 30 ) { // Look for on the 1/2 hour 771 setstrike = cqtr2; 772 } 773 else if (setarray[setminute] == 45 ) { // Look for on the three-quarter hour 774 setstrike = cqtr3; 775 } 776 else { 777 alreadychimed = false; // none of the above, reset ability to chime 778 } 779 780 if (setstrike > 0 && !alreadychimed ) { 781 chime(ChimeValue, setstrike, setarray[sethour]); // call chiming with 'type of chime'; 0,15,30,45 ; and # hours 782 DisplayIndex = DisplayTime; // force display of time while chiming 783 alreadychimed = true; // we will be here multiple times but only chime once 784 } 785 } // end of logic for chiming 786} // end of CheckForChime 787 788void chime (int chimetype, int strikeflag, int chour) { 789 790 int chour1; // am pm variable\\ 791 792 if (chour <= 12) { 793 chour1 = chour; // convert military time to am pm 794 } else { 795 chour1 = chour - 12; 796 } 797 798 799 if (chour1 <= 0) { 800 chour1 = 12; // don't chime 0 for midnite, chime 12 801 } 802 if (chimetype == hoursonly && strikeflag == cqtr2) { 803 QueueSound(SoundTrailingBell); // 1/2 hour only, do single 'ding' 804 } 805 else if ( chimetype == hoursonly && strikeflag == cqtr0) { 806 for (int i = 1; i < chour1; i++) { 807 QueueSound(SoundTruncatedBell); // Ding once less than hours 808 } 809 QueueSound(SoundTrailingBell); 810 } // and follow by trailing bell as last 'ding' (if used) 811 else if (((chimetype == westminster) ) && strikeflag == cqtr1) { 812 QueueSound(SoundQ1); // First Quarter 813 } 814 else if (((chimetype == westminster) ) && strikeflag == cqtr2) { 815 QueueSound(SoundQ2); // Second Quarter 816 } 817 else if (((chimetype == westminster)) && strikeflag == cqtr3) { 818 QueueSound(SoundQ3); // Third Quarter 819 } 820 else if (((chimetype == westminster)) && strikeflag == cqtr0) { 821 QueueSound(SoundQ4); 822 for (int i = 1; i < chour1; i++) { 823 QueueSound(SoundTruncatedSingle); // Chime once less than hours 824 } 825 QueueSound(SoundTrailingSingle); 826 } // Chime Westminster final hours 827 828 829} // end of Chime 830 831 832// Routine to check for 'real' (static) events and floating events (e.g., Memorial Day, Thanksgiving, Mother's Day and Fathers Day) 833 834/* Memorial Day is Last Monday in May 835 Thanksgiving is 4th Thursday in November 836 Mother's Day is 2nd Sunday in May 837 Father's Day is 3rd Sunday in June 838 MLK Day is 3rd Monday in February 839 Memorial Day is last Monday in May 840 Labor Day is first Monday in September 841 Daylight Savings Times starts 2nd Sunday in March 842 Daylight Savings Times ends first Sunday in November 843 Indigenous People's Day is second Monday in October (also its Columbus Day) 844 Oktoberfest is 3rd Saturday in September 845 Election Day is first Tuesday in November 846 National Ice Cream Day is 3rd Sunday in July 847 848*/ 849 850void CheckForEvent(int m, int d, int dow) { // called with Month, Day within month and day of the week (0 = Sunday) 851 852 event = false; // assume neither static or floating 853 eventindex = -1; // initial index to eventstrings 854 855 // Static event check 856 857 for (int i = 0; i < eventnumber; i++) { // then check the static events 858 if ((setarray[setmonth] == eventmonth[i]) && (setarray[setday] == eventday[i])) 859 { event = true; // set if match on month and day 860 eventindex++; // found one! 861 eventstrings[eventindex] = eventdescription[i]; // store pointer to static event description 862 } 863 } 864 865 // Floating event check 866 867 floatstring = "none"; // initialize string 868 869 if ((dow == thursday) && (m == november) && (d >= 22) && (d <= 28)) { // Thanksgiving 870 floatstring = "Thanksgiving"; 871 } 872 else if ((dow == sunday) && (m == may) && (d >= 8) && (d <= 14)) { // Mother's Day 873 floatstring = "Mother's Day"; 874 } 875 else if ((dow == sunday) && (m == june) && (d >= 15) && (d <= 21)) { //Father's Day 876 floatstring = "Father's Day"; 877 } 878 else if ((dow == monday) && (m == january) && (d >= 15) && (d <= 21)) { //MLK Day 879 floatstring = "Martin Luther King Day"; 880 } 881 else if ((dow == saturday) && (m == october) && (d >= 15) && (d <= 21)) { //Oktoberfest starts 882 floatstring = "Oktoberfest Begins"; 883 } 884 else if ((dow == sunday) && (m == july) && (d >= 15) && (d <= 21)) { //Oktoberfest starts 885 floatstring = "National Ice Cream Day"; 886 } 887 else if ((dow == monday) && (m == february) && (d >= 15) && (d <= 21)) { //President's Day 888 floatstring = "President's Day!"; 889 } 890 else if ((dow == monday) && (m == september) && (d <= 7) ) { //Labor Day 891 floatstring = "Labor Day"; 892 } 893 else if ((dow == tuesday) && (m == november) && (d <= 7) ) { //Election Day 894 floatstring = "Election Day"; 895 } 896 else if ((dow == monday) && (m == october) && (d >= 8) && (d <= 14) ) { //Indigenous People's Day 897 floatstring = "Indigenous People's Day"; 898 } 899 else if ((dow == monday) && (m == may) && (d + 7 > 31) ) { //Memorial Day 900 floatstring = "Memorial Day"; 901 } 902 else if ((dow == sunday) && (m == march) && (d >= 8) && (d <= 14)) { // DST begins 903 floatstring = "DST Begins"; 904 } 905 else if ((dow == sunday) && (m == november) && (d <= 7) ) { //DST ends 906 floatstring = "DST Ends"; 907 } 908 909 if (floatstring != "none" ) { // did we load anything? 910 event = true; // if so, then we have one 911 eventindex++; // so load it into event display queue 912 eventstrings[eventindex] = floatstring.c_str(); // store pointer to static event description 913 } 914 915} // end of floatingevent 916 917void LEDBrightness() { 918 int LEDvalue; 919 LEDvalue = analogRead(LEDpin); // Read LDR value (may need to play with values) 920 DDTl("LEDvalue",LEDvalue); 921 //DDTs("LED value",LEDvalue); 922 // LEDvalue = map(LEDvalue, 0, 1023, 0, MAX_INTENSITY); //map to valid value for brightness (max intensity is 15 btw) 923 // if (LEDvalue <= 12) { 924 // i = minbright;} 925 // else if (LEDvalue > 13 ) { 926 // i=maxbright; } 927 // else {i = midbright;} 928 if (LEDvalue >=950) {i=maxbright;} 929 else if (LEDvalue >=700) {i=midbright;} 930 else {i=minbright;} 931 932 mx.control(MD_MAX72XX::INTENSITY, i); 933 // DDTl("Mapped LED value", i); 934 935 936 937} //end of LEDbrightness 938 939void Scrollup() { // used to 'wipe' previous display 940 for (uint8_t i = 0; i < 8; i++) { 941 mx.transform(MD_MAX72XX::TSU); delay(2 * DELAYTIME); 942 delay(DELAYTIME); 943 } 944} // End of Scrollup 945 946#ifdef OLED_Display 947 948void OLED_Update(){ 949 display.clearDisplay(); 950 display.setTextSize(2); // Normal 1:1 pixel scale 951 display.setTextColor(SSD1306_WHITE); // Draw white text 952 display.setCursor(0,0); // Start at top-left corner 953 display.println(" Date/Time"); // header line 954 display.print(setarray[setmonth]); // use mm/dd/yyyy 955 display.print("/"); 956 display.print(setarray[setday]); 957 display.print("/"); 958 display.println(setarray[setyear]); 959 display.setTextSize(3); // Draw 3X-scale text so time is somewhat readable 960 if (setarray[sethour] <=12) // convert military time to am pm 961 {phours = setarray[sethour];} 962 else 963 {phours = setarray[sethour]-12;} 964 if (phours <= 0){phours = 12;} // don't print 0 for midnite, print "12" 965 printtwo(phours," "); // print a standard hh:mm am/pm time on the oled 966 display.print(":"); 967 printtwo(setarray[setminute],"0"); 968 if (setarray[sethour] < 12) 969 {display.print("am");} 970 else 971 {display.print("pm");} 972 display.display(); // update display 973 974} // end of OLED display 975 976void printtwo(int value, String fillchar){ // print two always and use fill to make it so 977 if (value <10) {display.print(fillchar);} // blank space for hours, 0 for minutes, seconds 978 display.print(value); 979 } 980#endif 981 982void scrollText(char *p) // copied from library 983{ 984 uint8_t charWidth; 985 uint8_t cBuf[8]; // this should be ok for all built-in fonts 986 mx.clear(); 987 while (*p != '\\0') 988 { 989 charWidth = mx.getChar(*p++, sizeof(cBuf) / sizeof(cBuf[0]), cBuf); 990 991 for (uint8_t i = 0; i <= charWidth; i++) // allow space between characters 992 {mx.transform(MD_MAX72XX::TSL); 993 if (i < charWidth) 994 mx.setColumn(0, cBuf[i]); 995 delay(DELAYTIME);} 996 } 997} // End of Scroll Text 998 999void printText(char *pMsg, int LOS, bool CenterJustify) // copied and modified from library 1000// Print the text string to the LED matrix modules specified. 1001// Message area is padded with blank columns after printing. 1002// And center justified if third argument is "true" 1003{ 1004 uint8_t modStart = 0; 1005 uint8_t modEnd = MAX_DEVICES - 1; 1006 uint8_t state = 0; 1007 uint8_t curLen; 1008 uint16_t showLen; 1009 uint8_t cBuf[8]; 1010 int16_t col = ((modEnd + 1) * COL_SIZE) - 1; 1011 int pixelcount = 0; 1012 int ccounter = LOS; 1013 1014 mx.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); 1015 1016 do // finite state machine to print the characters in the space available 1017 { switch (state) 1018 { 1019 case 0: // Load the next character from the font table 1020 // if we reached end of message, reset the message pointer 1021 if (*pMsg == '\\0') 1022 { 1023 showLen = col - (modEnd * COL_SIZE); // padding characters 1024 state = 2; 1025 break; 1026 } 1027 1028 // retrieve the next character form the font file 1029 1030 showLen = mx.getChar(*pMsg++, sizeof(cBuf) / sizeof(cBuf[0]), cBuf); 1031 if (ccounter > 0) { 1032 pixelcount = (pixelcount + showLen) + CHAR_SPACING; 1033 ccounter--; 1034 } 1035 curLen = 0; 1036 state++; 1037 // !! deliberately fall through to next state to start displaying 1038 1039 case 1: // display the next part of the character 1040 mx.setColumn(col--, cBuf[curLen++]); 1041 1042 // done with font character, now display the space between chars 1043 if (curLen == showLen) 1044 { 1045 showLen = CHAR_SPACING; 1046 state = 2; 1047 } 1048 break; 1049 1050 case 2: // initialize state for displaying empty columns 1051 1052 curLen = 0; 1053 1054 state++; 1055 // fall through 1056 1057 case 3: // display inter-character spacing or end of message padding (blank columns) 1058 mx.setColumn(col--, 0); 1059 curLen++; 1060 if (curLen == showLen) 1061 state = 0; 1062 break; 1063 1064 default: 1065 col = -1; // this definitely ends the do loop 1066 } 1067 } while (col >= (modStart * COL_SIZE)); 1068 1069 if (CenterJustify) { 1070 for (int i = 1; i <= (((MAX_DEVICES * COL_SIZE) - pixelcount) / 2); i++) { 1071 mx.transform( MD_MAX72XX::TSR); 1072 } 1073 } 1074 mx.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::ON); 1075} 1076void printCenter(String StringtoPrint) // Loads the print buffer and centers it 1077{ 1078 int LOS = StringtoPrint.length(); // Get the string's length 1079 for (int i = 0; i < BufferStringLength; i++) { 1080 BufferString[i] = ' '; // clear buffer 1081 } 1082 for (int i = 0; i < LOS; i++) { 1083 BufferString[i] = StringtoPrint[i]; // transfer 1084 printText(BufferString, LOS, true); // Print, providing length of string and "Yes, center" 1085 1086 } 1087} // End of Center and Load 1088 1089void ShowValue(int myindex, String setdescription) // Display while in setup mode 1090{ // shows what item is setting and its value 1091 1092 for (int i = 0; i < BufferStringLength; i++) {BufferString[i] = ' ';} // clear buffer 1093 setdescription.toCharArray(BufferString, setdesclength); // move description to output buffer 1094 LoadNumber(setarray[setupindex]); 1095 BufferString[setdesclength-1] = ' '; 1096 for (int i = 0; i <setdesclength; i++) {BufferString[i + setdesclength] = TDigits[i];} 1097 if (myindex != setyear) { // only the year is 4 digits, else 2 1098 BufferString[4] = ' '; 1099 BufferString[5] = ' '; 1100 if (BufferString[6] == '0') {BufferString[6] = ' ';} // and suppress leading 0 for 2 digit numbers too 1101 } 1102 BufferString[8] = '\\0'; // and terminate 1103 printText(BufferString, 7, false); 1104 1105 1106} // End of Show Value 1107 1108void LoadNumber(int numtoload) { // Converts number to four digit Ascii array 1109 int tempnum = numtoload; 1110 for (int i = 3; i >= 0; i--) { 1111 TDigits[i] = (tempnum % 10) + DecToAscii;; 1112 tempnum = tempnum / 10; 1113 } 1114} // end of LoadNumber 1115 1116void QueueSound(int SoundFile) { // place a sound to be played into sequence 1117 1118 if ((SoundQueuePtr == SizeQueue) || (SoundQueueCtr <= 0)) { 1119 SoundQueuePtr = -1; // Dont overflow the queue 1120 } 1121 SoundQueuePtr++; // Increment pointer 1122 SoundQueue[SoundQueuePtr] = SoundFile; // and store the sound chosen 1123 SoundQueueCtr++; // and increment # sounds in queue 1124} // end of Queue Sound File 1125 1126void PlaySound() { // Plays appropriate Sound file 1127 if (SoundQueueCtr > 0) { // if nothing in the queue, just return 1128 if (digitalRead(SoundBusyPin) == LOW) { //or if sound card busy, just return 1129 return; 1130 } //until idle 1131 if (SoundPlayPtr == SizeQueue) { 1132 SoundPlayPtr = -1; // Dont go past the queue 1133 } 1134 SoundPlayPtr++; // Increment pointer 1135 digitalWrite(FirstSoundPin + SoundQueue[SoundPlayPtr], LOW); //Play the sound 1136 delay(fxset); // hold to start the sound 1137 digitalWrite(FirstSoundPin + SoundQueue[SoundPlayPtr], HIGH); //Turn off the sound 1138 delay(fxset); // Ensure its set 1139 SoundQueueCtr--; // and decrement sounds yet to be played 1140 if (SoundQueueCtr <= 0) { 1141 SoundPlayPtr = -1; 1142 }; // reset play pointer when queue is empty 1143 } 1144 1145} // end of PlaySound 1146/* 1147 void PlaySoundUART() // uncomment for FX sound board UART use 1148 1149 // sfx.playTrack(name); } 1150 { 1151 Serial.print("\ 1152Playing track \\""); Serial.print(name); Serial.print("\\""); 1153 if (! sfx.playTrack(name) ) { 1154 Serial.println("Failed to play track?"); 1155 } 1156 } 1157*/ 1158 1159 1160#ifdef showoffcode 1161 1162void ShowOff() { // Played at startup -- just the MX Panel Test Graphics 1163 QueueSound(SoundShowOff); // play Game of Thones theme as sound 1164 PlaySound(); 1165 scrollText("Showing Off"); 1166 rows(); 1167 columns(); 1168 cross(); 1169 checkboard(); 1170 bullseye(); 1171 bounce(); 1172 stripe(); 1173 stripe(); 1174 transformation1(); 1175 transformation2(); 1176 bullseye(); 1177 spiral(); 1178 delay(2000); 1179 DisplayIndex = DisplayTime; // force display of time after showing off display 1180} // end of Show Off 1181 1182void rows() // these routines are all 1183// Demonstrates the use of setRow() // from the MX library 1184{mx.clear(); 1185 for (uint8_t row = 0; row < ROW_SIZE; row++) 1186 { 1187 mx.setRow(row, 0xff); 1188 delay(2 * DELAYTIME); 1189 mx.setRow(row, 0x00); 1190 } 1191} 1192 1193void checkboard() 1194// nested rectangles spanning the entire display 1195{ 1196 uint8_t chkCols[][2] = { { 0x55, 0xaa }, { 0x33, 0xcc }, { 0x0f, 0xf0 }, { 0xff, 0x00 } }; 1197 mx.clear(); 1198 for (uint8_t pattern = 0; pattern < sizeof(chkCols) / sizeof(chkCols[0]); pattern++) 1199 { 1200 uint8_t col = 0; 1201 uint8_t idx = 0; 1202 uint8_t rep = 1 << pattern; 1203 1204 while (col < mx.getColumnCount()) 1205 { 1206 for (uint8_t r = 0; r < rep; r++) 1207 mx.setColumn(col++, chkCols[pattern][idx]); // use odd/even column masks 1208 idx++; 1209 if (idx > 1) idx = 0; 1210 } 1211 delay(10 * DELAYTIME); 1212 } 1213} // end of Checkboard 1214 1215void columns() 1216// Demonstrates the use of setColumn() 1217{ 1218 1219 mx.clear(); 1220 1221 for (uint8_t col = 0; col < mx.getColumnCount(); col++) 1222 { 1223 mx.setColumn(col, 0xff); 1224 delay(DELAYTIME / MAX_DEVICES); 1225 mx.setColumn(col, 0x00); 1226 } 1227} 1228 1229void cross() 1230// Combination of setRow() and setColumn() with user controlled 1231// display updates to ensure concurrent changes. 1232{ 1233 1234 mx.clear(); 1235 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); 1236 1237 // diagonally down the display R to L 1238 for (uint8_t i = 0; i < ROW_SIZE; i++) 1239 { 1240 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1241 { 1242 mx.setColumn(j, i, 0xff); 1243 mx.setRow(j, i, 0xff); 1244 } 1245 mx.update(); 1246 delay(DELAYTIME); 1247 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1248 { 1249 mx.setColumn(j, i, 0x00); 1250 mx.setRow(j, i, 0x00); 1251 } 1252 } 1253 1254 // moving up the display on the R 1255 for (int8_t i = ROW_SIZE - 1; i >= 0; i--) 1256 { 1257 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1258 { 1259 mx.setColumn(j, i, 0xff); 1260 mx.setRow(j, ROW_SIZE - 1, 0xff); 1261 } 1262 mx.update(); 1263 delay(DELAYTIME); 1264 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1265 { 1266 mx.setColumn(j, i, 0x00); 1267 mx.setRow(j, ROW_SIZE - 1, 0x00); 1268 } 1269 } 1270 1271 // diagonally up the display L to R 1272 for (uint8_t i = 0; i < ROW_SIZE; i++) 1273 { 1274 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1275 { 1276 mx.setColumn(j, i, 0xff); 1277 mx.setRow(j, ROW_SIZE - 1 - i, 0xff); 1278 } 1279 mx.update(); 1280 delay(DELAYTIME); 1281 for (uint8_t j = 0; j < MAX_DEVICES; j++) 1282 { 1283 mx.setColumn(j, i, 0x00); 1284 mx.setRow(j, ROW_SIZE - 1 - i, 0x00); 1285 } 1286 } 1287 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); 1288} 1289 1290void bullseye() 1291// Demonstrate the use of buffer based repeated patterns 1292// across all devices. 1293{ 1294 1295 mx.clear(); 1296 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); 1297 1298 for (uint8_t n = 0; n < 3; n++) 1299 { 1300 byte b = 0xff; 1301 int i = 0; 1302 1303 while (b != 0x00) 1304 { 1305 for (uint8_t j = 0; j < MAX_DEVICES + 1; j++) 1306 { 1307 mx.setRow(j, i, b); 1308 mx.setColumn(j, i, b); 1309 mx.setRow(j, ROW_SIZE - 1 - i, b); 1310 mx.setColumn(j, COL_SIZE - 1 - i, b); 1311 } 1312 mx.update(); 1313 delay(3 * DELAYTIME); 1314 for (uint8_t j = 0; j < MAX_DEVICES + 1; j++) 1315 { 1316 mx.setRow(j, i, 0); 1317 mx.setColumn(j, i, 0); 1318 mx.setRow(j, ROW_SIZE - 1 - i, 0); 1319 mx.setColumn(j, COL_SIZE - 1 - i, 0); 1320 } 1321 1322 bitClear(b, i); 1323 bitClear(b, 7 - i); 1324 i++; 1325 } 1326 1327 while (b != 0xff) 1328 { 1329 for (uint8_t j = 0; j < MAX_DEVICES + 1; j++) 1330 { 1331 mx.setRow(j, i, b); 1332 mx.setColumn(j, i, b); 1333 mx.setRow(j, ROW_SIZE - 1 - i, b); 1334 mx.setColumn(j, COL_SIZE - 1 - i, b); 1335 } 1336 mx.update(); 1337 delay(3 * DELAYTIME); 1338 for (uint8_t j = 0; j < MAX_DEVICES + 1; j++) 1339 { 1340 mx.setRow(j, i, 0); 1341 mx.setColumn(j, i, 0); 1342 mx.setRow(j, ROW_SIZE - 1 - i, 0); 1343 mx.setColumn(j, COL_SIZE - 1 - i, 0); 1344 } 1345 1346 i--; 1347 bitSet(b, i); 1348 bitSet(b, 7 - i); 1349 } 1350 } 1351 1352 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); 1353} 1354 1355void spiral() 1356// setPoint() used to draw a spiral across the whole display 1357{ 1358 1359 int rmin = 0, rmax = ROW_SIZE - 1; 1360 int cmin = 0, cmax = (COL_SIZE * MAX_DEVICES) - 1; 1361 1362 mx.clear(); 1363 while ((rmax > rmin) && (cmax > cmin)) 1364 { 1365 // do row 1366 for (int i = cmin; i <= cmax; i++) 1367 { 1368 mx.setPoint(rmin, i, true); 1369 delay(DELAYTIME / MAX_DEVICES); 1370 } 1371 rmin++; 1372 1373 // do column 1374 for (uint8_t i = rmin; i <= rmax; i++) 1375 { 1376 mx.setPoint(i, cmax, true); 1377 delay(DELAYTIME / MAX_DEVICES); 1378 } 1379 cmax--; 1380 1381 // do row 1382 for (int i = cmax; i >= cmin; i--) 1383 { 1384 mx.setPoint(rmax, i, true); 1385 delay(DELAYTIME / MAX_DEVICES); 1386 } 1387 rmax--; 1388 1389 // do column 1390 for (uint8_t i = rmax; i >= rmin; i--) 1391 { 1392 mx.setPoint(i, cmin, true); 1393 delay(DELAYTIME / MAX_DEVICES); 1394 } 1395 cmin++; 1396 } 1397} 1398 1399void bounce() 1400// Animation of a bouncing ball 1401{ 1402 const int minC = 0; 1403 const int maxC = mx.getColumnCount() - 1; 1404 const int minR = 0; 1405 const int maxR = ROW_SIZE - 1; 1406 1407 int nCounter = 0; 1408 1409 int r = 0, c = 2; 1410 int8_t dR = 1, dC = 1; // delta row and column 1411 1412 1413 mx.clear(); 1414 1415 while (nCounter++ < 200) 1416 { 1417 mx.setPoint(r, c, false); 1418 r += dR; 1419 c += dC; 1420 mx.setPoint(r, c, true); 1421 delay(DELAYTIME / 2); 1422 1423 if ((r == minR) || (r == maxR)) 1424 dR = -dR; 1425 if ((c == minC) || (c == maxC)) 1426 dC = -dC; 1427 } 1428} 1429void stripe() 1430// Demonstrates animation of a diagonal stripe moving across the display 1431// with points plotted outside the display region ignored. 1432{ 1433 const uint16_t maxCol = MAX_DEVICES*ROW_SIZE; 1434 const uint8_t stripeWidth = 10; 1435 1436 mx.clear(); 1437 1438 for (uint16_t col=0; col<maxCol + ROW_SIZE + stripeWidth; col++) 1439 { 1440 for (uint8_t row=0; row < ROW_SIZE; row++) 1441 { 1442 mx.setPoint(row, col-row, true); 1443 mx.setPoint(row, col-row - stripeWidth, false); 1444 } 1445 delay(DELAYTIME); 1446 } 1447} 1448 1449void transformation1() 1450// Demonstrates the use of transform() to move bitmaps on the display 1451// In this case a user defined bitmap is created and animated. 1452{ 1453 uint8_t arrow[COL_SIZE] = 1454 { 1455 0b00001000, 1456 0b00011100, 1457 0b00111110, 1458 0b01111111, 1459 0b00011100, 1460 0b00011100, 1461 0b00111110, 1462 0b00000000 1463 }; 1464 1465 MD_MAX72XX::transformType_t t[] = 1466 { 1467 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1468 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1469 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1470 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1471 MD_MAX72XX::TFLR, 1472 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1473 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1474 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1475 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1476 MD_MAX72XX::TRC, 1477 MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, 1478 MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, 1479 MD_MAX72XX::TFUD, 1480 MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, 1481 MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, 1482 MD_MAX72XX::TINV, 1483 MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, 1484 MD_MAX72XX::TINV 1485 }; 1486 1487 // transformation 1488 1489 mx.clear(); 1490 1491 // use the arrow bitmap 1492 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); 1493 for (uint8_t j=0; j<mx.getDeviceCount(); j++) 1494 mx.setBuffer(((j+1)*COL_SIZE)-1, COL_SIZE, arrow); 1495 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); 1496 delay(DELAYTIME); 1497 1498 // run through the transformations 1499 mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::ON); 1500 for (uint8_t i=0; i<(sizeof(t)/sizeof(t[0])); i++) 1501 { 1502 mx.transform(t[i]); 1503 delay(DELAYTIME*4); 1504 } 1505 mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::OFF); 1506} 1507 1508void transformation2() 1509// Demonstrates the use of transform() to move bitmaps on the display 1510// In this case font characters are loaded into the display for animation. 1511{ 1512 MD_MAX72XX::transformType_t t[] = 1513 { 1514 MD_MAX72XX::TINV, 1515 MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, 1516 MD_MAX72XX::TINV, 1517 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1518 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1519 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1520 MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, 1521 MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, 1522 MD_MAX72XX::TSD, MD_MAX72XX::TSU, MD_MAX72XX::TSD, MD_MAX72XX::TSU, 1523 MD_MAX72XX::TFLR, MD_MAX72XX::TFLR, MD_MAX72XX::TFUD, MD_MAX72XX::TFUD 1524 };} 1525#endif //showoffcode// 1526
Downloadable files
Clock sounds
The ten sounds used to provide startup sound, Westminster chimes, simple bells and Game of Thrones theme
Clock sounds
See Through Matrix Clock Circuit Connections
Used in lieu of Fritzing diagrams because I can never figure out how to get all the components into the library
See Through Matrix Clock Circuit Connections
See Through Matrix Clock Circuit Connections
Used in lieu of Fritzing diagrams because I can never figure out how to get all the components into the library
See Through Matrix Clock Circuit Connections
Clock sounds
The ten sounds used to provide startup sound, Westminster chimes, simple bells and Game of Thrones theme
Clock sounds
Documentation
Sound file for clock
Contains the 10 sounds used and stored on the FX soundboard in OGG Format
Sound file for clock
Sound file for clock
Contains the 10 sounds used and stored on the FX soundboard in OGG Format
Sound file for clock
Enclosure
Picture of enclosure
Enclosure
Comments
Only logged in users can leave comments
spudnut1
0 Followers
•0 Projects
Table of contents
Intro
3
0