#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <LiquidCrystal_I2C.h>
#define TFT_CS 10
#define TFT_DC 9
#define TFT_RST 8
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
LiquidCrystal_I2C lcd(0x27, 16, 2);
#define BTN_THEME 2
// ========================================
// EASY AM/PM SWITCH - CHANGE THIS LINE ONLY!
// ========================================
String timeMode = "AM"; // ← "AM" or "PM" LANG!
int hours = 11, minutes = 00, seconds = 00, day = 20; // 11:59:55 PM (use 1-12 for hours)
unsigned long prevMillis = 0;
int currentThemeIndex = 0; // 0=Classic, 1=Neon, 2=Retro
// 3 ANALOG CLOCK THEMES (Background FIXED by AM/PM)
struct ClockTheme {
uint16_t rim, handH, handM, handS, center, hourMarkers;
String name;
};
// Define your themes
ClockTheme clockThemes[3] = {
// Theme 0: CLASSIC
{ILI9341_MAROON, ILI9341_BLACK, ILI9341_BLACK, ILI9341_RED, ILI9341_YELLOW, ILI9341_WHITE, "CLASSIC"},
// Theme 1: NEON
{ILI9341_PURPLE, ILI9341_MAGENTA, ILI9341_YELLOW, ILI9341_WHITE, ILI9341_CYAN, ILI9341_LIGHTGREY, "NEON"},
// Theme 2: RETRO
{ILI9341_PINK, ILI9341_RED, ILI9341_BLUE, ILI9341_GREEN, ILI9341_WHITE, ILI9341_YELLOW, "RETRO"}
};
int cx = 120, cy = 120;
unsigned long lastBtnPress = 0;
// Store last positions for erasing
float lastHourAngle = 0, lastMinuteAngle = 0, lastSecondAngle = 0;
// Function declarations
void setBackground();
void drawSunMoon();
void drawClockFace();
void drawThemeIndicator();
void updateClockHands();
void updateLCD();
void updateThemeByTime();
void setup() {
Serial.begin(9600);
pinMode(BTN_THEME, INPUT_PULLUP);
tft.begin();
tft.setRotation(0);
setBackground(); // Initial background based on time
lcd.init();
lcd.backlight();
lcd.clear();
drawSunMoon();
drawClockFace();
drawThemeIndicator();
// Initialize last angles
lastHourAngle = (hours % 12) * 30 + minutes * 0.5;
lastMinuteAngle = minutes * 6 + seconds * 0.1;
lastSecondAngle = seconds * 6;
Serial.println("✅ Fixed BG Clock - Press button to cycle clock themes");
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - prevMillis >= 1000) {
prevMillis = currentMillis;
int oldHours = hours;
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
}
if (minutes >= 60) {
minutes = 0;
hours = (hours + 1) % 24;
}
if (hours == 0 && minutes == 0) {
day++;
}
// Fire full auto AM/PM transitions
if (oldHours == 11 && hours == 12) {
timeMode = "PM";
Serial.println("☀️ NOON! 11:59AM → 12:00PM");
updateBackground();
drawSunMoon();
drawClockFace();
currentThemeIndex = 0;
}
if (oldHours == 23 && hours == 0) {
timeMode = "AM";
Serial.println("🌙 MIDNIGHT! 11:59PM → 12:00AM");
updateBackground();
drawSunMoon();
drawClockFace();
currentThemeIndex = 0;
}
updateClockHands();
updateLCD();
// Update theme based on time of day
updateThemeByTime();
}
// Theme button
if (digitalRead(BTN_THEME) == LOW && millis() - lastBtnPress > 300) {
currentThemeIndex = (currentThemeIndex + 1) % 3;
drawClockFace();
drawThemeIndicator();
lastBtnPress = millis();
Serial.println("Theme: " + getCurrentTheme().name);
}
}
// Function definitions
void setBackground() {
if (timeMode == "AM") {
tft.fillScreen(ILI9341_WHITE);
} else {
tft.fillScreen(ILI9341_BLACK);
}
}
void updateBackground() {
setBackground();
}
void drawSunMoon() {
int iconX = 230, iconY=30;
if (timeMode == "AM") {
// Sun
tft.fillCircle(iconX, iconY, 12, ILI9341_YELLOW);
tft.drawCircle(iconX, iconY, 14, ILI9341_ORANGE);
for (int i=0; i<8; i++) {
float angle = i*0.7854;
int x1 = iconX + cos(angle)*18;
int y1 = iconY + sin(angle)*18;
int x2 = iconX + cos(angle)*22;
int y2 = iconY + sin(angle)*22;
tft.drawLine(x1,y1,x2,y2, ILI9341_YELLOW);
}
} else {
// Moon
tft.fillCircle(iconX, iconY, 10, ILI9341_WHITE);
tft.drawCircle(iconX, iconY, 12, ILI9341_LIGHTGREY);
tft.fillCircle(iconX-3, iconY+2, 2, ILI9341_DARKGREY);
tft.fillCircle(iconX+4, iconY-1, 2, ILI9341_DARKGREY);
}
}
void drawClockFace() {
ClockTheme theme = getCurrentTheme();
uint16_t bgColor = (timeMode == "AM") ? ILI9341_WHITE : ILI9341_BLACK;
// Draw rim and center
tft.fillCircle(cx, cy, 118, theme.rim);
tft.fillCircle(cx, cy, 5, theme.center);
for (int i=0; i<12; i++) {
float angle = i * 0.5236; // approx 30 degrees in radians
int x1 = cx + cos(angle) * 105;
int y1 = cy + sin(angle) * 105;
int x2 = cx + cos(angle) * 90;
int y2 = cy + sin(angle) * 90;
tft.drawLine(x1, y1, x2, y2, theme.hourMarkers);
}
}
void drawThemeIndicator() {
uint16_t textColor = (timeMode == "AM") ? ILI9341_BLACK : ILI9341_WHITE;
uint16_t bgColor = (timeMode == "AM") ? ILI9341_WHITE : ILI9341_BLACK;
tft.fillRect(5, 285, 230, 15, bgColor);
tft.setTextColor(textColor);
tft.setTextSize(1);
tft.setCursor(10, 288);
tft.print("Mode: ");
tft.print(timeMode);
tft.print(" | ");
tft.print(getCurrentTheme().name);
}
void updateClockHands() {
ClockTheme theme = getCurrentTheme();
uint16_t bgColor = (timeMode == "AM") ? ILI9341_WHITE : ILI9341_BLACK;
float hourAngle = (hours % 12) * 30 + minutes * 0.5;
float minuteAngle = minutes * 6 + seconds * 0.1;
float secondAngle = seconds * 6;
// Erase old
drawHand(lastHourAngle, 70, bgColor);
drawHand(lastMinuteAngle, 95, bgColor);
drawHand(lastSecondAngle, 105, bgColor);
// Draw new
drawHand(hourAngle, 70, theme.handH);
drawHand(minuteAngle, 95, theme.handM);
drawHand(secondAngle, 105, theme.handS);
lastHourAngle = hourAngle;
lastMinuteAngle = minuteAngle;
lastSecondAngle = secondAngle;
// Center dot
tft.fillCircle(cx, cy, 5, theme.center);
}
void drawHand(float angleDeg, int length, uint16_t color) {
float angleRad = (angleDeg - 90) * PI / 180;
int x2 = cx + cos(angleRad) * length;
int y2 = cy + sin(angleRad) * length;
tft.drawLine(cx, cy, x2, y2, color);
if (length == 70) {
tft.drawLine(cx+1, cy, x2+1, y2, color);
tft.drawLine(cx-1, cy, x2-1, y2, color);
}
if (length == 95) {
tft.drawLine(cx+1, cy, x2+1, y2, color);
}
}
void updateLCD() {
lcd.clear();
lcd.setCursor(0, 0);
int displayHour = hours;
String ampm = timeMode;
if (displayHour < 10) lcd.print("0");
lcd.print(displayHour);
lcd.print(":");
if (minutes < 10) lcd.print("0");
lcd.print(minutes);
lcd.print(":");
if (seconds < 10) lcd.print("0");
lcd.print(seconds);
lcd.print(" ");
lcd.print(ampm);
lcd.setCursor(0, 1);
String monthName = "Feb";
lcd.print(monthName);
lcd.print(" ");
lcd.print(day);
lcd.print(", 2026");
}
void updateThemeByTime() {
int totalMinutes = hours * 60 + minutes; // total minutes since midnight
int morningStart = 6 * 60; // 6:00 AM
int morningEnd = 17 * 60 + 59; // 5:59 PM
int eveningStart = 18 * 60; // 6:00 PM
int eveningEnd = 23 * 60 + 59; // 11:59 PM
if (totalMinutes >= morningStart && totalMinutes <= morningEnd) {
// Daytime: white theme
if (timeMode != "AM") {
timeMode = "AM";
updateBackground();
drawSunMoon();
drawClockFace();
drawThemeIndicator();
}
} else {
// Nighttime: black theme
if (timeMode != "PM") {
timeMode = "PM";
updateBackground();
drawSunMoon();
drawClockFace();
drawThemeIndicator();
}
}
}
ClockTheme getCurrentTheme() {
return clockThemes[currentThemeIndex];
}