#include <WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <MD_Parola.h>
#include <SPI.h>
#include <time.h>
// ===== MATRIX CONFIG =====
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define MAX_DEVICES 4
#define DATA_PIN 23
#define CLK_PIN 18
#define CS_PIN 5
MD_Parola matrix(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
// ===== WIFI =====
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// ===== NTP (IST) =====
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 19800, 60000);
// ===== BRIGHTNESS =====
#define DAY_BRIGHTNESS 8
#define NIGHT_BRIGHTNESS 1
uint8_t currentBrightness = DAY_BRIGHTNESS;
// ===== DATA =====
char msg[50];
char secMsg[10];
String days[] = {"SUN","MON","TUE","WED","THU","FRI","SAT"};
// ===== BLINK COLON =====
bool colonState = true;
unsigned long lastBlink = 0;
// ===== RANDOM EFFECT SETS =====
textEffect_t allEffects[] = {
PA_PRINT, PA_SCROLL_LEFT, PA_SCROLL_RIGHT, PA_SCROLL_UP, PA_SCROLL_DOWN,
PA_SLICE, PA_OPENING, PA_CLOSING, PA_GROW_UP, PA_GROW_DOWN, PA_MESH,
PA_FADE, PA_BLINDS
};
uint8_t effectsCount = sizeof(allEffects)/sizeof(allEffects[0]);
// ========================================
void setup() {
Serial.begin(115200);
matrix.begin();
matrix.displayClear();
matrix.setIntensity(currentBrightness);
WiFi.begin(ssid, password);
matrix.displayText("WIFI...", PA_CENTER, 100, 2000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
timeClient.begin();
randomSeed(esp_random());
}
void loop() {
timeClient.update();
smoothAutoDimming();
showTimeWithSeconds();
showDay();
showDate();
}
// ===== FUNCTIONS =====
// ----- SMOOTH BRIGHTNESS FADE -----
void smoothAutoDimming() {
int hour = timeClient.getHours();
uint8_t target = (hour >= 22 || hour < 6) ? NIGHT_BRIGHTNESS : DAY_BRIGHTNESS;
if (currentBrightness < target) currentBrightness++;
if (currentBrightness > target) currentBrightness--;
matrix.setIntensity(currentBrightness);
}
// ----- RANDOM EFFECT PICKER -----
textEffect_t randomEffect() {
return allEffects[random(effectsCount)];
}
// ----- TIME + SMALLER SECONDS -----
void showTimeWithSeconds() {
static int lastMinute = -1;
static int lastSecond = -1;
int h = timeClient.getHours();
int m = timeClient.getMinutes();
int s = timeClient.getSeconds();
// Blink colon
if (millis() - lastBlink > 500) {
colonState = !colonState;
lastBlink = millis();
}
// MAIN TIME HH:MM (Top 3 modules)
sprintf(msg, "%02d%c%02d", h, colonState ? ':' : ' ', m);
if (m != lastMinute) {
matrix.setZone(0, 0, 2); // top 3 modules
matrix.displayClear();
matrix.displayText(msg, PA_LEFT, 100, 2000, randomEffect(), randomEffect());
while (!matrix.displayAnimate());
lastMinute = m;
}
// SMALL SECONDS at top-right (simulate smaller visually, bottom module)
if (s != lastSecond) {
sprintf(secMsg, "%02d", s);
matrix.setZone(1, 3, 3); // bottom module for seconds
matrix.displayClear();
matrix.displayText(secMsg, PA_RIGHT, 50, 0, randomEffect(), PA_NO_EFFECT);
while (!matrix.displayAnimate());
lastSecond = s;
}
// Reset full matrix
matrix.setZone(0, 0, MAX_DEVICES-1);
delay(200);
}
// ----- DAY -----
void showDay() {
sprintf(msg, "%s", days[timeClient.getDay()].c_str());
showCustom(msg);
}
// ----- DATE -----
void showDate() {
time_t epoch = timeClient.getEpochTime();
struct tm *tm = localtime(&epoch);
sprintf(msg, "%02d/%02d/%04d",
tm->tm_mday,
tm->tm_mon + 1,
tm->tm_year + 1900);
showCustom(msg);
}
// ----- DISPLAY HELPER -----
void showCustom(char* text) {
matrix.displayClear();
matrix.displayText(text, PA_CENTER, 100, 2000, randomEffect(), randomEffect());
while (!matrix.displayAnimate());
}