// https://forum.arduino.cc/t/60-led-ws2812-clock-using-standalone-atmega328p/1238273/
// https://wokwi.com/projects/393090717241801729
// https://youtu.be/Uv6MeIe0qME
// Add options for background settings
// TO DO: Clean up and comment code...
// BUG: demo state should start as displayMode 0 after intro
// NICE TO HAVE: When alarm is cancelled, the alarm still remains set for next day.
// 20240323 - "dimming" added as a divisor to "255" values. if 18 > hour > 7 divisor is "4,"" else "1"
// 20240323 - grouped Serial Monitor output
//Add the following libraries to the respective folder for you operating system. See http://arduino.cc/en/Guide/Environment
#include <FastLED.h> // FastSPI Library from http://code.google.com/p/fastspi/
#include <Wire.h> //This is to communicate via I2C. On arduino Uno & Nano use pins A4 for SDA (yellow/orange) and A5 for SCL (green). For other boards see http://arduino.cc/en/Reference/Wire
#include <RTClib.h> // Include the RTClib library to enable communication with the real time clock.
#include <EEPROM.h> // Include the EEPROM library to enable the storing and retrevel of settings.
#include "Bounce.h" // Include the Bounce library for de-bouncing issues with push buttons.
#include <Encoder.h> // Include the Encoder library to read the out puts of the rotary encoders
RTC_DS1307 RTC; // Establishes the chipset of the Real Time Clock
#define LEDStripPin A0 // Pin used for the data to the LED strip
#define menuPin A3 // Pin used for the menu button (green stripe)
#define numLEDs 60 // Number of LEDs in strip
int dimming = 1; // "dimming" starts with "1" for "255/1" and assigned a larger value during dark hours
int dimValue = 4; // try 2, 4, 8, 16
// Setting up the LED strip
struct CRGB leds[numLEDs];
Encoder rotary1(2, 3); // Setting up the Rotary Encoder
DateTime old; // Variable to compare new and old time, to see if it has moved on.
int rotary1Pos = 0;
int subSeconds; // 60th's of a second
int secondBrightness;
int secondBrightness2;
int breathBrightness;
long newSecTime; // Variable to record when a new second starts, allowing to create milli seconds
long oldSecTime;
long flashTime; //
// long breathCycleTime; // not used
#define holdTime 1500
int cyclesPerSec;
float cyclesPerSecFloat; // So can be used as a float in calcs
float fracOfSec;
// float breathFracOfSec; // not used
boolean demo;
#define demoTime 12 // seconds
long previousDemoTime;
long currentDemoTime;
boolean swingBack = false;
int timeHour;
int timeMin;
int timeSec;
int alarmMin; // The minute of the alarm
int alarmHour; // The hour of the alarm 0-23
int alarmDay = 0; // The day of the alarm
boolean alarmSet; // Whether the alarm is set or not
int modeAddress = 0; // Address of where mode is stored in the EEPROM
int alarmMinAddress = 1; // Address of where alarm minute is stored in the EEPROM
int alarmHourAddress = 2; // Address of where alarm hour is stored in the EEPROM
int alarmSetAddress = 3; // Address of where alarm state is stored in the EEPROM
int alarmModeAddress = 4; // Address of where the alarm mode is stored in the EEPROM
boolean alarmTrig = false; // Whether the alarm has been triggered or not
long alarmTrigTime; // Milli seconds since the alarm was triggered
boolean countDown = false;
long countDownTime = 0;
long currentCountDown = 0;
long startCountDown;
int countDownMin;
int countDownSec;
int countDownFlash;
int demoIntro = 0;
int j = 0;
long timeInterval = 5;
long currentMillis;
long previousMillis = 0;
float LEDBrightness = 0;
float fadeTime;
float brightFadeRad;
int state = 0; // Variable of the state of the clock, with the following defined states
char *states[] = {"clockState",
"alarmState",
"setAlarmHourState",
"setAlarmMinState",
"setClockHourState",
"setClockMinState",
"setClockSecState",
"countDownState",
"demoState"
};
#define clockState 0
#define alarmState 1
#define setAlarmHourState 2
#define setAlarmMinState 3
#define setClockHourState 4
#define setClockMinState 5
#define setClockSecState 6
#define countDownState 7
#define demoState 8
// int mode; // CHANGED TO displayMode // Variable of the display mode of the clock
int displayMode; // Variable of the display mode of the clock
int modeMax = 6; // Change this when new modes are added. This is so selecting modes can go back beyond.
int alarmMode; // Variable of the alarm display mode
int alarmModeMax = 3;
Bounce menuBouncer = Bounce(menuPin, 20); // Instantiate a Bounce object with a 50 millisecond debounce time for the menu button
boolean menuButton = false;
boolean menuPressed = false;
boolean menuReleased = false;
int advanceMove = 0;
boolean countTime = false;
long menuTimePressed;
long lastRotary;
int rotaryTime = 1000;
int LEDPosition;
int reverseLEDPosition;
int pendulumPos;
int fiveMins;
int odd;
int LEDOffset = 30;
void setup() {
// Set up all pins
pinMode(menuPin, INPUT_PULLUP); // Uses the internal 20k pull up resistor. Pre Arduino_v.1.0.1 need to be "digitalWrite(menuPin,HIGH);pinMode(menuPin,INPUT);"
// Start LEDs
LEDS.addLeds<WS2812, LEDStripPin, GRB>(leds, numLEDs); // Structure of the LED data. I have changed to from rgb to grb, as using an alternative LED strip. Test & change these if you're getting different colours.
// Start RTC
Wire.begin(); // Starts the Wire library allows I2C communication to the Real Time Clock
RTC.begin(); // Starts communications to the RTC
Serial.begin(9600); // Starts the serial communications
// Uncomment to reset all the EEPROM addresses. You will have to comment again and reload, otherwise it will not save anything each time power is cycled
// write a 0 to all 512 bytes of the EEPROM
// for (int i = 0; i < 512; i++)
// {EEPROM.write(i, 0);}
// Load any saved setting since power off, such as mode & alarm time
displayMode = EEPROM.read(modeAddress); // The mode will be stored in the address "0" of the EEPROM
alarmMin = EEPROM.read(alarmMinAddress); // The mode will be stored in the address "1" of the EEPROM
alarmHour = EEPROM.read(alarmHourAddress); // The mode will be stored in the address "2" of the EEPROM
alarmSet = EEPROM.read(alarmSetAddress); // The mode will be stored in the address "2" of the EEPROM
alarmMode = EEPROM.read(alarmModeAddress);
// Prints all the saved EEPROM data to Serial
Serial.print("Display Mode ");
Serial.println(displayMode);
// Serial.print("Alarm Hour is ");
// Serial.println(alarmHour);
// Serial.print("Alarm Min is ");
// Serial.println(alarmMin);
// Serial.print("Alarm is set ");
// Serial.println(alarmSet);
// Serial.print("Alarm Mode is ");
// Serial.println(alarmMode);
Serial.print("Alarm (HH:MM) ");
if (alarmHour < 10) Serial.print("0"); // prefix a 0 if < 9
Serial.print(alarmHour);
Serial.print(":");
if (alarmMin < 10) Serial.print("0"); // prefix a 0 if < 9
Serial.print(alarmMin);
Serial.print(" ");
if (alarmSet) Serial.print("ON"); else Serial.print("OFF");
Serial.print(" Mode ");
Serial.println(alarmMode);
// create a loop that calcuated the number of counted milliseconds between each second.
DateTime now = RTC.now();
// startTime = millis();
// while (RTC.old() = RTC.new())
// if (now.month() == 1 && now.day() == 1 && now.hour() == 0 && now.minute() == 0 && now.minute() == 0)
// {}
Serial.print("Time (HH:MM.SS) ");
if (now.hour() < 10) Serial.print(" "); // prefix with " " if < 9
Serial.print(now.hour());
Serial.print(":");
if (now.minute() < 10) Serial.print("0"); // prefix with "0" if < 9
Serial.print(now.minute());
Serial.print(".");
if (now.second() < 10) Serial.print("0"); // prefix with "0" if < 9
Serial.println(now.second());
Serial.print("Date (YYYY/MM/DD) ");
Serial.print(now.year());
Serial.print("/");
if (now.month() < 10) Serial.print("0"); // prefix with "0" if < 9
Serial.print(now.month());
Serial.print("/");
if (now.day() < 10) Serial.print("0"); // prefix with "0" if < 9
Serial.println(now.day());
}
void loop() {
int DST; // for adding an hour during months 10 (last Sunday) through 03 (last Sunday)
DateTime now = RTC.now(); // Fetches the time from RTC
// Determine non-Standard time to add one hour
// https://forum.arduino.cc/t/60-led-ws2812-clock-using-standalone-atmega328p/1238273/14
// MOVE THIS TO A FUNCTION
/*
- Begin "standard" time (DST) on Last Sunday in October at 02h00m
- End "standard" time (DST) on Last Sunday in March at 02h00m
- Findig UK DST dates...
- The date will range from 25 (if month begins on Thursday) to 31 (if month begins on Friday)
- if 'now' is in the months October (10) through March (3) INCLUSIVE
&&
within the date range of 25 through 31 INCLUSIVE
&&
the date in the date range falls on a Sunday (1) ***
&&
the time is on or after 02h00m
then
DST = 0 else DST = 1
do not use this...
rtc.adjust(DateTime(now.year(), now.month(), now.date(), now.hour() + 1, now.minute, now.second()));
This yields the date...
int yy = 24;
void setup() {
Serial.begin(115200);
int x1 = (5 + yy + (yy / 4)) % 7;
int x2 = (2 + yy + (yy / 4)) % 7;
Serial.println(31 - x1); // for March
Serial.println(31 - x2); // for October
}
void loop() {
// put your main code here, to run repeatedly:
}
*/
// if (
// ((now.month() >= 10 ) || (now.month() <= 3)) // October (10) through March (3) INCLUSIVE
// &&
// now.dayOfTheWeek() == 1)
// DST = 1; // "Saving" time
// else // months 11, 12, 1, 2
// DST = 0; // Standard time
// create a neopixel "dimming" value for hours of darkness
if (now.hour() > 18 || now.hour() < 7)
dimming = dimValue; // divisor for brightness... 255/dimming
else dimming = 1; // (255 / 1) = 255 (full brightness)
// Check for any button presses and action accordingley
menuButton = menuBouncer.update(); // Update the debouncer for the menu button and saves state to menuButton
rotary1Pos = rotary1.read(); // Checks the rotary position
if (rotary1Pos <= -2 && lastRotary - millis() >= rotaryTime) {
advanceMove = -1;
rotary1.write(0);
lastRotary = millis();
}
if (rotary1Pos >= 2 && lastRotary - millis() >= rotaryTime) {
advanceMove = 1;
rotary1.write(0);
lastRotary = millis();
}
if (menuButton == true || advanceMove != 0 || countTime == true) {
buttonCheck(menuBouncer, now);
}
// clear LED array
memset(leds, 0, numLEDs * 3);
// Check alarm and trigger if the time matches
if (alarmSet == true && alarmDay != now.day()) // The alarmDay statement ensures it is a newly set alarm or repeat from previous day, not within the minute of an alarm cancel.
{
if (alarmTrig == false) {
alarm(now);
} else {
alarmDisplay();
}
}
// Check the Countdown Timer
if (countDown == true) {
currentCountDown = countDownTime + startCountDown - now.unixtime();
if (currentCountDown <= 0) {
state = countDownState;
}
}
// Set the time LED's
if (state == setClockHourState || state == setClockMinState || state == setClockSecState) {
setClockDisplay(now);
} else if (state == alarmState || state == setAlarmHourState || state == setAlarmMinState) {
setAlarmDisplay();
} else if (state == countDownState) {
countDownDisplay(now);
} else if (state == demoState) {
runDemo(now);
} else {
timeDisplay(now);
}
// Update LEDs
LEDS.show();
}
void buttonCheck(Bounce menuBouncer, DateTime now) {
Serial.print("buttonCheck(): ");
if (menuBouncer.fallingEdge()) // Checks if a button is pressed, if so sets countTime to true
{
countTime = true;
// Serial.println("rising edge"); // looks like "rising" is an error
Serial.print("(falling edge)");
}
if (menuBouncer.risingEdge()) // Checks if a button is released,
{
countTime = false;
Serial.print("(rising edge)");
} // if so sets countTime to false. Now the ...TimePressed will not be updated when enters the buttonCheck,
if (countTime) // otherwise will menuBouncer.duration will
{
menuTimePressed = menuBouncer.duration();
if (menuTimePressed >= (holdTime - 100) && menuTimePressed <= holdTime) {
clearLEDs();
LEDS.show();
delay(100);
}
}
menuReleased = menuBouncer.risingEdge();
menuPressed = menuBouncer.fallingEdge(); // added for next condition on menuPressed
if (menuPressed == true) { // "menuPressed" added above
Serial.print(". Menu Button (pressed).");
}
if (menuReleased == true) {
Serial.print(". Menu Button (released).");
}
Serial.print(" Bounce Duration (");
Serial.print(menuTimePressed);
Serial.println(").");
if (alarmTrig == true) {
alarmTrig = false;
alarmDay = now.day(); // When the alarm is cancelled it will not display until next day. As without it, it would start again if within a minute, or completely turn off the alarm.
delay(300); // I added this 300ms delay, so there is time for the button to be released
return; // This return exits the buttonCheck function, so no actions are performs
}
switch (state) {
case clockState: // State 0
if (advanceMove == -1 && displayMode == 0) {
displayMode = modeMax;
advanceMove = 0;
} else if (advanceMove != 0) //if displaying the clock, advance button is pressed & released, then mode will change
{
displayMode = displayMode + advanceMove;
EEPROM.write(modeAddress, displayMode);
advanceMove = 0;
} else if (menuReleased == true) {
if (menuTimePressed <= holdTime) {
state = alarmState;
newSecTime = millis();
} // if displaying the clock, menu button is pressed & released, then Alarm is displayed
else {
state = setClockHourState; // if displaying the clock, menu button is held & released, then clock hour can be set
}
}
break;
case alarmState: // State 1
if (advanceMove == -1 && alarmMode <= 0) {
alarmMode = alarmModeMax;
alarmSet = 1;
} else if (advanceMove == 1 && alarmMode >= alarmModeMax) {
alarmMode = 0;
alarmSet = 0;
} else if (advanceMove != 0) {
alarmMode = alarmMode + advanceMove;
if (alarmMode == 0) {
alarmSet = 0;
} else {
alarmSet = 1;
}
}
Serial.print("alarmState is ");
Serial.println(alarmState);
Serial.print("alarmMode is ");
Serial.println(alarmMode);
EEPROM.write(alarmSetAddress, alarmSet);
EEPROM.write(alarmModeAddress, alarmMode);
advanceMove = 0;
alarmTrig = false;
if (menuReleased == true) {
if (menuTimePressed <= holdTime) {
state = countDownState;
j = 0;
} // if displaying the alarm time, menu button is pressed & released, then clock is displayed
else {
state = setAlarmHourState; // if displaying the alarm time, menu button is held & released, then alarm hour can be set
}
}
break;
case setAlarmHourState: // State 2
if (menuReleased == true) {
state = setAlarmMinState;
} else if (advanceMove == 1 && alarmHour >= 23) {
alarmHour = 0;
} else if (advanceMove == -1 && alarmHour <= 0) {
alarmHour = 23;
} else if (advanceMove != 0) {
alarmHour = alarmHour + advanceMove;
}
EEPROM.write(alarmHourAddress, alarmHour);
advanceMove = 0;
break;
case setAlarmMinState: // State 3
if (menuReleased == true) {
state = alarmState;
alarmDay = 0;
newSecTime = millis();
} else if (advanceMove == 1 && alarmMin >= 59) {
alarmMin = 0;
} else if (advanceMove == -1 && alarmMin <= 0) {
alarmMin = 59;
} else if (advanceMove != 0) {
alarmMin = alarmMin + advanceMove;
}
EEPROM.write(alarmMinAddress, alarmMin);
advanceMove = 0;
break;
case setClockHourState: // State 4
if (menuReleased == true) {
state = setClockMinState;
} else if (advanceMove == 1 && now.hour() == 23) {
RTC.adjust(DateTime(now.year(), now.month(), now.day(), 0, now.minute(), now.second()));
advanceMove = 0;
} else if (advanceMove == -1 && now.hour() == 0) {
RTC.adjust(DateTime(now.year(), now.month(), now.day(), 23, now.minute(), now.second()));
advanceMove = 0;
} else if (advanceMove != 0) {
RTC.adjust(DateTime(now.year(), now.month(), now.day(), (now.hour() + advanceMove), now.minute(), now.second()));
advanceMove = 0;
}
break;
case setClockMinState: // State 5
if (menuReleased == true) {
state = setClockSecState;
} else if (advanceMove == 1 && now.minute() == 59) {
RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), 0, now.second()));
advanceMove = 0;
} else if (advanceMove == -1 && now.minute() == 0) {
RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), 59, now.second()));
advanceMove = 0;
} else if (advanceMove != 0) {
RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), (now.minute() + advanceMove), now.second()));
advanceMove = 0;
}
break;
case setClockSecState: // State 6
if (menuReleased == true) {
state = clockState;
} else if (advanceMove == 1 && now.second() == 59) {
RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), 0));
advanceMove = 0;
} else if (advanceMove == -1 && now.second() == 0) {
RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), 59));
advanceMove = 0;
} else if (advanceMove != 0) {
RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), (now.second() + advanceMove)));
advanceMove = 0;
}
break;
case countDownState: // State 7
if (menuReleased == true) {
if (menuTimePressed <= holdTime) {
if (countDown == true && countDownTime <= 0) {
countDown = false;
countDownTime = 0;
currentCountDown = 0;
} else if (countDown == false && countDownTime > 0) {
countDown = true;
startCountDown = now.unixtime();
} else {
state = demoState;
demoIntro = 1;
j = 0;
} // if displaying the count down, menu button is pressed & released, then demo State is displayed
} else {
countDown = false;
countDownTime = 0;
currentCountDown = 0;
j = 0;
} // if displaying the clock, menu button is held & released, then the count down is reset
} else if (advanceMove == -1 && currentCountDown <= 0) {
countDown = false;
countDownTime = 0;
currentCountDown = 0;
demoIntro = 0;
} else if (advanceMove == 1 && currentCountDown >= 3600) {
countDown = false;
countDownTime = 3600;
} else if (advanceMove != 0) //if displaying the count down, rotary encoder is turned then will change accordingley
{
countDown = false;
countDownTime = currentCountDown - currentCountDown % 60 + advanceMove * 60; // This rounds the count down minute up to the next minute
}
advanceMove = 0;
break;
case demoState: // State 8
if (menuReleased == true) {
state = clockState;
displayMode = EEPROM.read(modeAddress);
} // if displaying the demo, menu button pressed then the clock will display and restore to the mode before demo started
break;
}
if (menuReleased || advanceMove != 0) {
countTime = false;
}
Serial.print("Display Mode (");
Serial.print(displayMode);
Serial.println(")");
Serial.print("Clock State (");
Serial.print(state);
Serial.print(") ");
Serial.println(states[state]);
}
void setAlarmDisplay() {
for (int i = 0; i < numLEDs; i++) {
fiveMins = i % 5;
if (fiveMins == 0) {
leds[i].r = 100;
leds[i].g = 100;
leds[i].b = 100;
}
}
if (alarmSet == 0) {
for (int i = 0; i < numLEDs; i++) // Sets background to red, to state that alarm IS NOT set
{
fiveMins = i % 5;
if (fiveMins == 0) {
leds[i].r = 20;
leds[i].g = 0;
leds[i].b = 0;
}
}
} else {
for (int i = 0; i < numLEDs; i++) // Sets background to green, to state that alarm IS set
{
fiveMins = i % 5;
if (fiveMins == 0) {
leds[i].r = 0;
leds[i].g = 20;
leds[i].b = 0;
}
}
}
if (alarmHour <= 11) {
leds[(alarmHour * 5 + LEDOffset) % 60].r = 255 / dimming;
} else {
leds[((alarmHour - 12) * 5 + LEDOffset + 59) % 60].r = 25;
leds[((alarmHour - 12) * 5 + LEDOffset) % 60].r = 255 / dimming;
leds[((alarmHour - 12) * 5 + LEDOffset + 1) % 60].r = 25;
}
leds[(alarmMin + LEDOffset) % 60].g = 100;
flashTime = millis();
if (state == setAlarmHourState && flashTime % 300 >= 150) {
leds[(((alarmHour % 12) * 5) + LEDOffset + 59) % 60].r = 0;
leds[(((alarmHour % 12) * 5) + LEDOffset) % 60].r = 0;
leds[(((alarmHour % 12) * 5) + LEDOffset + 1) % 60].r = 0;
}
if (state == setAlarmMinState && flashTime % 300 >= 150) {
leds[(alarmMin + LEDOffset) % 60].g = 0;
}
leds[(alarmMode + LEDOffset) % 60].b = 255 / dimming;
}
void setClockDisplay(DateTime now) {
for (int i = 0; i < numLEDs; i++) {
fiveMins = i % 5;
if (fiveMins == 0) {
leds[i].r = 10;
leds[i].g = 10;
leds[i].b = 10;
}
}
if (now.hour() <= 11) {
leds[(now.hour() * 5 + LEDOffset) % 60].r = 255 / dimming;
} else {
leds[((now.hour() - 12) * 5 + LEDOffset + 59) % 60].r = 255 / dimming;
leds[((now.hour() - 12) * 5 + LEDOffset) % 60].r = 255 / dimming;
leds[((now.hour() - 12) * 5 + LEDOffset + 1) % 60].r = 255 / dimming;
}
flashTime = millis();
if (state == setClockHourState && flashTime % 300 >= 150) {
leds[(((now.hour() % 12) * 5) + LEDOffset + 59) % 60].r = 0;
leds[((now.hour() % 12) * 5 + LEDOffset) % 60].r = 0;
leds[(((now.hour() % 12) * 5) + LEDOffset + 1) % 60].r = 0;
}
if (state == setClockMinState && flashTime % 300 >= 150) {
leds[(now.minute() + LEDOffset) % 60].g = 0;
} else {
leds[(now.minute() + LEDOffset) % 60].g = 255 / dimming;
}
if (state == setClockSecState && flashTime % 300 >= 150) {
leds[(now.second() + LEDOffset) % 60].b = 0;
} else {
leds[(now.second() + LEDOffset) % 60].b = 255 / dimming;
}
}
// Check if alarm is active and if is it time for the alarm to trigger
void alarm(DateTime now) {
if ((alarmMin == now.minute() % 60) && (alarmHour == now.hour() % 24)) //check if the time is the same to trigger alarm
{
alarmTrig = true;
alarmTrigTime = millis();
}
}
void alarmDisplay() // Displays the alarm
{
switch (alarmMode) {
case 1:
// set all LEDs to a dim white
for (int i = 0; i < numLEDs; i++) {
leds[i].r = 100;
leds[i].g = 100;
leds[i].b = 100;
}
break;
case 2:
LEDPosition = ((millis() - alarmTrigTime) / 300);
reverseLEDPosition = 60 - LEDPosition;
if (LEDPosition >= 0 && LEDPosition <= 29) {
for (int i = 0; i < LEDPosition; i++) {
leds[(i + LEDOffset) % 60].r = 5;
leds[(i + LEDOffset) % 60].g = 5;
leds[(i + LEDOffset) % 60].b = 5;
}
}
if (reverseLEDPosition <= 59 && reverseLEDPosition >= 31) {
for (int i = 59; i > reverseLEDPosition; i--) {
leds[(i + LEDOffset) % 60].r = 5;
leds[(i + LEDOffset) % 60].g = 5;
leds[(i + LEDOffset) % 60].b = 5;
}
}
if (LEDPosition >= 30) {
for (int i = 0; i < numLEDs; i++) {
leds[(i + LEDOffset) % 60].r = 5;
leds[(i + LEDOffset) % 60].g = 5;
leds[(i + LEDOffset) % 60].b = 5;
}
}
break;
case 3:
fadeTime = 60000;
brightFadeRad = (millis() - alarmTrigTime) / fadeTime; // Divided by the time period of the fade up.
if (millis() > alarmTrigTime + fadeTime) LEDBrightness = 255 / dimming;
else LEDBrightness = 255 / dimming * (1.0 + sin((1.57 * brightFadeRad) - 1.57));
Serial.println(brightFadeRad);
Serial.println(LEDBrightness);
for (int i = 0; i < numLEDs; i++) {
leds[i].r = LEDBrightness;
leds[i].g = LEDBrightness;
leds[i].b = LEDBrightness;
}
break;
// Currently not working
// case 4:
// fadeTime = 60000;
// brightFadeRad = (millis() - alarmTrigTime)/fadeTime; // Divided by the time period of the fade up.
// LEDPosition = ((millis() - alarmTrigTime)/(fadeTime/30));
//// if (millis() > alarmTrigTime + fadeTime) LEDBrightness = 255; // If the fade time is complete, then the LED brightness will be set to full.
// if (brightFadeRad <= 0) LEDBrightness = 0;
// else if (brightFadeRad >= 0) LEDBrightness = 1;
// else LEDBrightness = 255.0*(1.0+sin((1.57*brightFadeRad)-1.57));
//
//// Serial.println(brightFadeRad);
//// Serial.println(LEDBrightness);
// reverseLEDPosition = 60 - LEDPosition;
// if (LEDPosition >= 0 && LEDPosition <= 29)
// {
// for (int i = 0; i < LEDPosition; i++)
// {
// leds[i].r = LEDBrightness;
// leds[i].g = LEDBrightness;
// leds[i].b = LEDBrightness;
// }
// }
// if (reverseLEDPosition <= 59 && reverseLEDPosition >= 31)
// {
// for (int i = 59; i > reverseLEDPosition; i--)
// {
// leds[i].r = LEDBrightness;
// leds[i].g = LEDBrightness;
// leds[i].b = LEDBrightness;
// }
// }
// if (LEDPosition >= 30)
// {
// for (int i = 0; i < numLEDs; i++)
// {
// leds[i].r = LEDBrightness;
// leds[i].g = LEDBrightness;
// leds[i].b = LEDBrightness;
// }
// }
// break;
}
}
//
void countDownDisplay(DateTime now) {
flashTime = millis();
if (countDown == true) {
currentCountDown = countDownTime + startCountDown - now.unixtime();
if (currentCountDown > 0) {
countDownMin = currentCountDown / 60;
countDownSec = currentCountDown % 60 * 4; // have multiplied by 4 to create brightness
for (int i = 0; i < countDownMin; i++) {
leds[(i + LEDOffset + 1) % 60].b = 240; // Set a blue LED for each complete minute that is remaining
}
leds[(countDownMin + LEDOffset + 1) % 60].b = countDownSec; // Display the remaining secconds of the current minute as its brightness
} else {
countDownFlash = now.unixtime() % 2;
if (countDownFlash == 0) {
for (int i = 0; i < numLEDs; i++) // Set the background as all off
{
leds[i].r = 0;
leds[i].g = 0;
leds[i].b = 0;
}
} else {
for (int i = 0; i < numLEDs; i++) // Set the background as all blue
{
leds[i].r = 0;
leds[i].g = 0;
leds[i].b = 255 / dimming;
}
}
}
} else {
currentCountDown = countDownTime;
if (countDownTime == 0) {
currentMillis = millis();
clearLEDs();
switch (demoIntro) {
case 0:
for (int i = 0; i < j; i++) {
leds[(i + LEDOffset + 1) % 60].b = 20;
}
if (currentMillis - previousMillis > timeInterval) {
j++;
previousMillis = currentMillis;
}
if (j == numLEDs) {
demoIntro = 1;
}
break;
case 1:
for (int i = 0; i < j; i++) {
leds[(i + LEDOffset + 1) % 60].b = 20;
}
if (currentMillis - previousMillis > timeInterval) {
j--;
previousMillis = currentMillis;
}
if (j < 0) {
demoIntro = 0;
}
break;
}
} else if (countDownTime > 0 && flashTime % 300 >= 150) {
countDownMin = currentCountDown / 60; //
for (int i = 0; i < countDownMin; i++) {
leds[(i + LEDOffset + 1) % 60].b = 255 / dimming; // Set a blue LED for each complete minute that is remaining
}
}
}
}
void runDemo(DateTime now) {
currentDemoTime = now.unixtime();
currentMillis = millis();
clearLEDs();
switch (demoIntro) {
case 0:
timeDisplay(now);
if (currentDemoTime - previousDemoTime > demoTime) {
previousDemoTime = currentDemoTime;
displayMode++;
}
break;
case 1:
for (int i = 0; i < j; i++) {
leds[i].r = 255 / dimming;
}
if (currentMillis - previousMillis > timeInterval) {
j++;
previousMillis = currentMillis;
}
if (j == numLEDs) {
j = 0;
demoIntro++;
}
break;
case 2:
for (int i = j; i < numLEDs; i++) {
leds[i].r = 255 / dimming;
}
if (currentMillis - previousMillis > timeInterval) {
j++;
previousMillis = currentMillis;
}
if (j == numLEDs) {
j = 0;
demoIntro++;
}
break;
case 3:
for (int i = 0; i < j; i++) {
leds[i].g = 255 / dimming;
}
if (currentMillis - previousMillis > timeInterval) {
j++;
previousMillis = currentMillis;
}
if (j == numLEDs) {
j = 0;
demoIntro++;
}
break;
case 4:
for (int i = j; i < numLEDs; i++) {
leds[i].g = 255 / dimming;
}
if (currentMillis - previousMillis > timeInterval) {
j++;
previousMillis = currentMillis;
}
if (j == numLEDs) {
j = 0;
demoIntro++;
}
break;
case 5:
for (int i = 0; i < j; i++) {
leds[i].b = 255 / dimming;
}
if (currentMillis - previousMillis > timeInterval) {
j++;
previousMillis = currentMillis;
}
if (j == numLEDs) {
j = 0;
demoIntro++;
}
break;
case 6:
for (int i = j; i < numLEDs; i++) {
leds[i].b = 255 / dimming;
}
if (currentMillis - previousMillis > timeInterval) {
j++;
previousMillis = currentMillis;
}
if (j == numLEDs) {
j = 0;
demoIntro++;
}
break;
case 7:
for (int i = 0; i < j; i++) {
leds[i].r = 255 / dimming;
leds[i].g = 255 / dimming;
leds[i].b = 255 / dimming;
}
if (currentMillis - previousMillis > timeInterval) {
j++;
previousMillis = currentMillis;
}
if (j == numLEDs) {
j = 0;
demoIntro++;
}
break;
case 8:
for (int i = j; i < numLEDs; i++) {
leds[i].r = 255 / dimming;
leds[i].g = 255 / dimming;
leds[i].b = 255 / dimming;
}
if (currentMillis - previousMillis > timeInterval) {
j++;
previousMillis = currentMillis;
}
if (j == numLEDs) {
demoIntro = 0;
displayMode = 0;
Serial.print("Mode is ");
Serial.println(displayMode);
Serial.print("State is ");
Serial.println(state);
}
break;
}
}
void clearLEDs() {
for (int i = 0; i < numLEDs; i++) // Set all the LEDs to off
{
leds[i].r = 0;
leds[i].g = 0;
leds[i].b = 0;
}
}
void timeDisplay(DateTime now) {
switch (displayMode) {
case 0:
minimalClock(now);
break;
case 1:
basicClock(now);
break;
case 2:
smoothSecond(now);
break;
case 3:
outlineClock(now);
break;
case 4:
minimalMilliSec(now);
break;
case 5:
simplePendulum(now);
break;
case 6:
breathingClock(now);
break;
default: // Keep this here and add more timeDisplay modes as defined cases.
{
displayMode = 0;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////
// CLOCK DISPLAY MODES
// Add any new display mode functions here. Then add to the "void timeDisplay(DateTime now)" function.
// Add each of the new display mode functions as a new "case", leaving default last.
////////////////////////////////////////////////////////////////////////////////////////////
//
void minimalClock(DateTime now) {
unsigned char hourPos = (now.hour() % 12) * 5;
leds[(hourPos + LEDOffset) % 60].r = 255 / dimming;
leds[(now.minute() + LEDOffset) % 60].g = 255 / dimming;
leds[(now.second() + LEDOffset) % 60].b = 255 / dimming;
}
//
void basicClock(DateTime now) {
unsigned char hourPos = ((now.hour() % 12) * 5 + (now.minute() + 6) / 12);
leds[(hourPos + LEDOffset + 59) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset + 59) % 60].g = 0;
leds[(hourPos + LEDOffset + 59) % 60].b = 0;
leds[(hourPos + LEDOffset) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset) % 60].g = 0;
leds[(hourPos + LEDOffset) % 60].b = 0;
leds[(hourPos + LEDOffset + 1) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset + 1) % 60].g = 0;
leds[(hourPos + LEDOffset + 1) % 60].b = 0;
leds[(now.minute() + LEDOffset) % 60].r = 0;
leds[(now.minute() + LEDOffset) % 60].g = 255 / dimming;
leds[(now.minute() + LEDOffset) % 60].b = 0;
leds[(now.second() + LEDOffset) % 60].r = 0;
leds[(now.second() + LEDOffset) % 60].g = 0;
leds[(now.second() + LEDOffset) % 60].b = 255 / dimming;
}
//
void smoothSecond(DateTime now) {
if (now.second() != old.second()) {
old = now;
cyclesPerSec = millis() - newSecTime;
cyclesPerSecFloat = (float)cyclesPerSec;
newSecTime = millis();
}
// set hour, min & sec LEDs
fracOfSec = (millis() - newSecTime) / cyclesPerSecFloat; // This divides by 733, but should be 1000 and not sure why???
if (subSeconds < cyclesPerSec) {
secondBrightness = 50.0 * (1.0 + sin((3.14 * fracOfSec) - 1.57));
}
if (subSeconds < cyclesPerSec) {
secondBrightness2 = 50.0 * (1.0 + sin((3.14 * fracOfSec) + 1.57));
}
unsigned char hourPos = ((now.hour() % 12) * 5 + (now.minute() + 6) / 12);
// The colours are set last, so if on same LED mixed colours are created
leds[(hourPos + LEDOffset + 59) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset + 1) % 60].r = 255 / dimming;
leds[(now.minute() + LEDOffset) % 60].g = 255 / dimming;
leds[(now.second() + LEDOffset) % 60].b = secondBrightness;
leds[(now.second() + LEDOffset + 59) % 60].b = secondBrightness2;
}
//
void outlineClock(DateTime now) {
for (int i = 0; i < numLEDs; i++) {
fiveMins = i % 5;
if (fiveMins == 0) {
leds[i].r = 100;
leds[i].g = 100;
leds[i].b = 100;
}
}
unsigned char hourPos = ((now.hour() % 12) * 5 + (now.minute() + 6) / 12);
leds[(hourPos + LEDOffset + 59) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset + 1) % 60].r = 255 / dimming;
leds[(now.minute() + LEDOffset) % 60].g = 255 / dimming;
leds[(now.second() + LEDOffset) % 60].b = 255 / dimming;
}
//
void minimalMilliSec(DateTime now) {
if (now.second() != old.second()) {
old = now;
cyclesPerSec = (millis() - newSecTime);
newSecTime = millis();
}
// set hour, min & sec LEDs
unsigned char hourPos = ((now.hour() % 12) * 5 + (now.minute() + 6) / 12);
subSeconds = (((millis() - newSecTime) * 60) / cyclesPerSec) % 60; // This divides by 733, but should be 1000 and not sure why???
// Millisec lights are set first, so hour/min/sec lights override and don't flicker as millisec passes
leds[(subSeconds + LEDOffset) % 60].r = 50;
leds[(subSeconds + LEDOffset) % 60].g = 50;
leds[(subSeconds + LEDOffset) % 60].b = 50;
// The colours are set last, so if on same LED mixed colours are created
leds[(hourPos + LEDOffset + 59) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset + 1) % 60].r = 255 / dimming;
leds[(now.minute() + LEDOffset) % 60].g = 255 / dimming;
leds[(now.second() + LEDOffset) % 60].b = 255 / dimming;
}
// Pendulum will be at the bottom and left for one second and right for one second
void simplePendulum(DateTime now) {
if (now.second() != old.second()) {
old = now;
cyclesPerSec = millis() - newSecTime;
cyclesPerSecFloat = (float)cyclesPerSec;
newSecTime = millis();
if (swingBack == true) {
swingBack = false;
} else {
swingBack = true;
}
}
// set hour, min & sec LEDs
fracOfSec = (millis() - newSecTime) / cyclesPerSecFloat; // This divides by 733, but should be 1000 and not sure why???
if (subSeconds < cyclesPerSec && swingBack == true) {
pendulumPos = 27.0 + 3.4 * (1.0 + sin((3.14 * fracOfSec) - 1.57));
}
if (subSeconds < cyclesPerSec && swingBack == false) {
pendulumPos = 27.0 + 3.4 * (1.0 + sin((3.14 * fracOfSec) + 1.57));
}
unsigned char hourPos = ((now.hour() % 12) * 5 + (now.minute() + 6) / 12);
// Pendulum lights are set first, so hour/min/sec lights override and don't flicker as millisec passes
leds[(pendulumPos + LEDOffset) % 60].r = 100;
leds[(pendulumPos + LEDOffset) % 60].g = 100;
leds[(pendulumPos + LEDOffset) % 60].b = 100;
// The colours are set last, so if on same LED mixed colours are created
leds[(hourPos + LEDOffset + 59) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset + 1) % 60].r = 255 / dimming;
leds[(now.minute() + LEDOffset) % 60].g = 255 / dimming;
leds[(now.second() + LEDOffset) % 60].b = 255 / dimming;
}
void breathingClock(DateTime now) {
if (alarmTrig == false) {
breathBrightness = 15.0 * (1.0 + sin((3.14 * millis() / 2000.0) - 1.57));
for (int i = 0; i < numLEDs; i++) {
fiveMins = i % 5;
if (fiveMins == 0) {
leds[i].r = breathBrightness + 5;
leds[i].g = breathBrightness + 5;
leds[i].b = breathBrightness + 5;
} else {
leds[i].r = 0;
leds[i].g = 0;
leds[i].b = 0;
}
}
}
unsigned char hourPos = ((now.hour() % 12) * 5 + (now.minute() + 6) / 12);
leds[(hourPos + LEDOffset + 59) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset) % 60].r = 255 / dimming;
leds[(hourPos + LEDOffset + 1) % 60].r = 255 / dimming;
leds[(now.minute() + LEDOffset) % 60].g = 255 / dimming;
leds[(now.second() + LEDOffset) % 60].b = 255 / dimming;
}
/*
// Cycle through the color wheel, equally spaced around the belt
void rainbowCycle(uint8_t wait)
{
uint16_t i, j;
for (j=0; j < 384 * 5; j++)
{ // 5 cycles of all 384 colors in the wheel
for (i=0; i < numLEDs; i++)
{
// tricky math! we use each pixel as a fraction of the full 384-color
// wheel (thats the i / strip.numPixels() part)
// Then add in j which makes the colors go around per pixel
// the % 384 is to make the wheel cycle around
strip.setPixelColor(i, Wheel(((i * 384 / numLEDs) + j) % 384));
}
delay(wait);
}
}
//Input a value 0 to 384 to get a color value.
//The colours are a transition r - g - b - back to r
uint32_t Wheel(uint16_t WheelPos)
{
byte r, g, b;
switch(WheelPos / 128)
{
case 0:
r = 127 - WheelPos % 128; // red down
g = WheelPos % 128; // green up
b = 0; // blue off
break;
case 1:
g = 127 - WheelPos % 128; // green down
b = WheelPos % 128; // blue up
r = 0; // red off
break;
case 2:
b = 127 - WheelPos % 128; // blue down
r = WheelPos % 128; // red up
g = 0; // green off
break;
}
return(strip.Color(r,g,b));
}
*/