|
#include "FastLED.h" |
|
|
|
#define BTN_PIN 3 |
|
#define LED_PIN A3 |
|
#define LED_TYPE WS2812B |
|
#define LED_COLOR 0x7aff0b |
|
#define TOTAL_LEDS 110 |
|
#define BRIGHTNESS 123 /* 0 to 255 */ |
|
#define LOWEST_BRIGHTNESS 5 /* 0 to 255 */ |
|
|
|
#define CHUNK_SIZE_MS 300000 /* 5 minute chunks (1 sentence per chunk) */ |
|
#define INTERVAL_MS 250 |
|
#define HOUR_MAX_LEN 6 /* Maximum amount of letters an hour can have */ |
|
#define SENTENCE_MAX_LEN 24 /* Maximum amount of letters a sentence can have */ |
|
#define POWERUP_SAFETY_DELAY 3000 /* Avoid to fry the LEDs on startup */ |
|
#define DEBUG_MODE false /* Same as holding down the push button */ |
|
|
|
CRGB leds[TOTAL_LEDS]; |
|
|
|
/* Clock starts at 9am (o'clock) */ |
|
int HOUR_LETTERS[12][HOUR_MAX_LEN] = { |
|
{51,52,53,54}, /* N I N E */ |
|
{109,108,107}, /* T E N */ |
|
{82,81,80,79,78,77}, /* E L E V E N */ |
|
{93,94,95,96,97,98}, /* T W E L V E */ |
|
{65,64,63}, /* O N E */ |
|
{74,75,76}, /* T W O */ |
|
{59,58,57,56,55}, /* T H R E E */ |
|
{66,67,68,69}, /* F O U R */ |
|
{70,71,72,73}, /* F I V E */ |
|
{62,61,60}, /* S I X */ |
|
{88,89,90,91,92}, /* S E V E N */ |
|
{87,86,85,84,83} /* E I G H T */ |
|
}; |
|
|
|
/* Replaceable numbers is the fastest solution I found for variable hours */ |
|
/* e.g.: 888 = {hours}, 999 = {next hours} */ |
|
int SENTENCE_LETTERS[12][SENTENCE_MAX_LEN] = { |
|
{0,1, 3,4, 104,103,102,101,100,99, 888}, /* IT IS {hs} O'CLOCK */ |
|
{0,1, 3,4, 28,29,30,31, 44,45,46,47, 888}, /* IT IS FIVE PAST {hs} */ |
|
{0,1, 3,4, 38,37,36, 44,45,46,47, 888}, /* IT IS TEN PAST {hs} */ |
|
{0,1, 3,4, 19,18,17,16,15,14,13, 44,45,46,47, 888}, /* IT IS QUARTER PAST {hs} */ |
|
{0,1, 3,4, 22,23,24,25,26,27, 44,45,46,47, 888}, /* IT IS TWENTY PAST {hs} */ |
|
{0,1, 3,4, 22,23,24,25,26,27, 28,29,30,31, 44,45,46,47, 888}, /* IT IS TWENTYFIVE PAST {hs} */ |
|
{0,1, 3,4, 43,42,41,40, 44,45,46,47, 888}, /* IT IS half PAST {hs} */ |
|
{0,1, 3,4, 22,23,24,25,26,27, 28,29,30,31, 34,33, 999}, /* IT IS TWENTYFIVE TO {next-hs} */ |
|
{0,1, 3,4, 22,23,24,25,26,27, 34,33, 999}, /* IT IS TWENTY TO {next-hs} */ |
|
{0,1, 3,4, 19,18,17,16,15,14,13, 34,33, 999}, /* IT IS QUARTER TO {next-hs} */ |
|
{0,1, 3,4, 38,37,36, 34,33, 999}, /* IT IS TEN TO {next-hs} */ |
|
{0,1, 3,4, 28,29,30,31, 34,33, 999} /* IT IS FIVE TO {next-hs} */ |
|
}; |
|
|
|
int manuallyEnteredChunk = 0; /* Updated with the button */ |
|
int currentSentenceOrder = 42; /* Answer to the ultimate question of life, the universe, and everything */ |
|
int prevHourOrder = 0; |
|
|
|
void setup() { |
|
delay(POWERUP_SAFETY_DELAY); |
|
FastLED.addLeds<LED_TYPE, LED_PIN>(leds, TOTAL_LEDS); |
|
FastLED.setBrightness(BRIGHTNESS); |
|
/* Initialize button pin as a pull-up input. */ |
|
pinMode(BTN_PIN, INPUT_PULLUP); |
|
} |
|
|
|
void loop() { |
|
int buttonState = digitalRead(BTN_PIN); |
|
if (buttonState == LOW || DEBUG_MODE) { |
|
manuallyEnteredChunk = manuallyEnteredChunk + 1; |
|
} |
|
|
|
int chunk = floor(millis() / CHUNK_SIZE_MS) + manuallyEnteredChunk; |
|
int sentenceOrder = chunk % 12; |
|
|
|
/* Avoid to change anything if there was no change in the sentence */ |
|
if (sentenceOrder != currentSentenceOrder) { |
|
currentSentenceOrder = sentenceOrder; |
|
|
|
int hourChunk = floor(chunk / 12); |
|
int hourOrder24 = hourChunk % 24; /* 0 to 24 on HOUR_LETTERS */ |
|
|
|
cleanDisplay(); |
|
|
|
/* Only paint LEDs between 9am and 11pm */ |
|
if (hourOrder24 <= 13) { |
|
/* Lower the brightness between 9pm and 9am */ |
|
FastLED.setBrightness(hourOrder24 >= 12 ? LOWEST_BRIGHTNESS : BRIGHTNESS); |
|
|
|
for (int i = 0; i < SENTENCE_MAX_LEN; i++) { |
|
int letter = SENTENCE_LETTERS[sentenceOrder][i]; |
|
if (letter < TOTAL_LEDS) { |
|
paintLED(letter); |
|
} else if (letter == 888) { |
|
int hourOrder = hourChunk % 12; /* 0 to 12 */ |
|
for (int j = 0; j < HOUR_MAX_LEN; j++) { |
|
paintLED(HOUR_LETTERS[hourOrder][j]); |
|
} |
|
} else if (letter == 999) { |
|
int nextHourChunk = hourChunk + 1; |
|
int nextHourOrder = nextHourChunk % 12; /* 0 to 12 */ |
|
for (int j = 0; j < HOUR_MAX_LEN; j++) { |
|
paintLED(HOUR_LETTERS[nextHourOrder][j]); |
|
} |
|
} |
|
} |
|
} |
|
|
|
FastLED.show(); |
|
} |
|
|
|
delay(INTERVAL_MS); |
|
} |
|
|
|
void cleanDisplay() { |
|
for (int i = 0; i < TOTAL_LEDS; i++) { |
|
leds[i] = CRGB::Black; |
|
} |
|
} |
|
|
|
void paintLED(int i) { |
|
leds[i].setColorCode(LED_COLOR); |
|
} |
Connected pins
Top
GND
: Negative (-) wire for the LED stripVCC
: Positive (+5V) wire for the LED stripA3
: Data for the LED strip (defined asLED_PIN
in the code)Bottom
GND
: Negative (-) leg of the push button3
: Positive (+) leg of the push button (defined asBTN_PIN
in the code)