Amazing Colorful RGB LED Clock

Hello friends, this is RGB LED clock. You will get the time in 12Hr format and changing color of number automatically. In this project we used WS2812B LED strip which is available in online market easily. Also you can change the time from key. Here you will get five no of key to set time and color pattern. If you want to make this then just follow all the step and make your own RGB LED clock.

Diagram :

Componants

Arduino Nano : https://amzn.to/37Qhz0e
WS2811 LED Chain : https://amzn.to/3kCFXWy
MT3608 Module : https://amzn.to/3uyHfXC
DC Socket : https://amzn.to/2HLNYtS
DS3231 Module :
PVC Sheet :

Code :

#include <Adafruit_NeoPixel.h>
#include <math.h>
#include <Wire.h>
#include "RTClib.h"
#include <Encoder.h>

#ifdef __AVR__
  #include <avr/power.h>
#endif

#define PIN 9

#define SEGMENTS_PER_DIGIT 7
#define LEDS_PER_SEGMENT 1
#define LEDS_PER_DIGIT 7
#define GRID_WIDTH 13
#define GRID_HEIGHT 5

#define NUMPIXELS 30


RTC_DS3231 rtc;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int mode = 0;
bool justEnteredMode = true;

double timeOfLastInput = 0;
double timeOfLastRefresh = 0;
int inactivityTimeoutDuration = 10000;

uint32_t startColor,endColor;

uint16_t startHue = 0;
uint16_t endHue = 80;

int brightness = 50;

int inputHours = 1;
int inputMinutes = 0;

int inputColor = 0;
int inputColorShift = 12;
int inputColorDirection = 0;

int analogInput = 0;
bool upIsPressed, rightIsPressed, downIsPressed, leftIsPressed, centerIsPressed = false;
int upHeldCycles, rightHeldCycles, downHeldCycles, leftHeldCycles, centerHeldCycles = 0;

// Defines the light number for each light by its position in a grid covering the whole clock face
// 0 indicates positions where no light exists

 const int PROGMEM pixelNumber[5][13] = 
{
  {0,2,0,   0, 9, 0,    0,   0,18, 0,   0,25,0},
  {1,0,3,   8, 0,10,   15,   17,0,19,   24,0,26},
  {0,7,0,   0,14, 0,    0,   0,23, 0,   0,30,0},
  {6,0,4,   13,0,11,   16,   22,0,20,   29,0,27},
  {0,5,0,   0,12, 0,    0,   0,21, 0,   0,28,0},
};

const int pixelValues[][7] = 
{
  {1,1,1,1,1,1,0}, // Number 0
  {0,0,1,1,0,0,0}, // Number 1
  {0,1,1,0,1,1,1}, // Number 2
  {0,1,1,1,1,0,1}, // Number 3
  {1,0,1,1,0,0,1}, // Number 4
  {1,1,0,1,1,0,1}, // Number 5
  {1,1,0,1,1,1,1}, // Number 6
  {0,1,1,1,0,0,0}, // Number 7
  {1,1,1,1,1,1,1}, // Number 8
  {1,1,1,1,1,0,1}, // Number 9
  {0,0,0,0,0,0,0}  // Digit off
};


void setup () 
{
  Serial.begin(9600);

  if (! rtc.begin()) 
  {
    Serial.println("Couldn't find RTC");
    while (1);
  }
    
  if (rtc.lostPower()) 
  {
    Serial.println("RTC lost power, lets set the time!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }

  // Uncomment below to set time to compile time.
  // THIS WILL KEEP RESETTING THE TIME EVERY BOOT UNTIL CODE WITH THIS DISABLED IS UPLOADED
  //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  strip.begin(); // This initializes the NeoPixel library.

  pinMode(A2, INPUT);
  pinMode(13,OUTPUT);
  digitalWrite(13, HIGH);
   pinMode(12,OUTPUT);
  digitalWrite(12, LOW);
}

void loop () 
{
  analogInput = analogRead(A2);
   Serial.println(analogInput);
   delay(100);

  if (upIsPressed){
    upHeldCycles++;
  }
  else{
    upHeldCycles = 0;}
    
  if (rightIsPressed){
    rightHeldCycles++;}
  else{
    rightHeldCycles = 0;}
    
  if (downIsPressed){
    downHeldCycles++;}
  else{
    downHeldCycles = 0;}
    
  if (leftIsPressed){
    leftHeldCycles++;}
  else{
    leftHeldCycles = 0;}

  if (centerIsPressed){
    centerHeldCycles++;}
  else{
    centerHeldCycles = 0;}
  

  upIsPressed = rightIsPressed = downIsPressed = leftIsPressed = centerIsPressed = false;

  
  // Set isPressedValues
  if (analogInput > 130 && analogInput < 190)
  {
      upIsPressed = true;
      timeOfLastInput = millis();
      Serial.print("up ");
      Serial.println(upHeldCycles);
  }
  else if (analogInput > 190 && analogInput < 220)
  {
      rightIsPressed = true;
      timeOfLastInput = millis();
      Serial.print("right ");
      Serial.println(rightHeldCycles);
  }
  else if (analogInput > 230 && analogInput < 270)
  {
      downIsPressed = true;
      timeOfLastInput = millis();
      Serial.print("down ");
      Serial.println(downHeldCycles);
  }
  else if (analogInput > 280 && analogInput < 370)
  {
      leftIsPressed = true;
      timeOfLastInput = millis();
      Serial.print("left ");
      Serial.println(leftHeldCycles);
  }
  else if (analogInput > 380 && analogInput < 600)
  {
      centerIsPressed = true;
      timeOfLastInput = millis();
      Serial.print("center ");
      Serial.println(centerHeldCycles);
  }

  
  if (leftHeldCycles == 1)
  {
    mode--;
    justEnteredMode = true;
  }

  if (rightHeldCycles == 1) 
  {
    mode++;
    justEnteredMode = true;
  }

  int delayNum = 500;

  DateTime now = rtc.now();
  

  int convertedHour;

  if (now.hour() > 12) 
  {
    convertedHour = now.hour() - 12;
  }
  else 
  {
    convertedHour = now.hour();
  }

  int num0 = (convertedHour /10) % 10;
  int num1 = convertedHour % 10;
  int num2 = (now.minute() /10) % 10;
  int num3 = now.minute() % 10;

  // Time display and brightness adjust mode
  if (mode == 0) 
  {
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 0, display time");
      Serial.print(num0);
      Serial.print(num1);
      Serial.print(":");
      Serial.print(num2);
      Serial.println(num3);
    }
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&brightness, 1, 1, 100);
      Serial.print("Brightness raised to ");
      Serial.println(brightness);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&brightness, -1, 1, 100);
      Serial.print("Brightness lowered to ");
      Serial.println(brightness);
    }

    updateDigits(num0,num1,num2,num3);

    inputColor += 5;
  }

  // Color set mode
  else if (mode == 1) 
  {
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 1, set color 1");
      updateDigits(1, 10, 10, 10);
      delay(300);
    }
    
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&inputColor, -5, 0, 360);
      Serial.print("Input color raised to ");
      Serial.println(inputColor);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&inputColor, 5, 0, 360);
      Serial.print("Input color lowered to ");
      Serial.println(inputColor);
    }
    
    resetModeAfterInactivity();

    updateDigits(8, 8, 8, 8);
  }

  
  // Color shift amount set mode
  else if (mode == 2) 
  {
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 2, set color shift amount");
      updateDigits(2, 10, 10, 10);
      delay(300);
    }
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&inputColorShift, -1, -30, 30);
      Serial.print("Color shift amount raised to ");
      Serial.println(inputColorShift);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&inputColorShift, 1, -30, 30);
      Serial.print("Color shift amount lowered to ");
      Serial.println(inputColorShift);
    }
    
    resetModeAfterInactivity();
        
    updateDigits(8, 8, 8, 8);
  }  

  
  // Gradient direction set mode
  else if (mode == 3) 
  {
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 3, set gradient direction");
      updateDigits(3, 10, 10, 10);
      delay(300);
    }
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&inputColorDirection, -1, 0, 2);
      Serial.print("Input color direction set to ");
      Serial.println(inputColorDirection);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&inputColorDirection, 1, 0, 2);
      Serial.print("Input color direction set to ");
      Serial.println(inputColorDirection);
    }

    resetModeAfterInactivity();

    updateDigits(8, 8, 8, 8);
  }


  // Hour set mode
  else if (mode == 4) 
  {
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 4, set hour");
    }
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&inputHours, 1, 1, 12);
      Serial.print("Hours raised to ");
      Serial.println(inputHours);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&inputHours, -1, 1, 12);
      Serial.print("Hours lowered to ");
      Serial.println(inputHours);
    }    

    int digitOne = (millis() % 1000 < 800) ? (inputHours / 10) % 10 : 10;
    int digitTwo = (millis() % 1000 < 800) ? inputHours % 10 : 10;
    
    resetModeAfterInactivity();

    updateDigits(digitOne, digitTwo, num2, num3);

    rtc.adjust(DateTime(now.year(), now.month(), now.day(), inputHours, now.minute(), 0));
  }

  // Minute set mode
  else if (mode == 5) 
  {
    inputMinutes = now.minute();
    
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 6, set minutes");
    }
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&inputMinutes, 1, 0, 59);
      Serial.print("Minutes raised to ");
      Serial.println(inputMinutes);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&inputMinutes, -1, 0, 59);
      Serial.print("Minutes lowered to ");
      Serial.println(inputMinutes);
    }        
    
    resetModeAfterInactivity();

    int digitThree = (millis() % 1000 < 800) ? floor(inputMinutes/10) : 10;
    int digitFour = (millis() % 1000 < 800) ? (inputMinutes % 10) : 10;

    //int digitThree = floor(inputMinutes / 10);
    //int digitFour = inputMinutes % 10;
    
    updateDigits(num0, num1, digitThree, digitFour);

    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), inputMinutes, 0));
  }
  
  else 
  {
    mode = 0;
  }
  
}


void resetModeAfterInactivity() 
{
  if (timeOfLastInput + inactivityTimeoutDuration < millis()) 
  {
    Serial.print("Time of last input: ");
    Serial.print(timeOfLastInput);
    Serial.print(". Current milis: ");
    Serial.println(millis());
    mode = 0;
  }
}

void updateDigits(int num0, int num1, int num2, int num3) 
{
  if (timeOfLastRefresh + 100 < millis()) 
  {
    timeOfLastRefresh = millis();
    
    // Set the segments
    for (int v=0; v<GRID_HEIGHT; v++) 
    {
      for (int h=0; h<GRID_WIDTH; h++) 
      {
        // Subtract one from the value we retrieve because
        // we're starting at 1 in the array
        // but at 0 on strip.setPixelColor
        int currentPixelNumber = pgm_read_word_near(&(pixelNumber[v][h])) - 1;
    
        if (currentPixelNumber >= 0) 
        {
          // There is a pixel here, see if it should be lit
          int digitOffset = 0;
  
          // Track which number we want to draw to the current digit position
          int numberToDraw;
  
          // Track if we're on a divider pixel
          bool divider = false;
          
          // First digit
          if (h >= 0 && h < LEDS_PER_SEGMENT + 2) 
          {
            digitOffset = 0;
            numberToDraw = num0;
            if (numberToDraw == 0) 
            {
              numberToDraw = 10;
            }
          }
  
          // Second digit
          if (h >=LEDS_PER_SEGMENT + 2 && h < (LEDS_PER_SEGMENT * 2) + 4) 
          {
            digitOffset = LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT;
            numberToDraw = num1;
          }
  
          // Divider
          if (h == (LEDS_PER_SEGMENT * 2) + 4) 
          {
            digitOffset = (LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT) * 2;
            divider = true;
          }
  
          if (h >= (LEDS_PER_SEGMENT * 2) + 5 && h <= (LEDS_PER_SEGMENT * 3) + 6) 
          {
            digitOffset = (LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT) * 2 + 2;
            numberToDraw = num2;
          }
  
          if (h >= (LEDS_PER_SEGMENT * 3) + 7 && h <= (LEDS_PER_SEGMENT * 4) + 8) 
          {
            digitOffset = (LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT) * 3 + 2;
            numberToDraw = num3;
          }
          
          if (pixelValues[numberToDraw][currentPixelNumber-digitOffset] == 1 || divider == true) 
          {   
            if (inputColorDirection == 0) 
            {
              uint32_t color = HSV_to_RGB(inputColor + h*inputColorShift,100,brightness);
              strip.setPixelColor(currentPixelNumber, color); 
            }
            else if (inputColorDirection == 1) 
            {
              uint32_t color = HSV_to_RGB(inputColor + v*inputColorShift,100,brightness);
              strip.setPixelColor(currentPixelNumber, color); 
            }
            else 
            {
              uint32_t color = HSV_to_RGB(inputColor,100,brightness);
              strip.setPixelColor(currentPixelNumber, color); 
            }
          }
          else 
          {
            uint32_t color = strip.Color(0,0,0);
            strip.setPixelColor(currentPixelNumber, color);
          }
          
          strip.show();
        }
      }
  
    }
  
  }
    
}


void adjustVariable(int *variable, int change, int minNum, int maxNum) 
{
  *variable += change;
    
  if (*variable < minNum) 
    *variable = maxNum;

  if (*variable > maxNum)
    *variable = minNum;
}


uint32_t HSV_to_RGB(float h, float s, float v) 
{
  int i;
  float f,p,q,t;
  uint8_t r,g,b;

  while (h >= 360) 
  {
    h = h - 360;
  }
  while (h < 0) 
  {
    h = h + 360;
  }
  
  h = max(0.0, min(360.0, h));
  s = max(0.0, min(100.0, s));
  v = max(0.0, min(100.0, v));
  
  s /= 100;
  v /= 100;
  
  if (s == 0) 
  {
    // Achromatic (grey)
    r = g = b = round(v*255);
    return;
  }

  h /= 60; // sector 0 to 5
  i = floor(h);
  f = h - i; // factorial part of h
  p = v * (1 - s);
  q = v * (1 - s * f);
  t = v * (1 - s * (1 - f));
    
  switch(i)
  {
    case 0:
      r = round(255*v);
      g = round(255*t);
      b = round(255*p);
      break;
    case 1:
      r = round(255*q);
      g = round(255*v);
      b = round(255*p);
      break;
    case 2:
      r = round(255*p);
      g = round(255*v);
      b = round(255*t);
      break;
    case 3:
      r = round(255*p);
      g = round(255*q);
      b = round(255*v);
      break;
    case 4:
      r = round(255*t);
      g = round(255*p);
      b = round(255*v);
      break;
    default: // case 5:
      r = round(255*v);
      g = round(255*p);
      b = round(255*q);
  }

  return strip.Color(r,g,b);  
};