Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
|
This time I will show you how to build a nice-looking ring clock. The clock uses a WS2812 ring containing 60 Leds, (4 Quarter circle neopixels x 15 Leds) and it is ideal for this purpose. This is an analog-style digital clock with multiple display states, a 24-hour alarm, a count down alarm, multiple alarm display states, and a demo mode. Hours, minutes, and seconds are represented by a different color of the corresponding LED.
The ring holder is made with a 3D printer and you can download the.stl file below. The physical housing for the desktop clock is based on the dimensions of the neopixel ring. The clock would need to be powered by a Power supply of 5V/3A or more. The code is taken from the Open Source project "Rise and Shine LED Clock". I made only a small hardware modification to change the light intensity of the LEDs.
The device is very simple to make and contains only a few components:
- 60 Leds WS2812 ring, made of Four Quarter circle neopixels with 15 Leds
- Arduino nano microcontroller
- Rotary encoder with push button
- Two 3A rectifier diodes
- and one switch
After switching on, (with 3 short presses) we enter to clock face changing mode. Moving the rotary encoder left and right changes the faces of the clock: There are 6 different faces.
- Standard clock with 3 dots red for hours, green minutes, blue for seconds
- Breathing effect
- with 12 white leds on every hour
- with pendulum
- Graduallu seconds changing
- 60 minutes Timer
To set the correct time, hold down the button for more than two seconds. The flashing light indicates the setting mode in hours, minutes, and seconds. Next pressing the button we enter the display of the alarm. Now with a long press of the button we enter the Alarm set mode. The next press is followed by countdown mode. In this mode, the selected time is counted, which can range from 1 to 60 minutes.
And finally comes the Demo mode. In demo mode, the clock faces change one by one at a certain time.
// Add options for background settings
// TO DO: Clean up and comment code...
// BUG: demo state should start as mode 0 after intro
// NICE TO HAVE: When alarm is cancelled, the alarm still remains set for next day.
//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 ee 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
// 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;
#define holdTime 1500
int cyclesPerSec;
float cyclesPerSecFloat; // So can be used as a float in calcs
float fracOfSec;
float breathFracOfSec;
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
#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; // 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<WS2811, 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
mode = 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("Mode is ");Serial.println(mode);
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);
// 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("Hour time is... ");
Serial.println(now.hour());
Serial.print("Min time is... ");
Serial.println(now.minute());
Serial.print("Sec time is... ");
Serial.println(now.second());
Serial.print("Year is... ");
Serial.println(now.year());
Serial.print("Month is... ");
Serial.println(now.month());
Serial.print("Day is... ");
Serial.println(now.day());
}
void loop()
{
DateTime now = RTC.now(); // Fetches the time from RTC
// 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)
{
if (menuBouncer.fallingEdge()) // Checks if a button is pressed, if so sets countTime to true
{
countTime = true;
Serial.println("rising edge");
}
if (menuBouncer.risingEdge()) // Checks if a button is released,
{
countTime = false;
Serial.println("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();
if (menuPressed == true) {Serial.println("Menu Button Pressed");}
if (menuReleased == true) {Serial.println("Menu Button Released");}
Serial.print("Menu Bounce Duration ");
Serial.println(menuTimePressed);
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 && mode == 0)
{
mode = modeMax;
advanceMove = 0;
}
else if(advanceMove != 0) //if displaying the clock, advance button is pressed & released, then mode will change
{
mode = mode + advanceMove;
EEPROM.write(modeAddress,mode);
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; mode = 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("Mode is ");
Serial.println(mode);
Serial.print("State is ");
Serial.println(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;
}
else
{
leds[((alarmHour - 12)*5+LEDOffset+59)%60].r = 25;
leds[((alarmHour - 12)*5+LEDOffset)%60].r = 255;
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;
}
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;}
else
{
leds[((now.hour() - 12)*5+LEDOffset+59)%60].r = 255;
leds[((now.hour() - 12)*5+LEDOffset)%60].r = 255;
leds[((now.hour() - 12)*5+LEDOffset+1)%60].r = 255;
}
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;}
if (state == setClockSecState && flashTime%300 >= 150) {leds[(now.second()+LEDOffset)%60].b = 0;}
else {leds[(now.second()+LEDOffset)%60].b = 255;}
}
// 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;
else LEDBrightness = 255.0*(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;
}
}
}
}
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;} // 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; mode++;}
break;
case 1:
for (int i = 0; i < j; i++) {leds[i].r = 255;}
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;}
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;}
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;}
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;}
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;}
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; leds[i].g = 255; leds[i].b = 255;}
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; leds[i].g = 255; leds[i].b = 255;}
if (currentMillis - previousMillis > timeInterval) {j++; previousMillis = currentMillis;}
if (j == numLEDs)
{
demoIntro = 0;
mode = 0;
Serial.print("Mode is ");
Serial.println(mode);
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 (mode)
{
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.
{
mode = 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;
leds[(now.minute()+LEDOffset)%60].g = 255;
leds[(now.second()+LEDOffset)%60].b = 255;
}
//
void basicClock(DateTime now)
{
unsigned char hourPos = ((now.hour()%12)*5 + (now.minute()+6)/12);
leds[(hourPos+LEDOffset+59)%60].r = 255;
leds[(hourPos+LEDOffset+59)%60].g = 0;
leds[(hourPos+LEDOffset+59)%60].b = 0;
leds[(hourPos+LEDOffset)%60].r = 255;
leds[(hourPos+LEDOffset)%60].g = 0;
leds[(hourPos+LEDOffset)%60].b = 0;
leds[(hourPos+LEDOffset+1)%60].r = 255;
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;
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;
}
//
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;
leds[(hourPos+LEDOffset)%60].r = 255;
leds[(hourPos+LEDOffset+1)%60].r = 255;
leds[(now.minute()+LEDOffset)%60].g = 255;
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;
leds[(hourPos+LEDOffset)%60].r = 255;
leds[(hourPos+LEDOffset+1)%60].r = 255;
leds[(now.minute()+LEDOffset)%60].g = 255;
leds[(now.second()+LEDOffset)%60].b = 255;
}
//
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;
leds[(hourPos+LEDOffset)%60].r = 255;
leds[(hourPos+LEDOffset+1)%60].r = 255;
leds[(now.minute()+LEDOffset)%60].g = 255;
leds[(now.second()+LEDOffset)%60].b = 255;
}
// 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;
leds[(hourPos+LEDOffset)%60].r = 255;
leds[(hourPos+LEDOffset+1)%60].r = 255;
leds[(now.minute()+LEDOffset)%60].g = 255;
leds[(now.second()+LEDOffset)%60].b = 255;
}
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;
leds[(hourPos+LEDOffset)%60].r = 255;
leds[(hourPos+LEDOffset+1)%60].r = 255;
leds[(now.minute()+LEDOffset)%60].g = 255;
leds[(now.second()+LEDOffset)%60].b = 255;
}
/*
// 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));
}
*/
Comments