Created by Christopher & Jessica Hogan 2017 #include <Time.h> #include <TimeLib.h> #include <FastLED.h> #include <Adafruit_NeoPixel.h> #ifdef __AVR__ #include <avr/power.h> #endif #define PI_PIN 8 #define NUM_PI_PIXELS 13 #define CLOCK_PIN 10 #define NUM_CLOCK_PIXELS 66 #define LED_PIN 10 #define NUM_LEDS 66 #define BRIGHTNESS 64 #define LED_TYPE WS2811 #define COLOR_ORDER GRB CRGB leds [ NUM_LEDS ]; #define UPDATES_PER_SECOND 100 // 3.14159.... but in reverse because the neopixels are wired in the // opposite way, and they only work in one direction. The array could // be specified in the regular order if the neopixels had been soldered // in the reverse direction. int piDigits [] = { 0 , 3 , 2 , 9 , 5 , 4 , 4 , 9 , 4 , 7 , 9 , 0 , 2 , 8 , 5 , 0 , 1 , 5 , 7 , 3 , 9 , 9 , 3 , 9 , 6 , 1 , 7 , 9 , 1 , 4 , 8 , 8 , 2 , 0 , 5 , 9 , 7 , 2 , 3 , 8 , 3 , 3 , 4 , 6 , 2 , 6 , 4 , 8 , 3 , 2 , 3 , 9 , 7 , 9 , 8 , 5 , 3 , 5 , 6 , 2 , 9 , 5 , 1 , 4 , 1 , 3 }; // This is how we clear the digits that aren't being lit. // Whenever a digit is lit, the corresponding index into piDigits // is stored in the array according to the scheme hh:mm for indices // 0,1,2,3. So, if the time were 2:36, litPixels would have the // value [-1, 59, 1, 24]. The hour is indexed starting at the end // of piDigits, and the minute is indexed starting at the beginning. // A -1 indicates that nothing in that position needs cleared. For // the example, the first hour digit is not used, so -1. The next // hour digit is 2, and the first instance of 2 from the end of the // piDigits array is at index 59. The minutes start at the beginning // of the array. The first instance of 3 is at index 1, and the first // instance of 6 is at index 24. int litPixels [ 4 ] = { - 1 , - 1 , - 1 , - 1 }; // The pixels that comprise the pi symbol Adafruit_NeoPixel piPixels = Adafruit_NeoPixel ( NUM_PI_PIXELS , PI_PIN , NEO_GRB + NEO_KHZ800 ); // The pixels that comprise all the numbers Adafruit_NeoPixel clockPixels = Adafruit_NeoPixel ( NUM_CLOCK_PIXELS , CLOCK_PIN , NEO_GRB + NEO_KHZ800 ); // We need to keep track of the previous minute, second, and // hour so we know when they have changed. Initialize them // all to -1 so the clock starts working right away. int previousSecond = - 1 ; int previousMinute = - 1 ; int previousHour = - 1 ; // This variable keeps track of even seconds becuase we blink // the pi symbol every other (even) second. bool even = true ; extern CRGBPalette16 myRedWhiteBluePalette ; extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM ; CRGBPalette16 currentPalette ; TBlendType currentBlending ; void setup () { delay ( 3000 ); // power-up safety delay // Serial.begin(9600); int hour = 6 ; int minute = 23 ; int second = 0 ; int day = 9 ; int month = 3 ; int year = 2017 ; setTime ( hour , minute , second , day , month , year ); piPixels . begin (); clockPixels . begin (); FastLED . addLeds < LED_TYPE , LED_PIN , COLOR_ORDER > ( leds , NUM_LEDS ). setCorrection ( TypicalLEDStrip ); FastLED . setBrightness ( BRIGHTNESS ); currentPalette = RainbowColors_p ; currentBlending = LINEARBLEND ; } void loop () { ///////////// // Pi code // ///////////// int currentSecond = second (); if ( previousSecond != currentSecond ) { even = ! even ; if ( even ) { int r = random ( 0 , 256 ); int g = random ( 0 , 256 ); int b = random ( 0 , 256 ); for ( int i = 0 ; i < NUM_PI_PIXELS ; i ++ ) { piPixels . setPixelColor ( i , piPixels . Color ( r , g , b )); piPixels . show (); } } else { for ( int i = 0 ; i < NUM_PI_PIXELS ; i ++ ) { piPixels . setPixelColor ( i , 0 ); piPixels . show (); } } previousSecond = currentSecond ; } ////////////////// // Clock Digits // ////////////////// int currentHour = hourFormat12 (); int currentMinute = minute (); if ( currentHour == 3 && currentMinute == 14 ) { // Run crazy pi colors for 1 minute piTime (); return ; } // Handle hour if ( currentHour != previousHour ) { if ( currentHour >= 1 && currentHour <= 9 ) { // 1 digit -> Orange int hourPixelOnes = - 1 ; for ( int i = NUM_CLOCK_PIXELS ; i > 0 ; i -- ) { if ( piDigits [ i ] == currentHour ) { hourPixelOnes = i ; break ; } } clearLitHour (); clockPixels . setPixelColor ( hourPixelOnes , clockPixels . Color ( 225 , 34 , 4 )); litPixels [ 0 ] = - 1 ; litPixels [ 1 ] = hourPixelOnes ; clockPixels . show (); } else { // 2 digits // Red Yellow int tensHourPixel = 64 ; int onesHourPixel ; if ( currentHour == 10 ) { onesHourPixel = 33 ; } else if ( currentHour == 11 ) { onesHourPixel = 62 ; } else { // 12 onesHourPixel = 59 ; } clearLitHour (); // Red clockPixels . setPixelColor ( tensHourPixel , clockPixels . Color ( 255 , 0 , 0 )); // Yellow clockPixels . setPixelColor ( onesHourPixel , clockPixels . Color ( 190 , 190 , 0 )); // Remember which pixel we lit so we can turn it off later litPixels [ 0 ] = tensHourPixel ; litPixels [ 1 ] = onesHourPixel ; clockPixels . show (); } previousHour = currentHour ; } // Handle minute if ( currentMinute != previousMinute ) { if ( currentMinute == 0 ) { // Exactly on the hour, so light no minutes clearLitMinute (); litPixels [ 2 ] = - 1 ; litPixels [ 3 ] = - 1 ; clockPixels . show (); } else if ( currentMinute >= 1 && currentMinute <= 9 ) { // 1 digit -> Yellow int pixelToLight = - 1 ; for ( int i = 0 ; i < NUM_CLOCK_PIXELS ; i ++ ) { if ( piDigits [ i ] == currentMinute ) { pixelToLight = i ; break ; } } clearLitMinute (); litPixels [ 2 ] = - 1 ; if ( currentHour > 9 ) { // Green clockPixels . setPixelColor ( pixelToLight , clockPixels . Color ( 0 , 150 , 0 )); } else { // Yellow clockPixels . setPixelColor ( pixelToLight , clockPixels . Color ( 190 , 190 , 0 )); } // Remember which pixel we lit so we can turn it off later litPixels [ 3 ] = pixelToLight ; clockPixels . show (); } else { // Minute is 2 digits int tensMinutePixel = - 1 ; int onesMinutePixel = - 1 ; String minuteString = String ( currentMinute ); const char tensChar = minuteString [ 0 ]; const char onesChar = minuteString [ 1 ]; tensMinutePixel = - 1 ; onesMinutePixel = - 1 ; onesMinutePixel = findIndexOf ( onesChar ); if ( tensChar == onesChar ) { for ( int i = onesMinutePixel + 1 ; i < NUM_CLOCK_PIXELS ; i ++ ) { if ( piDigits [ i ] == tensChar - '0' ) { tensMinutePixel = i ; break ; } } } else { tensMinutePixel = findIndexOf ( tensChar ); } clearLitMinute (); if ( currentHour < 10 ) { // turquoise clockPixels . setPixelColor ( tensMinutePixel , clockPixels . Color ( 0 , 174 , 255 )); // purple clockPixels . setPixelColor ( onesMinutePixel , clockPixels . Color ( 135 , 0 , 255 )); } else { // Green clockPixels . setPixelColor ( tensMinutePixel , clockPixels . Color ( 0 , 255 , 0 )); // Blue clockPixels . setPixelColor ( onesMinutePixel , clockPixels . Color ( 0 , 0 , 255 )); } // Remember which pixel we lit so we can turn it off later litPixels [ 2 ] = tensMinutePixel ; litPixels [ 3 ] = onesMinutePixel ; clockPixels . show (); } previousMinute = currentMinute ; } } int findIndexOf ( char digit ) { for ( int i = 0 ; i < NUM_CLOCK_PIXELS ; i ++ ) { if ( piDigits [ i ] == digit - '0' ) { return i ; } } return - 1 ; } int findReverseIndexOf ( char digit ) { for ( int i = NUM_CLOCK_PIXELS - 1 ; i >= 0 ; i -- ) { if ( piDigits [ i ] == digit - '0' ) { return i ; } } return - 1 ; } void clearLitMinute () { if ( litPixels [ 2 ] != - 1 ) { clockPixels . setPixelColor ( litPixels [ 2 ], 0 ); } if ( litPixels [ 3 ] != - 1 ) { clockPixels . setPixelColor ( litPixels [ 3 ], 0 ); } } void clearLitHour () { if ( litPixels [ 0 ] != - 1 ) { clockPixels . setPixelColor ( litPixels [ 0 ], 0 ); } if ( litPixels [ 1 ] != - 1 ) { clockPixels . setPixelColor ( litPixels [ 1 ], 0 ); } } void piTime () { int keepRunning = 5 ; while ( keepRunning > 0 ) { ChangePalettePeriodically (); static uint8_t startIndex = 0 ; startIndex = startIndex + 1 ; /* motion speed */ FillLEDsFromPaletteColors ( startIndex ); FastLED . show (); FastLED . delay ( 1000 / UPDATES_PER_SECOND ); keepRunning -- ; } } void FillLEDsFromPaletteColors ( uint8_t colorIndex ) { uint8_t brightness = 255 ; for ( int i = 0 ; i < NUM_LEDS ; i ++ ) { leds [ i ] = ColorFromPalette ( currentPalette , colorIndex , brightness , currentBlending ); colorIndex += 3 ; } } // There are several different palettes of colors demonstrated here. // // FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p, // OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p. // // Additionally, you can manually define your own color palettes, or you can write // code that creates color palettes on the fly. All are shown here. void ChangePalettePeriodically () { uint8_t secondHand = ( millis () / 1000 ) % 60 ; static uint8_t lastSecond = 99 ; if ( lastSecond != secondHand ) { lastSecond = secondHand ; if ( secondHand == 0 ) { currentPalette = RainbowColors_p ; currentBlending = LINEARBLEND ; } if ( secondHand == 10 ) { currentPalette = RainbowStripeColors_p ; currentBlending = NOBLEND ; } if ( secondHand == 15 ) { currentPalette = RainbowStripeColors_p ; currentBlending = LINEARBLEND ; } if ( secondHand == 20 ) { SetupPurpleAndGreenPalette (); currentBlending = LINEARBLEND ; } if ( secondHand == 25 ) { SetupTotallyRandomPalette (); currentBlending = LINEARBLEND ; } if ( secondHand == 30 ) { SetupBlackAndWhiteStripedPalette (); currentBlending = NOBLEND ; } if ( secondHand == 35 ) { SetupBlackAndWhiteStripedPalette (); currentBlending = LINEARBLEND ; } if ( secondHand == 40 ) { currentPalette = CloudColors_p ; currentBlending = LINEARBLEND ; } if ( secondHand == 45 ) { currentPalette = PartyColors_p ; currentBlending = LINEARBLEND ; } if ( secondHand == 50 ) { currentPalette = myRedWhiteBluePalette_p ; currentBlending = NOBLEND ; } if ( secondHand == 55 ) { currentPalette = myRedWhiteBluePalette_p ; currentBlending = LINEARBLEND ; } } } // This function fills the palette with totally random colors. void SetupTotallyRandomPalette () { for ( int i = 0 ; i < 16 ; i ++ ) { currentPalette [ i ] = CHSV ( random8 (), 255 , random8 ()); } } // This function sets up a palette of black and white stripes, // using code. Since the palette is effectively an array of // sixteen CRGB colors, the various fill_* functions can be used // to set them up. void SetupBlackAndWhiteStripedPalette () { // 'black out' all 16 palette entries... fill_solid ( currentPalette , 16 , CRGB :: Black ); // and set every fourth one to white. currentPalette [ 0 ] = CRGB :: White ; currentPalette [ 4 ] = CRGB :: White ; currentPalette [ 8 ] = CRGB :: White ; currentPalette [ 12 ] = CRGB :: White ; } // This function sets up a palette of purple and green stripes. void SetupPurpleAndGreenPalette () { CRGB purple = CHSV ( HUE_PURPLE , 255 , 255 ); CRGB green = CHSV ( HUE_GREEN , 255 , 255 ); CRGB black = CRGB :: Black ; currentPalette = CRGBPalette16 ( green , green , black , black , purple , purple , black , black , green , green , black , black , purple , purple , black , black ); } // This example shows how to set up a static color palette // which is stored in PROGMEM (flash), which is almost always more // plentiful than RAM. A static PROGMEM palette like this // takes up 64 bytes of flash. const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM = { CRGB :: Red , CRGB :: Gray , // 'white' is too bright compared to red and blue CRGB :: Blue , CRGB :: Black , CRGB :: Red , CRGB :: Gray , CRGB :: Blue , CRGB :: Black , CRGB :: Red , CRGB :: Red , CRGB :: Gray , CRGB :: Gray , CRGB :: Blue , CRGB :: Blue , CRGB :: Black , CRGB :: Black };