#include <LiquidCrystal_I2C.h>
#include <Stepper.h>
// --- LCD Configuration (Address 0x27 is standard for Wokwi) ---
LiquidCrystal_I2C lcd(0x27, 16, 2);
int STEPS_PER_REV = 200;
int BUTTON_PIN = 8;
int secPins[2] = {6, 7};
int minPins[2] = {4, 5};
int hourPins[2] = {2, 3};
int POT_HOUR_PIN = A0;
int POT_MIN_PIN = A1;
int POT_SEC_PIN = A2;
// Time counters
int seconds = 0;
int minutes = 0;
int hours = 0;
bool isRunning = false;
unsigned long lastTick = 0;
// Current Physical Step Positions (0-199)
int currentSecStep = 0;
int currentMinStep = 0;
int currentHourStep = 0;
void setup() {
// put your setup code here, to run once:
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(secPins[0], OUTPUT);
pinMode(secPins[1], OUTPUT);
pinMode(minPins[0], OUTPUT);
pinMode(minPins[1], OUTPUT);
pinMode(hourPins[0], OUTPUT);
pinMode(hourPins[1], OUTPUT);
// 3. Setup LCD
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Analog Clock");
lcd.setCursor(0, 1);
lcd.print("System Ready");
delay(2000);
}
void loop() {
// put your main code here, to run repeatedly:
// 1. Check Button
if (digitalRead(BUTTON_PIN) == LOW) {
delay(50);
if (digitalRead(BUTTON_PIN) == LOW) {
isRunning = !isRunning;
// Update LCD immediately on mode change
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(isRunning ? ">> RUNNING <<" : ">> SETUP MODE <<");
while(digitalRead(BUTTON_PIN) == LOW);
}
}
// 2. Logic Handler
if (!isRunning) {
handleSetupMode();
} else {
//handleClockMode();
}
}
void handleSetupMode() {
// Read Potentiometers
int rawSec = analogRead(POT_SEC_PIN);
int rawMin = analogRead(POT_MIN_PIN);
int rawHour = analogRead(POT_HOUR_PIN);
// Map Pot values
seconds = map(rawSec, 0, 1023, 0, 59);
minutes = map(rawMin, 0, 1023, 0, 59);
hours = map(rawHour, 0, 1023, 0, 12);
// Display Time on LCD
displayTime(hours, minutes, seconds);
// Move Motors
moveMotorToTime(secPins, seconds, 60, currentSecStep);
moveMotorToTime(minPins, minutes, 60, currentMinStep);
moveMotorToTime(hourPins, hours, 12, currentHourStep);
delay(50); // Small delay to stop LCD flickering
}
void displayTime(int h, int m, int s) {
char hour[10];
// Format as HH:MM:SS (e.g., 09:05:02)
sprintf(hour, "%02d:%02d:%02d", h, m, s);
lcd.setCursor(4, 1); // Center it on the second row
lcd.print(hour);
}
// --- Helper: Moves A4988 Driver ---
void moveMotorToTime(const int pins[], int timeValue, int maxTime, int ¤tPos) {
// Calculate target step.
// Example: if seconds=30, maxTime=60, steps=200 -> target = 100.
// Example: if seconds=59, maxTime=60, steps=200 -> target = 196.
//Purpose: Convert Human Language(time) to Machine Language(position to reach).
int targetStep = (STEPS_PER_REV / maxTime)*(long)timeValue;
// Example 1: If seconds = 30 (half a minute):
// Calculation: (30 \times 200) / 60 = 100.
// Target is Step 100 (exactly half a circle).
// Example 2: If seconds = 15 (quarter minute):
// Calculation: $
// (15 \times 200) / 60 = 50$.
// Target is Step 50.
//Purpose: Calculate the Gap:how far away the target is
//from where the motor is currently sitting.
int stepsToMove = targetStep - currentPos;
// Now we know where we want to be (targetStep) and we know where we are (currentPos).
// We subtract them to find the difference.
// If I am at step 50 and want to go to 60: 60 - 50 = +10 (Move forward 10).
// If I am at step 60 and want to go to 50: 50 - 60 = -10 (Move backward 10).
//Purpose: Solve the "Circle Problem.":
//it tells the motor to go the other way
if (abs(stepsToMove) > STEPS_PER_REV / 2) {
if (stepsToMove > 0) stepsToMove -= STEPS_PER_REV;
else stepsToMove += STEPS_PER_REV;
}
// The Logic: "If the distance is more than half a circle (100 steps),
// don't go the long way around. Go the other way."
// Scenario: We are at 196, Target is 0.
// Initial Gap: -196.
// Is |-196| > 100? Yes.
// Fix: -196 + 200 = +4.
// New Result: Move +4 steps.
// The motor moves forward across the finish line,
// which is exactly what a clock should do.
//Purpose: Drive the Hardware:direction and pulses
if (stepsToMove != 0) {
digitalWrite(pins[1], stepsToMove > 0 ? HIGH : LOW);
int stepsAbs = abs(stepsToMove);// Ignore negative sign for the loop
for(int i=0; i<stepsAbs; i++) {
digitalWrite(pins[0], HIGH);// Pulse ON
delayMicroseconds(1000);
digitalWrite(pins[0], LOW);// Pulse OFF
delayMicroseconds(1000);
}
// Direction: If stepsToMove is positive,
// we set the DIR pin HIGH. If negative, LOW.
// The Loop: We loop X times.
// Inside the loop, we turn the STEP pin HIGH and LOW.
// Each time we do this, the A4988 forces the motor to jump one step.
//Purpose: Update the "You Are Here" Marker
//update the currentPos variable
//so the Arduino remembers the new location for next time.
currentPos += stepsToMove;
if (currentPos >= STEPS_PER_REV) currentPos -= STEPS_PER_REV;
if (currentPos < 0) currentPos += STEPS_PER_REV;
// Imagine your motor is at Step 190 (near the end of the circle).
// You tell it to move forward 20 steps.
// 190 + 20 = 210
// But step 210 does not exist!
// The circle ends at 199 and starts again at 0.
// Step 210 is actually just Step 10 on the next lap.
// The Solution (Going Forward):
// "If the number is bigger than the circle size (200), subtract 200 from it."
// Math: 210 - 200 = 10.
// Now the computer knows we are physically at Step 10.
// Imagine you are at Step 5 (beginning of the circle).
// You tell it to move backwards 10 steps.
// 5 + (-10) = -5
// The variable currentPos is now -5. Step -5 does not exist.
// "Negative 5" just means 5 steps back from the finish line.
// Translation: "If the number is negative, add 200 to loop it back to the end of the circle."
// Math: -5 + 200 = 195.
// Now the computer knows we are physically at Step 195.
// his technique is called "wrapping" or "normalizing" the value.
// It ensures that no matter how many times the clock spins,
// the currentPos variable always stays strictly between 0 and 199.
//Think of this function as a GPS for your motor:
// it calculates the route, finds the shortest path, drives the car,
// and then updates your "current location" pin on the map.
}
// 1- Where do I want to go? (Target)
// 2- How far is it? (Distance)
// 3- Which way is faster? (Shortcut)
// 4- Move! (Hardware)
// 5- Write down my new location. (Update)
}