Introduction: Cryptic Wall Clock

About: My name is Troy. I'm a Mechatronics and Aerospace Engineer. I make things out of wood and electronics and spend time outdoors (especially SCUBA diving).

The passage of time is something we cannot control. It happens at the same rate if we are sleeping, awake, bored, or engaged. With current events, it's important to remember time will pass. While we are waiting for time to pass, why not make something that makes the passage of time more appealing to watch.

This clock was inspired by the Mengenlehreuhr found in Berlin, Germany and can be read in the same manner. Just like the original, it tells the time by means of illuminated, colored fields.

It features 96 LED's that light up 52 'digit' regions. Unlike the original, it features a circular design that includes a seconds ring, instead of a horizontal bar layout. The outer band indicates seconds in conjunction with the middle dot, the next two bands indicate minutes, with the final inner bands indicating hours.

If you have some scrap material and extra time on your hands, why not use this time to make something that will show it!

There are a few changes I would make to this project if I were to make it again. First, I would paint the frame and LED board white instead of black. This would reflect more light through the large lens in the front. I would also wait until the end to insert the LED's. I needed the board to be finished earlier so it could help me with writing the code. With that out of the way, let's first learn how to read it!

Step 1: How to Read the Clock

The clock is read from the inner circles to the outer. The inner ring of four fields denote five full hours each, alongside the second ring, also of four fields, which denote one full hour each, displaying the hour value in 24-hour format. The third ring consists of eleven fields, which denote five full minutes each, the next ring has another four fields, which mark one full minute each. Finally the outer ring of 29 fields denote even seconds with the light in the center blinking to denote odd (when lit) or even-numbered (when unlit) seconds.

For example, the above image has 1 of the five hour digits, 3 of the one hour digits, 8 of the five minute digits, 4 of the one minute digits, and 23 of the two second digits and the middle second digit lit up.

1x5 + 3x1 : 8x5 + 4x1 : 23x2 + 1x1 = 8:44:47 = 8:44:47 AM

The time shown above is: 3x5 + 0x1 : 3x5 + 2x1 : 5x2 + 1x1 = 15:17:11 = 3:17:11 PM

The time shown above is: 3x5 + 2x1 : 3x5 + 3x1 : 16x2 + 1x1 = 17:18:33 = 5:18:33 PM

Step 2: Tools and Materials

Electronics Materials:

Woodworking Materials:

  • 3/4 in. Plywood
  • Thin Plywood
  • Scrap Wood (I used 2x4s but hardwood would work as well)
  • Paint
  • Acrylic 30 x 36 in. Sheet (found at local home improvement store)
  • Window Tint (try to source locally. If none is available, you can find a sheet large enough here)
  • Window Tint Application Fluid (I used water mixed with baby shampoo in a spray bottle)
  • Windex
  • Butcher Paper
  • Screws
  • Spray Adhesive
  • Glue
  • Glue Stick

Tools:

  • Ruler
  • Xacto Knife
  • Tape
  • Double Sided Tape
  • Compass
  • Circle Cutting Jig
  • Jigsaw
  • Bandsaw
  • Spindle Sander
  • Palm Sander
  • Disc Sander
  • Router Table
  • Awl
  • Drill and Drill Bits/Drivers
  • Clamps
  • Soldering Iron
  • Solder
  • Wire Strippers

Step 3: Assemble Templates

For the large template, print it off using the poster setting in Adobe Reader. Trim off the margins for each paper and tape together. The vertical, horizontal, and diagonal lines will help in lining up the template. The pages all have small numbers on them to help keep them organized if they fall out of order.

All templates and files needed are found in Step 26.

Step 4: Rough Cut Circles

Laying out the two templates on a sheet of 3/4 in. plywood, draw circles a bit larger than needed with a compass. Using a jigsaw, cut out the rough shape.

Step 5: Cut to Size

Using a circle cutting jig on the bandsaw, cut the circles to final size.

Step 6: Apply Template

Using spray adhesive, apply each template to a circle. Insert a nail in the center of the template to center it on the circle.

Step 7: Cut Template

Using a jigsaw, cut out each individual window of the template. If you have access to a CNC, this step would be much easier! I drilled a hole in each window to help with this process. As you start cutting, the template may start to come off. If this happens, you can secure it in place with small pieces of tape.

Step 8: Sanding

Using sandpaper applied to a stick, a spindle sander, and palm sander, sand and smooth out the rough cut left by the jigsaw.

Step 9: Drill Holds for LEDs

Mark the center of each hole with an awl and drill clearance holes for the LEDs. I used a guide to help keep the drill perpendicular to my workpiece and a backerboard to keep from blowing out the wood on the back.

Step 10: Combine Boards

Swap the front and back boards and trace parts of the frame on the back of the LED board. Move the frame back to the front of the LED board and drill holes and screw the pieces together.

See image notes for more information.

Step 11: Insert LEDs

Push the LEDs through the back of the LED board. The holes should be spaced just enough that you won't need to cut any wires except moving from one circle to the next.

From the back, the LEDs start in the center and then run counter clockwise then up to the next ring.

Step 12: Attach Segment 1

Cut out 9 segments from the "Segment 1" template attached on 3/4 in. plywood (found in step 26). Attach to the LED board with glue and clamps. If you are impatient you can also use nails to clamp it in place.

Once dry, sand the edge flush with a disc sander.

Step 13: Paint

Spray paint both the LED board and the frame. If I was making this again, I would have selected to use white paint instead of black as it would be more reflective through the lens.

Step 14: Segment 2

Cut out 9 segments from the "Segment 2" template attached out of wood that is 2 3/8 in. thick (found in step 26). I used some scrap 2x4s from around the shop. Dry fit the segments and ensure it fits well with a band clamp. If everything checks out, cover the outside with painters tape to keep the glue from sticking and let dry for at least an hour before moving on to the next step.

Step 15: Segment 3

Cut out 9 segments from the "Segment 3" template attached out of 3/8 in. thick scrapwood (found in step 26). Glue them so the seams from Segment 2 are in the middle of each Segment 3. This will strengthen the ring.

Step 16: Smooth Ring and Paint

I made a custom sanding block out of the offcut piece of the large ring. Sand the inside and outside of the ring and fill any cracks that may have appeared during the glue up process.

Once smooth, apply a few coats of black paint and clear coat.

Step 17: Cut Acrylic

Cut the acrylic to a square measuring 30 x 30 in. and mark the center. Attach the acrylic with double sided tape. Using a flush trim router bit, remove the excess acrylic

Step 18: Apply Window Tint

In a dust free environment, remove the protective film from the acrylic. Apply spray and remove backing from the window tint. Apply window tint sticky side down. Using a squeegee or credit card, squeeze out all the liquid from under the window tint. Once all bubbles and wrinkles have been removed, trim the excess window tint using a sharp knife.

Step 19: Attach Defuser

I used a large piece of butcher paper to act as a defuser. Lay out the paper on a flat surface. Cover the face of the frame with glue from a glue stick. Before the glue drys, lay the front of the clock face down on the paper and rough cut the excess. Once dry, use a sharp knife to trim flush.

Step 20: Apply Insulation

I used electrical tape to keep the power and data lines separate.

Step 21: Assemble

Remove the other protective layer from the acrylic. Place the acrylic inside the ring with the window tint side up. Slide the remainder of the clock into the ring. Use a clamp to apply light pressure while a hole is drilled through the ring and into the LED board. This should be roughly 1 1/8 in. from the back. Be careful not to drill into an LED. Screw a truss head screw into the hole. Repeat for a total of eight screws around the perimeter of the clock.

Step 22: Attach Anchor Points

Glue anchor points to the back of the clock for the back cover to attach to. These are 3/4 in. thick and about 2 in. long.

Step 23: Drill Power and LDR Sensor Holes

Drill a power hole through the bottom of the clock for the power plug and a hole in the top for the light dependent resistor (LDR) sensor.

Step 24: Install Electronics Holder

Install the 3D printed holder for the RTC and Arduino Nano. Connect all electronics as shown in the schematic.

Step 25: Back Cover

Cut a back cover from thin plywood just smaller than the outside of the clock. Drill holes into the anchor points. Find the center of the back and measure out 8 inches in either direction to cut keyholes (standard 16 in centers for studs in the US). I drilled the main hole just larger than the head of the screws I'm going to use and filed the hole larger in one direction. Paint black and attach the cover in place.

Step 26: Code and Files

Again, I'm fairly new to using many of the Arduino libraries used here so I'm sure there are better ways to utilize them.

I wrote the code to be easily updated based on how many LEDs you are using if the project is scaled up or down. All you need to do is update the LED starting and ending positions as well as how many LEDs are part of each digit.

I've added a few animations that play at startup as well as on the hour. They are sudo random based on the random number generator it has on board.

You can set the clock to cycle through colors or stay static at one. You can even highlight the indicator digit to help read time as shown in the introduction.

Feel free to edit and change the code as you wish.

#include "RTClib.h"
#include <FastLED.h>

#define NUM_LEDS 96
#define DATA_PIN 3
#define LDR A0

RTC_DS1307 rtc;

boolean timeChange = false;
boolean printTime = false; // Set to true if you want to see output in the console. Helpful for debugging.
boolean redDown = true;
boolean greenDown = false;
boolean blueDown = false;
boolean cycle = false; // Set true if you want clock colors to cycle
boolean highlight = true; // Set true to highlight 'last digit'.

// Locations of the start and end of each group of time
const int SECOND_1_LOCATION = 0;
const int HOUR_2_START_LOCATION = 1;
const int HOUR_2_END_LOCATION = 8;
const int HOUR_1_START_LOCATION = 9;
const int HOUR_1_END_LOCATION = 20;
const int MINUTE_2_START_LOCATION = 21;
const int MINUTE_2_END_LOCATION = 42;
const int MINUTE_1_START_LOCATION = 43;
const int MINUTE_1_END_LOCATION = 66;
const int SECOND_2_START_LOCATION = 67;
const int SECOND_2_END_LOCATION = 95;
const int LEDS_PER_HOUR_1 = 3;
const int LEDS_PER_HOUR_2 = 2;
const int LEDS_PER_MINUTE_1 = 6;
const int LEDS_PER_MINUTE_2 = 2;

// Multipliers used to split up time
const int MULTIPLIER_FIVE = 5;
const int MULTIPLIER_TWO = 2;

const int START_UP_DELAY = 1; // Change this to speed up or slow down startup animation
const int CYCLE_SPEED = 1; // Change the rate here for color changing cycle (must be above 1)

// Declare variables
int lastSecond = 0;
int currentHour = 0;
int currentMinute = 0;
int currentSecond = 0;
int hour1 = 0;
int hour2 = 0;
int minute1 = 0;
int minute2 = 0;
int second1 = 0;
int second2 = 0;
int cycleCount = 1;
float fadeValue = 255;
float fadeCheck = 255;
uint8_t bright = 255;

int numberOfAnimations = 5;
int randomness = 0;

// Set Colors
uint8_t red = 0;
uint8_t green = 0;
uint8_t blue = 255;
uint8_t highlight_red = 60;
uint8_t highlight_green = 60;
uint8_t highlight_blue = 255;

// Define the array of leds
CRGB leds[NUM_LEDS];

void setup() {

  Serial.begin(19200);
  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
  LEDS.setBrightness(bright);
  FastLED.clear();
  rtc.begin();


  // Uncomment line below to set time.
//     rtc.adjust(DateTime(2020, 2, 19, 23, 59, 50));
//     rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  // Startup animation
  animate(randomness);
}

void loop() {

  // Get time
  DateTime now = rtc.now();
  currentHour = now.hour();
  currentMinute = now.minute();
  currentSecond = now.second();
  timeChange = false;

  // Use these to manually set time without RTC. Helpful for debugging
  //    currentHour = 5;
  //    currentMinute = 30;
  //    currentSecond = 30;

  //   Reset all bits to zero
  for (int i = SECOND_1_LOCATION; i <= SECOND_2_END_LOCATION; i++)
  {
    leds[i] = CRGB::Black;
  }

  // Set Hour
  // Set hour 1
  hour1 = (currentHour % MULTIPLIER_FIVE) * LEDS_PER_HOUR_1; // This will count the total LEDs of the time unit to light up

  for (int i = HOUR_1_START_LOCATION; i < (HOUR_1_START_LOCATION + hour1); i++)
  {
    leds[i] = CRGB( red, green, blue);
  }

  if (highlight == true && hour1 > 0)// && hour1 < 12)
  {
    for (int i = (HOUR_1_START_LOCATION + hour1 - 1); i >= (HOUR_1_START_LOCATION + hour1 - LEDS_PER_HOUR_1); i--)
    {
      leds[i] = CRGB( highlight_red, highlight_green, highlight_blue);
    }
  }

  // Set hour 2
  hour2 = (currentHour / MULTIPLIER_FIVE) * LEDS_PER_HOUR_2; // This will count the total LEDs of the time unit to light up

  for (int i = HOUR_2_START_LOCATION; i < (HOUR_2_START_LOCATION + hour2); i++)
  {
    leds[i] = CRGB( red, green, blue);
  }

  if (highlight == true && hour2 > 0)// && hour2 < 8)
  {
    for (int i = (HOUR_2_START_LOCATION + hour2 - 1); i >= (HOUR_2_START_LOCATION + hour2 - LEDS_PER_HOUR_2); i--)
    {
      leds[i] = CRGB( highlight_red, highlight_green, highlight_blue);
    }
  }

  // Set Minute
  // Set minute 1
  minute1 = (currentMinute % MULTIPLIER_FIVE) * LEDS_PER_MINUTE_1; // This will count the total LEDs of the time unit to light up

  for (int i = MINUTE_1_START_LOCATION; i < (MINUTE_1_START_LOCATION + minute1); i++)
  {
    leds[i] = CRGB( red, green, blue);
  }

  if (highlight == true && minute1 > 0)// && minute1 < 24)
  {
    for (int i = (MINUTE_1_START_LOCATION + minute1 - 1); i >= (MINUTE_1_START_LOCATION + minute1 - LEDS_PER_MINUTE_1); i--)
    {
      leds[i] = CRGB( highlight_red, highlight_green, highlight_blue);
    }
  }

  // Set minute 2
  minute2 = (currentMinute / MULTIPLIER_FIVE) * LEDS_PER_MINUTE_2; // This will count the total LEDs of the time unit to light up

  for (int i = MINUTE_2_START_LOCATION; i < (MINUTE_2_START_LOCATION + minute2); i++)
  {
    leds[i] = CRGB( red, green, blue);

  }

  if (highlight == true && minute2 > 0)// && minute2 < 22)
  {
    for (int i = (MINUTE_2_START_LOCATION + minute2 - 1); i >= (MINUTE_2_START_LOCATION + minute2 - LEDS_PER_MINUTE_2); i--)
    {
      leds[i] = CRGB( highlight_red, highlight_green, highlight_blue);
    }
  }

  // Set Second
  if (currentSecond != lastSecond)
  {
    timeChange = true;
  }

  // Set second 1
  second1 = currentSecond % MULTIPLIER_TWO;

  if (second1 == 1)
  {
    leds[SECOND_1_LOCATION] = CRGB( red, green, blue);
  }

  // Set second 2
  second2 = currentSecond / MULTIPLIER_TWO;

  for (int i = SECOND_2_START_LOCATION; i < (SECOND_2_START_LOCATION + second2); i++)
  {
    leds[i] = CRGB( red, green, blue);
  }

  if (highlight == true && second2 > 0)// && second2 < 29)
  {
    for (int i = (SECOND_2_START_LOCATION + second2 - 1); i >= (SECOND_2_START_LOCATION + second2 - 1); i--)
    {
      leds[i] = CRGB( highlight_red, highlight_green, highlight_blue);
    }
  }
  lastSecond = currentSecond;

  // Count cycles of the program and call the setColor function to change the color of the LEDs ever CYCLE_SPEED cycles.
  if (cycleCount < CYCLE_SPEED)
  {
    cycleCount++;
  }

  else if (cycleCount == CYCLE_SPEED && cycle == true)
  {
    cycleCount++;
    setColor(cycle);
  }

  else
  {
    cycleCount = 0;
  }

  // Animate every hour
  randomness = random(numberOfAnimations);
  if (currentMinute == 0 && currentSecond == 0)
  {
    animate(randomness);
  }

  // This equation is creating using interpolation to determine the brightness.
  // I suggest leaving it commented unless you find the LEDs too bright at night.
  // If you want to change the brightness of your lights and want to create your own equation,
  // search 'Interpolation' online and jump feet first into the world of numerial analysis.
  
  //  fadeValue = analogRead(LDR) * -.18 + 333.71;
  //  float readin = analogRead(LDR);
  //  Serial.println(readin);
  //  Serial.println(fadeValue);
  //  if (fadeValue > bright)
  //  {
  //    fadeValue = bright;
  //  }
  //  else if (fadeValue < 150)
  //  {
  //    fadeValue = 150;
  //  }
  //  LEDS.setBrightness(fadeValue);

  FastLED.show();

  // Print current time to the console
  if (timeChange == true && printTime == true)
  {
    printToConsole();
  }
}

// Animation function add more animations here as you wish
void animate(int select)
{
  if (select == 0)
  {
    for (int i = SECOND_1_LOCATION; i <= SECOND_2_END_LOCATION; i++)
    {
      leds[i] = CRGB( red, green, blue);
      FastLED.show();

      delay(START_UP_DELAY);
    }

    for (int i = SECOND_2_END_LOCATION; i >= SECOND_1_LOCATION; i--)
    {
      leds[i] = CRGB::Black;
      FastLED.show();

      delay(START_UP_DELAY);
    }
  }

  else if (select == 1)
  {
    for (int i = 0; i < 250; i++)
    {
      int light = random(95);

      leds[light] = CRGB( red, green, blue);

      FastLED.show();
    }
  }

  else if (select == 2)
  {
    leds[0] = CRGB( red, green, blue);

    for (int i = 0; i <= SECOND_2_END_LOCATION - SECOND_2_START_LOCATION; i++)
    {
      leds[SECOND_2_START_LOCATION + i] = CRGB( red, green, blue);

      if (i <= (MINUTE_1_END_LOCATION - MINUTE_1_START_LOCATION))
      {
        leds[MINUTE_1_START_LOCATION + i] = CRGB( red, green, blue);
      }

      if (i <= (MINUTE_2_END_LOCATION - MINUTE_2_START_LOCATION))
      {
        leds[MINUTE_2_START_LOCATION + i] = CRGB( red, green, blue);
      }

      if (i <= (HOUR_1_END_LOCATION - HOUR_1_START_LOCATION))
      {
        leds[HOUR_1_START_LOCATION + i] = CRGB( red, green, blue);
      }

      if (i <= (HOUR_2_END_LOCATION - HOUR_2_START_LOCATION))
      {
        leds[HOUR_2_START_LOCATION + i] = CRGB( red, green, blue);
      }

      delay(34);
      FastLED.show();
    }
  }

  else if (select == 3)
  {
    leds[0] = CRGB( red, green, blue);

    for (int i = 0; i <= SECOND_2_END_LOCATION - SECOND_2_START_LOCATION; i++)
    {
      leds[SECOND_2_END_LOCATION - i] = CRGB( red, green, blue);

      if (i <= (MINUTE_1_END_LOCATION - MINUTE_1_START_LOCATION))
      {
        leds[MINUTE_1_END_LOCATION - i] = CRGB( red, green, blue);
      }

      if (i <= (MINUTE_2_END_LOCATION - MINUTE_2_START_LOCATION))
      {
        leds[MINUTE_2_END_LOCATION - i] = CRGB( red, green, blue);
      }

      if (i <= (HOUR_1_END_LOCATION - HOUR_1_START_LOCATION))
      {
        leds[HOUR_1_END_LOCATION - i] = CRGB( red, green, blue);
      }

      if (i <= (HOUR_2_END_LOCATION - HOUR_2_START_LOCATION))
      {
        leds[HOUR_2_END_LOCATION - i] = CRGB( red, green, blue);
      }

      delay(34);
      FastLED.show();
    }
  }

  else if (select == 4)
  {
    for (int i = SECOND_2_END_LOCATION; i >= SECOND_1_LOCATION; i--)
    {
      leds[i] = CRGB::Black;
    }
    FastLED.show();
    delay(200);

    for (int i = SECOND_1_LOCATION; i <= SECOND_2_END_LOCATION; i++)
    {
      leds[i] = CRGB( red, green, blue);
    }
    FastLED.show();
    delay(200);

    for (int i = SECOND_2_END_LOCATION; i >= SECOND_1_LOCATION; i--)
    {
      leds[i] = CRGB::Black;
    }
    FastLED.show();
    delay(200);

    for (int i = SECOND_1_LOCATION; i <= SECOND_2_END_LOCATION; i++)
    {
      leds[i] = CRGB( red, green, blue);
    }
    FastLED.show();
    delay(200);

    for (int i = SECOND_2_END_LOCATION; i >= SECOND_1_LOCATION; i--)
    {
      leds[i] = CRGB::Black;
    }
    FastLED.show();
    delay(200);
  }
}

// Color cycling function
void setColor(boolean cycleColors)
{
  if (cycleColors == true)
  {
    if (redDown == true && greenDown == false)
    {
      red++;
      green--;
      if (green <= 0)
      {
        red = 255;
        redDown = false;
        greenDown = true;
      }
    }
    else if (greenDown == true && blueDown == false)
    {
      green++;
      blue--;
      if (blue <= 0)
      {
        green = 255;
        greenDown = false;
        blueDown = true;
      }
    }
    else if (blueDown == true && redDown == false)
    {
      blue++;
      red--;
      if (red <= 0)
      {
        blue = 255;
        blueDown = false;
        redDown = true;
      }
    }
  }

  else
  {
    red = 0;
    green = 0;
    blue = 255;
  }

}

// Print to Serial Monitor function
void printToConsole()
{
  Serial.print("Current Time: ");
  Serial.print(currentHour);
  Serial.print(":");
  Serial.print(currentMinute);
  Serial.print(":");
  Serial.println(currentSecond);
  Serial.println(" ");


  for (int i = HOUR_2_START_LOCATION; i <= HOUR_2_END_LOCATION; i++)
  {
    Serial.print(leds[i]);
    if (i % 2 == 0)
    {
      Serial.print("  ");
    }
  }
  Serial.println(" ");

  for (int i = HOUR_1_START_LOCATION; i <= HOUR_1_END_LOCATION; i++)
  {
    Serial.print(leds[i]);
    if (((i - HOUR_1_START_LOCATION + 1) % 3) == 0)
    {
      Serial.print(" ");
    }
  }
  Serial.println("  ");

  for (int i = MINUTE_2_START_LOCATION; i <= MINUTE_2_END_LOCATION; i++)
  {
    Serial.print(leds[i]);
    if (((i - MINUTE_2_START_LOCATION) + 1) % 2 == 0)
    {
      Serial.print(" ");
    }
  }
  Serial.println(" ");


  for (int i = MINUTE_1_START_LOCATION; i <= MINUTE_1_END_LOCATION; i++)
  {
    Serial.print(leds[i]);
    if (((i - MINUTE_1_START_LOCATION) + 1) % 6 == 0)
    {
      Serial.print("  ");
    }
  }

  Serial.println(" ");

  for (int i = SECOND_2_START_LOCATION; i <= SECOND_2_END_LOCATION; i++)
  {
    Serial.print(leds[i]);
    Serial.print(" ");
  }

  Serial.println(" ");

  Serial.println(leds[SECOND_1_LOCATION]);

  Serial.println();

  for (int i = 0; i < NUM_LEDS; i++)
  {
    Serial.print(leds[i]);
  }

  Serial.println();
  Serial.println();
}

Step 27: Enjoy!

In conclusion, this clock is wonderful to watch and once you get the hang of it, it's relatively easy to read. If you make your own clock project, let me know!

Arduino Contest 2020

Participated in the
Arduino Contest 2020