As I was scouring YouTube for drawing techniques to finish up my latest project, I stumbled upon an incredible clock design that had a hollow center and the clock hands attached to the rim. Even though I didn't watch the entire process of how it was made, the concept stuck with me, and I knew I had to create my own version to display my engineering prowess.
My son added some squiggly lines around the clock
One of the challenges in my clock project was calculating the necessary gears, rather than simply creating a CAD model in Fusion 360. I had envisioned a clock with two hands: one for minutes, which would rotate 12 times per hour, and one for hours, which would rotate once per hour. To ensure the clock was compact and aesthetically pleasing, I needed it to be around 4 inches in diameter, which meant I had to use multiple gears.
Despite having recently learned about gears in my Total Mechatronics class, I struggled to calculate the necessary ratios. Over the course of 3-4 days, I searched extensively for helpful articles and YouTube videos, but most of what I found only covered the calculation of ratios for two gears. This was not sufficient for my project, as I needed to use more than two gears to achieve the desired design. Nonetheless, I persisted and eventually found a way to make the gear ratios work for my clock base, resulting in a harmonious overall look.
To make this work, I used Gear 12 as the driving gear. The first Gear 60 rotated once for every five rotations of the driving gear, while the second Gear 60 rotated once for every 12 rotations of the first Gear 60. To scale the Gear 12 to 1:12, I used two combined gears of 12:24 and a Gear 12:36. This system of interlocking gears allowed me to achieve the desired motion for the clock hands. Pictures of the gears will be posted in the design section.
As I delved deeper into the project, I faced yet another daunting challenge: how to arrange the gears in a way that would both minimize the base size and give the clock an eye-catching appearance. And then, a stroke of genius hit me: why not make the clock run using Arduino? However, I quickly discovered that the motor wouldn't fit the Gear 12, and I had to add another gear. But adding a gear meant more complications - I couldn't connect the 12:36 gears to the hour gear. So, I had to add yet another connection gear. After careful consideration, I chose the 24-teeth gear to connect the 12:36 to the 60-teeth gear (hour gear). It was a thrilling and challenging journey, but in the end, it was all worth it when I saw the clock ticking away perfectly.
My initial idea was to use a stepper motor to rotate the clock arm since that was the only motor available to me. Originally, I planned to use buttons to set the time on the clock. However, I soon realized that the RPM of the stepper motor was not fast enough for time setting. Consequently, I revised my idea and decided to use a rotary switch to set the direction of the stepper motor for the sole purpose of demonstrating how the clock works. I added an LED to indicate whether the clock is in normal mode or fast run mode.
I don't want to use screws to assemble all the parts, since this clock doesn't require high torque or speed. Instead, I have designed a C-clip to securely hold the gear in place.
After assembling all the parts and using the provided code, I observed that all the gears are functioning correctly according to my expectations.
I initially ran into the problem of single-threaded operation. To work around this, I researched and discovered that specific pins on the board can be used as interrupt signals to detect input signals outside the loop() function.
After successfully implementing interrupt signals, I had to tackle the next challenge of decoding the rotary encoder used in the clock. I experimented with different approaches, but none of them yielded the accurate results I needed. Initially, I thought that the low-quality encoder might be the issue. However, after conducting thorough research, I found the correct method to decode the rotary encoder. This breakthrough enabled the clock to function as intended.
Additionally, I added a feature to the clock that allows for easy time adjustments. By pressing the rotary knob and turning it to the left or right, the minute arm runs at full speed, making it simple to adjust the time accurately.
To ensure that the clock kept accurate time, I calculated the speed of the stepper. The stepper had 4096 steps to make a full revolution, which I multiplied by 5 to make the minutes gear turn one revolution. Then, I divided it by 60 to get the number of steps the stepper needed to move per minute. Since the clock had only two arms, I calculated the number of steps per hour by dividing the steps per minute by 60, which slowed the stepper down to make five rotations per hour.
#include <AccelStepper.h>
#include <Encoder.h>
// Motor pin definitions:
#define motorPin1 10 // IN1 on the ULN2003 driver
#define motorPin2 11 // IN2 on the ULN2003 driver
#define motorPin3 12 // IN3 on the ULN2003 driver
#define motorPin4 13 // IN4 on the ULN2003 driver
#define encPinCLK 3
#define encPinDT 4
#define encPinSW 2
#define btnPin 8
#define ledPin 6
#define MotorInterfaceType 8
#define MODE_RUN 0
#define MODE_ADJUST 1
AccelStepper stepper = AccelStepper(MotorInterfaceType, motorPin1, motorPin3, motorPin2, motorPin4);
int stepsPerRevolution = 4096;
float speedForMinute = stepsPerRevolution * 5 / 60; // 5 circle of 12 gear to complete the 60 teeth gear. Divide it to 60 seconds, the result is steps per minute.
float speedForHour = speedForMinute / 60; //Slow the speed down to 60 times. 0
float speed = 5;
int moveTo = 0;
int mode = MODE_RUN;
int direction = -1;
long timeOfLastBlinking = 0;
int dealyOfBlinking = 500;
int ledState = LOW;
int encCLK;
void setup() {
stepper.setMaxSpeed(1000);
pinMode(ledPin, OUTPUT);
pinMode(encPinSW, INPUT_PULLUP);
pinMode(encPinCLK, INPUT);
attachInterrupt(digitalPinToInterrupt(encPinCLK), encoder_detect, CHANGE);
attachInterrupt(digitalPinToInterrupt(encPinSW), button_detect, FALLING);
Serial.begin(9600);
encCLK = digitalRead(encPinCLK);
}
void loop() {
if(mode == MODE_RUN){
led(HIGH);
speed = speedForHour;
stepper.setSpeed(speed * direction);
stepper.runSpeed();
}else{
led(getLedState());
if(direction != 0){
speed = 1000;
stepper.setSpeed(speed * direction);
stepper.runSpeed();
}
}
}
int getLedState(){
long currentTime = millis();
if(currentTime - timeOfLastBlinking > dealyOfBlinking){
led(ledState);
ledState = !ledState;
timeOfLastBlinking = currentTime;
}
return ledState;
}
void led(int state){
digitalWrite(ledPin, state);
}
void encoder_detect () {
if(mode == MODE_ADJUST){
int currentCLK = digitalRead(encPinCLK);
Serial.println(currentCLK);
if(currentCLK != encCLK)
{
if(digitalRead(encPinDT) != currentCLK)
{
moveTo++;
direction = -1;
}
else
{
moveTo--;
direction = 1;
}
}
encCLK = currentCLK;
}
}
void button_detect()
{
// mode = !mode;
Serial.println("Mode changing to");
if(mode == MODE_ADJUST){
Serial.println("Running");
direction = -1;
mode = MODE_RUN;
}else{
Serial.println("Adjusting");
stepper.stop();
direction = 0;
mode = MODE_ADJUST;
timeOfLastBlinking = millis();
}
}