## Things used in this project

Hardware components:
 Neopixels
×1
 Arduino Arduino UNO & Genuino UNO
×1
Hand tools and fabrication machines:
 Soldering iron (generic)

## Schematics

Lighting up neopixels to tell time!

## Code

##### PiClockArduino
For use with a fabric pi quilt & neopixels!
```Created by Christopher & Jessica Hogan 2017

#include <Time.h>
#include <TimeLib.h>
#include <FastLED.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];

// 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
// The pixels that comprise all the numbers

// 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();
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
};
```

## Credits

##### user460763
Thanks to Arduino, Adafruit, and Pale Gray Labs.

## Replications

Did you replicate this project? Share it!

Love this project? Think it could be improved? Tell us what you think!

#### Similar projects you might like

Intermediate
• 58
• 4

Full instructions

Do you have a pen stand that just sits there and does nothing? Can we make it play a game/flash LEDs every time you use it? Let's find out!

#### Pen Stand: Gaming Enabled

Intermediate
• 1,064
• 74

Protip

Want to make an Arduino having only an ATmega328 chip and nothing else? Not even the crystal oscillator!

#### Arduino on Internal Oscillator Crystal as Clock Source

Naman Chauhan

Intermediate
• 240
• 8

Protip

this is self balancing robot on two wheels using mpu6050 accelerometer based on arduino..

#### Self Balancing Robot Using Mpu6050 Accelerometer

s_r tronics

Intermediate
• 173
• 3

Protip

this is our project..and new thats called autobot with dancing like michael jackson....

#### Dancing Autobot

s_r tronics

Intermediate
• 5,695
• 42

Full instructions

Barbot is an open source Arduino cocktail mixing robot controlled with the hybrid mobile app via Bluetooth.

#### Barbot: Cocktail Mixing Robot

sidlauskas

Intermediate
• 4,981
• 35

Full instructions

A vehicle measures distance with an encoder on its wheel. It is remotely controlled and transmits the distance via Websocket.

Matthew Lee