Introduction: Mesmerizing Magnetic 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).

Mechanical clocks have always fascinated me. The way all of the internal gears, springs, and escapements work together to result in a constant reliable timepiece has always seemed out of reach for my limited skill set. Thankfully modern electronics and 3D printed parts can bridge the gap to create something simple that doesn't rely on small precise metallic parts.

This minimalist wall clock hides a pair of 3D printed ring gears driven by inexpensive stepper motors that rotate magnets behind a classic walnut veneer.

Initially inspired by STORY Clock, I wanted a time piece that indicated the time of day using ball bearings only vs the digital readout and slow moving ball bearing their product uses.

Step 1: Tools and Materials

Materials:

Tools:

  • 3D Printer
  • Compass
  • X-acto Knife
  • Glue
  • Clamps
  • Circle Cutting Jig
  • Hack Saw
  • Disc Sander
  • Ratchet Clamp
  • Chisel
  • Ruler
  • Sander
  • Drills
  • Screwdrivers
  • Soldering Iron
  • Hot Glue Gun

Step 2: Glue Together Wood Frame

Glue together three pieces of wood that will form the frame of the clock. I used reclaimed particle board from an old bed frame.

Step 3: Cut Frame Using Circle Cutting Jig

Mark the center of the board and mount to a circle cutting jig. Cut five circles with the following diameters:

  • 12 in.
  • 11 1/4 in.
  • 9 1/4 in.
  • 7 1/4 in.
  • 5 3/8 in.

Step 4: Print and Assemble Gears

The ring gears are broken into segments so they can be printed on a small printer and snapped together. All parts were printed in ABS to aid in the fusing process shown in the next step. Sand all edges and surfaces of the parts.

Print the following quantities of parts found in step 22:

  • 1 - Hour Ring Gear Segment Magnet
  • 6 - Hour Ring Gear Segment Basic
  • 1 - Hour Retaining Ring Segment Stepper Mount
  • 6 - Hour Retaining Ring Segment Basic
  • 1 - Hour Hall Effect Sensor Holder
  • 1 - Minute Ring Gear Segment Magnet
  • 7 - Minute Ring Gear Segment Basic
  • 1 - Minute Retaining Ring Segment Stepper Mount
  • 6 - Minute Retaining Ring Segment Basic
  • 1 - Minute Hall Effect Sensor Holder
  • 2 - Spur Gear
  • 1 - Electronics Mount

Step 5: "Glue" Sections Together

In a glass bottle with some acetone, dissolve failed prints old support material, etc. Paint the acetone mixture on each seam to fuse the pieces together. Once cured, sand each seam flat.

Step 6: Cut Reliefs in Frame

Place the ring gears and retaining rings in the frame and cut out reliefs for the stepper motors. I measured and cut the inner ring too large so I shimmed it to size using some maple edge banding I had around the shop.

Step 7: Cut Clearance for Hall Effect Sensors

Cut a clearance hole through the inner ring for the minute hall effect sensor and slot for the hour hall effect sensor. I used a chisel, file, and small hand saw to cut these clearances.

Step 8: Glue Outer Ring

Glue and tape the outer ring the size of the minute retaining ring.

Step 9: Cut Hall Effect Sensor Adjustment Screws

Cut machine screws with a hack saw so they are just longer than the thickness of the retaining ring and hall effect sensor holder. Cut a slot in the threads so it can be adjusted from the threaded end with a flat screwdriver.

Step 10: Glue Rings to Hardboard

Cut a circle of hardboard just larger than the outer ring. Glue outer and inner ring to hardboard face. Use the minute retaining ring and ring gear to place the inner ring. Pay better attention than I did to not glue the inner ring on backwards. Image two shows a new slot cut for minute hall effect sensor.

Use a disc sander to trim the hardboard down to the size of the outer ring.

Step 11: Glue Inner Disc

Glue inner disc in place using the hour retaining ring and ring gear to place the inner disc.

Step 12: Attach Veneer

Cut a strip of veneer wider than the clock is deep and long enough to wrap around the clock (3.14 * diameter of the clock, will return the length needed. Add an inch to be sure that you have enough.) Dry fit the veneer to cut to length. Applying ample glue to the veneer and clamp in place with a strap clamp. Let dry a couple hours to ensure adhesion.

Step 13: Trim Veneer

Using a sharp chisel, trim the excess veneer from the front and back of the clock.

Step 14: Cut Veneer

My veneer had some cracks in it. To make it easier to work with, I applied painters tape to hold it together. Using an x-acto knife in a compass, cut the veneer just larger than the face of the clock.

Step 15: Glue Veneer

Use the off cut rings to spread out the pressure across the face of the clock. Apply ample glue to the non tape side of the veneer. Orient the grain vertically on the clock face and apply many clamps tightening each one a little at a time. This will ensure the veneer doesn't shift and has even pressure across the face.

I used a couple flat boards on the face side of the clock and some cauls on the back.

Step 16: Sand and Finish

Using sandpaper, carefully remove the excess veneer from the face of the clock and sand starting at 220 grit up to 600 grit.

Apply between 10 and 20 coats of lacquer. This will build up the surface that the ball bearing will ride along. Inevitably due to dust and other particles in the air, I think that lines will appear along the path of each ball bearing. Applying more coats of finish should delay this as long as possible. It will also make future refinishing easier. I will update this step if lines ever appear on my clock.

Step 17: Install Power

Using a 27/64 in. drill bit, drill a hole in the bottom of the clock and screw the power plug in place.

Step 18: Assemble Electronics

Attach stepper drivers and real time clock to the electronic board. I needed to find a way to secure the Arduino so holes were drilled and a slot was cut for a zip tie. These features have been added to the file found in step 22.

Step 19: Solder and Connect Electronics

Following the block diagram, solder all components together. Hot glue the rings in place and secure any stray wires with hot glue as well.

Step 20: Back Plate

Create the back plate by cutting another circle 1/2 in. larger than the face of the clock and a ring with the inner diameter the same as the back of the clock. Glue the ring and circle together with some spring clamps.

Once dry, scribe a line 1/8 in. larger than the inner ring and trim to size using the band saw or disc sander.

Cut a slot 1 in. long 1/4 in. wide at the top of the back using a router or drill bits. Countersink four holes to secure the back into the frame of the clock.

Apply black spray paint and attach to the clock once dry.

Step 21: Arduino Code

The arduino code is commented as well as possible. Keep in mind that I'm not a programmer, I have minimal arduino experience (be kind). The code runs continually checking to see if the current time matches the "Reset Time". Because I couldn't think of a way to translate the current time into steps, it only corrects itself once daily (midnight by default). At midnight the gears rotate to the midnight position then wait until 00:01 moving to that time then continues from there. As it currently sits, the clock only loses about 5 seconds over a 24 hour period.

You will need the Stepper and RTClib libraries installed.

I know the code can be optimized by someone with more experience than myself. If you are up to the challenge, please recreate this project for yourself and share your knowledge.

#include <Stepper.h>
#include "RTClib.h"
RTC_DS1307 rtc;

#define oneRotation 2038 // the number of steps in one revolution of 28BYJ-48 stepper motor
Stepper hourHand(oneRotation, 3, 5, 4, 6);
Stepper minuteHand(oneRotation, 7, 9, 8, 10);
#define hourStopSensor 12
#define minuteStopSensor 11
int endStep = 0;

// Time dealy for speed of the clock.
int setDelay1 = 168;
int setDelay2 = 166;
int setDelay3 = 5;

// Current time to do math with. 
float hr = 0;
float mn = 0;
float sc = 0;

// Set the time of day to reset clock (24 hour format).
int resetHour = 0;
int resetMinute = 0;

// Variables to set correct time at startup and reset.
float setTimeStepHour = 0;
float setTimeStepMinute = 0;
float handDelay = 0;
float hourTest = 0;
float minuteTest = 0;


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

  // Setup real time clock and reset hall effect sensors.
  pinMode(hourStopSensor, INPUT_PULLUP);
  pinMode(minuteStopSensor, INPUT_PULLUP);
  rtc.begin();

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

  // Set top speed of stepper motors.
  hourHand.setSpeed(15);
  minuteHand.setSpeed(15);


  // Loop until minute and hour hand are at noon
  while (digitalRead(hourStopSensor) == LOW ||  digitalRead(minuteStopSensor) == LOW)
  {

    if (digitalRead(hourStopSensor) == LOW )
    {
      hourHand.step(2);
    }
    
    else
    {
      delay(3);
    }
    

    if (digitalRead(minuteStopSensor) == LOW)
    {
      minuteHand.step(3);
    }
    
    else
    {
      delay(4);
    }
  }

  while (digitalRead(hourStopSensor) != LOW || digitalRead(minuteStopSensor) != LOW)
  {
    if (digitalRead(hourStopSensor) != LOW )
    {
      hourHand.step(2);
    }
    
    else
    {
      delay(3);
    }
    
    if (digitalRead(minuteStopSensor) != LOW)
    {
      minuteHand.step(3);
    }
    
    else
    {
      delay(4);
    }  
  }

  // Get current time
  DateTime now = rtc.now();
  hr =  now.hour();
  mn = now.minute();
  sc = now.second();

  // Change to 12 hour format 
  if (hr >= 12)
  {
    hr = hr - 12;
  }

  // See what hand must travel across the face further and use that distance 
  // to adjust set time accordingly. 
  hourTest = hr / 12;
  minuteTest = mn / 60;

  if (hourTest > minuteTest)
  {
    handDelay = hourTest;
  }
  else
  {
    handDelay = minuteTest;
  }

  // Set current hour
  setTimeStepHour = (hr * 498) + (mn * 8.3) + ((sc + (handDelay * 36)) * .1383);

  // Set current minute
  setTimeStepMinute = (mn * 114) + ((sc + (handDelay * 45)) * 1.9);

  // Test which hand will need more steps and set that to the longest step count for the for loop. 
  if (setTimeStepHour > setTimeStepMinute)
  {
    endStep = setTimeStepHour;
  }
  else
  {
    endStep = setTimeStepMinute;
  }

  for (int i = 0; i <= endStep; i++)
  {
    if (i < setTimeStepHour)
    {
      hourHand.step(2);
    }
    
    else
    {
      delay(3);
    }

    if (i < setTimeStepMinute)
    {
      minuteHand.step(3);
    }

    else
    {
      delay(4);
    }
  }

  // Set clock running RPM
  hourHand.setSpeed(1); 
  minuteHand.setSpeed(1); 

}

void loop() {
  // Start clock running loop.
  for (int i = 0; i < 22; i++)
  {
    minuteHand.step(1);
    delay(setDelay1);

    // Test for reset time, if ready to be reset, break.
    if (rtc.now().hour() == resetHour && rtc.now().minute() == resetMinute)
    {
      break;
    }
  }

  delay(setDelay3);

  for (int i = 0; i < 38; i++)
  {
    hourHand.step(1);
    delay(setDelay1);

    // Test for reset time, if ready to be reset, break.
    if (rtc.now().hour() == resetHour && rtc.now().minute() == resetMinute)
    {
      break;
    }

    for (int i = 0; i < 20; i++)
    {
      minuteHand.step(1);
      delay(setDelay2);

      // Test for reset time, if ready to be reset, break.
      if (rtc.now().hour() == resetHour && rtc.now().minute() == resetMinute)
      {
        break;
      }
    }
  }

  // Reset clock at reset time
  if (rtc.now().hour() == resetHour && rtc.now().minute() == resetMinute)
  {
    // Change speed of clock
    hourHand.setSpeed(10);
    minuteHand.setSpeed(10);

    // Loop till minute and hour hand reach noon.
     while (digitalRead(hourStopSensor) == LOW ||  digitalRead(minuteStopSensor) == LOW)
  {

    if (digitalRead(hourStopSensor) == LOW )
    {
      hourHand.step(2);
    }
    
    else
    {
      delay(3);
    }
    
    if (digitalRead(minuteStopSensor) == LOW)
    {
      minuteHand.step(3);
    }
    
    else
    {
      delay(4);
    }
    
  }

  while (digitalRead(hourStopSensor) != LOW || digitalRead(minuteStopSensor) != LOW)
  {

    if (digitalRead(hourStopSensor) != LOW )
    {
      hourHand.step(2);
    }
    
    else
    {
      delay(3);
    }
    
    if (digitalRead(minuteStopSensor) != LOW)
    {
      minuteHand.step(3);
    }
    
    else
    {
      delay(4);
    }  
  }

    // Wait here until the reset time has passed.
    while (rtc.now().minute() == resetMinute)
    {
      delay(1000);
    }

    // Get current time
    DateTime now = rtc.now();
    hr =  now.hour();
    mn = now.minute();
    sc = now.second();

    // Change to 12 hour format
    if (hr >= 12)
    {
      hr = hr - 12;
    }

    // See what hand must travel across the face further and use that distance 
    // to adjust set time accordingly. 
    hourTest = hr / 12;
    minuteTest = mn / 60;

    if (hourTest > minuteTest)
    {
      handDelay = hourTest;
    }
    else
    {
      handDelay = minuteTest;
    }

    // Set current hour
    setTimeStepHour = (hr * 498) + (mn * 8.3) + ((sc + (handDelay * 36)) * .1383);

    // Set current minute
    setTimeStepMinute = (mn * 114) + ((sc + (handDelay * 45)) * 1.9);

    // Test which hand will need more steps and set that to the longest step count for the for loop.
    if (setTimeStepHour > setTimeStepMinute)
    {
      endStep = setTimeStepHour;
    }
    else
    {
      endStep = setTimeStepMinute;
    }


    for (int i = 0; i <= endStep; i++)
    {
      if (i < setTimeStepHour)
      {
        hourHand.step(2);
      }
      
      else
      {
        delay(3);
      }
  
      if (i < setTimeStepMinute)
      {
        minuteHand.step(3);
      }
  
      else
      {
        delay(4);
      }
    }

    hourHand.setSpeed(1); 
    minuteHand.setSpeed(1); 
  }
}

Step 22: STL Files

You will need to print the following quantities of the files:

  • 1 - Hour Ring Gear Segment Magnet
  • 6 - Hour Ring Gear Segment Basic
  • 1 - Hour Retaining Ring Segment Stepper Mount
  • 6 - Hour Retaining Ring Segment Basic
  • 1 - Hour Hall Effect Sensor Holder
  • 1 - Minute Ring Gear Segment Magnet
  • 7 - Minute Ring Gear Segment Basic
  • 1 - Minute Retaining Ring Segment Stepper Mount
  • 6 - Minute Retaining Ring Segment Basic
  • 1 - Minute Hall Effect Sensor Holder
  • 2 - Spur Gear
  • 1 - Electronics Mount

Step 23: Solidworks Files

These are the original Solidworks files used to create the STLs found in the previous step. Feel free to edit and change my files as you see fit.

Step 24: Conclusion

This clock turned out better than I had anticipated. Having minimal Arduino experience, I'm glad with how it turned out and how accurate it is. It looks great and functions just as I'd hoped.