Mesmerizing Magnetic Wall Clock

by tomatoskins in Circuits > Clocks

114238 Views, 1063 Favorites, 0 Comments

Mesmerizing Magnetic Wall Clock

Edit 1 Square.jpg
DSC_7941 square.jpg

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.

Tools and Materials

20200318_181140.jpg
20200318_181122.jpg
20200318_181001.jpg
20200318_181049.jpg
20200318_180922.jpg
20200316_152148.jpg
20200316_125716.jpg
20200227_155345.jpg
20200206_165600(0).jpg
20200227_161341.jpg

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

Glue Together Wood Frame

20200206_165600(0).jpg
20200206_170120.jpg
20200206_170755(0).jpg
20200207_171318.jpg

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

Cut Frame Using Circle Cutting Jig

20200207_175142.jpg
20200207_173335.jpg
20200207_171625.jpg
20200207_171907.jpg
20200207_171955.jpg
20200207_172051.jpg
20200207_172123.jpg

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.

Print and Assemble Gears

20200218_155843.jpg
20200218_160122.jpg

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

"Glue" Sections Together

20200318_124250.jpg
20200218_164605.jpg
20200218_174036.jpg

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.

Cut Reliefs in Frame

20200207_181043.jpg
20200207_181341.jpg
20200207_181827.jpg

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.

Cut Clearance for Hall Effect Sensors

20200207_182500.jpg
20200207_185257.jpg
20200218_180130.jpg

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.

Glue Outer Ring

20200207_183434.jpg

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

Cut Hall Effect Sensor Adjustment Screws

20200218_182012.jpg

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.

Glue Rings to Hardboard

20200209_214605.jpg
20200210_172641.jpg
20200210_171226.jpg

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.

Glue Inner Disc

20200210_174507.jpg
20200210_175417(0).jpg

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

Attach Veneer

20200227_152634.jpg
20200227_153211(0).jpg
20200227_154710.jpg
20200227_155106.jpg

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.

Trim Veneer

20200227_171607.jpg

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

Cut Veneer

20200227_161221.jpg
20200305_125242.jpg

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.

Glue Veneer

20200228_172654.jpg
20200305_133054.jpg

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.

Sand and Finish

20200306_163453.jpg
20200306_163744.jpg

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.

Install Power

20200316_130216.jpg
20200316_130544.jpg

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

Assemble Electronics

20200225_114600.jpg
20200225_114607.jpg
20200227_092426.jpg
20200227_092455(0).jpg

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.

Solder and Connect Electronics

20200316_135135.jpg
20200316_135149.jpg
20200316_150934.jpg
20200316_150948.jpg
20200213_130249.jpg
20200213_134752.jpg

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

Back Plate

20200316_154541.jpg
20200316_173552.jpg
20200316_173922.jpg
20200316_175047.jpg
20200318_120508.jpg
20200318_120524.jpg

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.

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); 
  }
}

Downloads

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

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.

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.