#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Wire.h>
#include "RTClib.h"
// TFT pins
#define TFT_CS 10
#define TFT_DC 9
#define TFT_RST 8
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
RTC_DS3231 rtc;
// Screen positions
int centerX, centerY;
int targetX;
float x;
float dx;
bool bouncing = false;
bool moveRight = true;
// Colors
uint16_t colors[] = {
ILI9341_RED, ILI9341_GREEN, ILI9341_BLUE,
ILI9341_YELLOW, ILI9341_CYAN, ILI9341_MAGENTA, ILI9341_WHITE
};
int colorIndex = 0;
// Timing
unsigned long lastSecond = 0;
unsigned long lastFrame = 0;
char timeStr[9];
int steps = 0;
void setup() {
tft.begin();
tft.setRotation(0); // Portrait
tft.fillScreen(ILI9341_BLACK);
tft.setTextSize(3);
tft.setTextColor(colors[colorIndex], ILI9341_BLACK);
Wire.begin();
rtc.begin();
if (rtc.lostPower()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// Center position
centerX = (tft.width() - 150) / 2;
centerY = (tft.height() - 30) / 2;
x = centerX;
}
void loop() {
DateTime now = rtc.now();
// Update time string once per second
if (millis() - lastSecond >= 1000) {
lastSecond = millis();
sprintf(timeStr, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
// Trigger bounce at multiples of 5 seconds
if (now.second() % 5 == 0 && !bouncing) {
bouncing = true;
moveRight = !moveRight; // alternate direction
targetX = moveRight ? (tft.width() - 150) : 0;
dx = (targetX - centerX) / 100.0; // 100 steps over 5s
colorIndex = (colorIndex + 1) % (sizeof(colors)/sizeof(colors[0]));
tft.setTextColor(colors[colorIndex], ILI9341_BLACK);
// Clear the whole screen on bounce
tft.fillScreen(ILI9341_BLACK);
steps = 0;
}
}
// Animate bounce if active
if (bouncing && millis() - lastFrame >= 50) {
lastFrame = millis();
x += dx;
steps++;
if (steps >= 100) { // after 5s
bouncing = false;
x = centerX; // snap back to middle
}
}
// Draw time
tft.setCursor((int)x, centerY);
tft.print(timeStr);
}