Skip to content

Instantly share code, notes, and snippets.

@jazzyjackson
Last active April 9, 2017 17:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jazzyjackson/27ea9fa039b1f080b86ba6f2dacc7baf to your computer and use it in GitHub Desktop.
Save jazzyjackson/27ea9fa039b1f080b86ba6f2dacc7baf to your computer and use it in GitHub Desktop.
// 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
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment