Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Mirko Pavleski
Published © GPL3+

DIY WS2812 Analog Style Arduino Ring Clock

This is an analog-style digital clock with multiple display states, a 24-hour alarm, a countdown alarm, multiple alarm display states...

BeginnerFull instructions provided15,050
DIY WS2812 Analog Style Arduino Ring Clock

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
NeoPixel Ring: WS2812 5050 RGB LED
Adafruit NeoPixel Ring: WS2812 5050 RGB LED
×1
DS3231M - ±5ppm, I2C Real-Time Clock
Maxim Integrated DS3231M - ±5ppm, I2C Real-Time Clock
×1
Rotary Encoder with Push-Button
Rotary Encoder with Push-Button
×1
Resistor 220 ohm
Resistor 220 ohm
×1
1N4007 – High Voltage, High Current Rated Diode
1N4007 – High Voltage, High Current Rated Diode
×2
Slide Switch
Slide Switch
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Custom parts and enclosures

3Dprinted ring

Schematics

Schematic

Code

Code

C/C++
// 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));
}
*/

Credits

Mirko Pavleski

Mirko Pavleski

154 projects • 1301 followers

Comments