/*
ESP32 NTP CLOCK - Web Dashboard Edition
Architecture: Non-Blocking State Machine + Web Server
Theme: Modern Glassmorphism
*/
// https://docs.wokwi.com/guides/esp32-wifi
#include <WiFi.h>
#include <WebServer.h>
#include <time.h>
// =====================================================
// CONFIG
// =====================================================
static const char* WIFI_SSID = "Wokwi-GUEST";
static const char* WIFI_PASS = "";
static const char* NTP_SERVER = "pool.ntp.org";
constexpr long UTC_OFFSET_SEC = 7 * 3600;
constexpr int DST_OFFSET_SEC = 0;
WebServer server(80);
// =====================================================
// DASHBOARD HTML (Glassmorphism)
// =====================================================
const char* htmlPage = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { background: #0f172a; color: white; font-family: 'Segoe UI', sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
.card { background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(15px); padding: 40px; border-radius: 20px; border: 1px solid rgba(255,255,255,0.1); text-align: center; box-shadow: 0 8px 32px rgba(0,0,0,0.3); }
h1 { font-size: 1rem; opacity: 0.6; margin-bottom: 10px; }
#time { font-size: 4rem; font-weight: bold; margin: 10px 0; color: #38bdf8; }
#date { font-size: 1.2rem; }
</style>
</head>
<body>
<div class="card">
<h1>ESP32 CLOCK</h1>
<div id="time">--:--:--</div>
<div id="date">Syncing...</div>
</div>
<script>
async function update() {
const res = await fetch('/data');
const data = await res.json();
document.getElementById('time').innerText = data.time;
document.getElementById('date').innerText = data.date;
}
setInterval(update, 1000);
update();
</script>
</body>
</html>
)rawliteral";
// =====================================================
// SYSTEM STATE & TIMERS
// =====================================================
enum class SystemState : uint8_t { BOOT, WIFI_CONNECTING, RUNNING };
SystemState currentState = SystemState::BOOT;
uint32_t nowMs = 0;
void handleData() {
struct tm timeinfo;
if(!getLocalTime(&timeinfo)) {
server.send(200, "application/json", "{\"time\":\"--:--:--\", \"date\":\"Error\"}");
return;
}
char timeStr[10], dateStr[15];
strftime(timeStr, sizeof(timeStr), "%H:%M:%S", &timeinfo);
strftime(dateStr, sizeof(dateStr), "%d/%m/%Y", &timeinfo);
String json = "{\"time\":\"" + String(timeStr) + "\", \"date\":\"" + String(dateStr) + "\"}";
server.send(200, "application/json", json);
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
server.on("/", []() { server.send(200, "text/html", htmlPage); });
server.on("/data", handleData);
}
void loop() {
nowMs = millis();
switch (currentState) {
case SystemState::BOOT:
if (WiFi.status() == WL_CONNECTED) {
configTime(UTC_OFFSET_SEC, DST_OFFSET_SEC, NTP_SERVER);
server.begin();
currentState = SystemState::RUNNING;
Serial.println("Server Started at: " + WiFi.localIP().toString());
}
break;
case SystemState::RUNNING:
server.handleClient(); // Non-blocking server loop
break;
}
}