ALIVE SERVER MONITOR
Arduino Sketch - Server Status Dashboard
KEY FEATURES
DISPLAY PREVIEW
TOGGLE SERVERS
ARDUINO SKETCH
/* * Server Status Dashboard - "ALIVE" * ESP32-S3 Super Mini + GC9A01 1.28" Round LCD + Status LEDs * * Blue LED: Always ON (power indicator) * Red LED: Flashing when any server is down */ #include <WiFi.h> #include <ESP32Ping.h> #include <Adafruit_GFX.h> #include <Adafruit_GC9A01A.h> // ============== CONFIGURATION ============== // WiFi credentials const char* WIFI_SSID = "YOUR_WIFI_SSID"; const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD"; // Server list - add your servers here (IP addresses or hostnames) const char* servers[] = { "192.168.1.10", // Server 1 "192.168.1.11", // Server 2 "192.168.1.12", // Server 3 "192.168.1.13", // Server 4 "192.168.1.14", // Server 5 "google.com" // External check (optional) }; const int NUM_SERVERS = sizeof(servers) / sizeof(servers[0]); // Optional: Friendly names for display const char* serverNames[] = { "Web Server", "Database", "File Server", "Backup", "Mail Server", "Internet" }; // Check interval (milliseconds) const unsigned long CHECK_INTERVAL = 30000; // 30 seconds // ============== PIN DEFINITIONS ============== // ESP32-S3 Super Mini - matching WeatherClock pinout // SPI for GC9A01 Display #define TFT_CS 12 // Chip Select #define TFT_DC 11 // Data/Command #define TFT_RST 10 // Reset #define TFT_MOSI 9 // SPI Data #define TFT_SCLK 8 // SPI Clock #define TFT_BL 13 // Backlight // Status LEDs #define LED_BLUE 5 #define LED_RED 4 // ============== DISPLAY SETTINGS ============== #define SCREEN_WIDTH 240 #define SCREEN_HEIGHT 240 #define CX (SCREEN_WIDTH / 2) // Center X #define CY (SCREEN_HEIGHT / 2) // Center Y // Cyberpunk Color definitions (RGB565) #define COLOR_BG 0x0821 // Dark blue-black (#0a0a1a) #define COLOR_TEXT 0xB5B6 // Light gray (#b0b0b0) #define COLOR_CYAN 0x07FF // Neon Cyan (#00ffff) #define COLOR_MAGENTA 0xF81F // Neon Magenta (#ff00ff) #define COLOR_YELLOW 0xFFE0 // Neon Yellow (#ffff00) #define COLOR_GREEN 0x07E0 // Neon Green (#00ff00) #define COLOR_RED 0xF800 // Red (#ff0000) #define COLOR_HEADER 0x07FF // Neon Cyan header #define COLOR_RING_OK 0x07FF // Cyan (all OK) #define COLOR_RING_BAD 0xF81F // Magenta (problems) // Create display object Adafruit_GC9A01A tft(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST); // ============== STATUS TRACKING ============== bool serverStatus[NUM_SERVERS]; bool allServersUp = true; unsigned long lastCheck = 0; unsigned long lastBlink = 0; unsigned long lastFlashUpdate = 0; bool redLedState = false; bool flashState = false; // ============== SETUP ============== void setup() { Serial.begin(115200); delay(1000); Serial.println("\n=== ALIVE - Server Status Dashboard ==="); // Initialize LED pins pinMode(LED_BLUE, OUTPUT); pinMode(LED_RED, OUTPUT); digitalWrite(LED_BLUE, LOW); digitalWrite(LED_RED, LOW); // Initialize backlight pinMode(TFT_BL, OUTPUT); digitalWrite(TFT_BL, HIGH); // Initialize display tft.begin(); tft.setRotation(0); tft.fillScreen(COLOR_BG); // Show startup screen drawStartupScreen(); // Connect to WiFi connectWiFi(); // Show checking screen and do initial check drawCheckingScreen(); checkAllServers(); updateDisplay(); lastCheck = millis(); lastFlashUpdate = millis(); flashState = true; } // ============== MAIN LOOP ============== void loop() { // Maintain WiFi connection if (WiFi.status() != WL_CONNECTED) { connectWiFi(); } // Periodic server check if (millis() - lastCheck >= CHECK_INTERVAL) { checkAllServers(); updateDisplay(); lastCheck = millis(); lastFlashUpdate = millis(); // Reset flash timing flashState = true; // Start with text visible } // Flash DOWN text only (not whole screen) if (!allServersUp && millis() - lastFlashUpdate >= 300) { flashState = !flashState; flashDownText(flashState); lastFlashUpdate = millis(); } // Handle LED states updateLEDs(); delay(50); } // ============== FLASH DOWN TEXT ONLY ============== void flashDownText(bool showText) { int startY = 50; int lineHeight = 26; int maxVisible = 6; tft.setTextSize(1); for (int i = 0; i < NUM_SERVERS && i < maxVisible; i++) { if (!serverStatus[i]) { int yPos = startY + (i * lineHeight); // Clear the text area tft.fillRect(170, yPos + 1, 30, 10, COLOR_BG); if (showText) { tft.setTextColor(COLOR_RED); tft.setCursor(170, yPos + 3); tft.print("DOWN"); } } } } // ============== DRAW GLOWING RING ============== void drawGlowRing(int cx, int cy, int r, uint16_t color) { // Draw thick ring for glow effect tft.drawCircle(cx, cy, r, color); tft.drawCircle(cx, cy, r - 1, color); } // ============== STARTUP SCREEN ============== void drawStartupScreen() { tft.fillScreen(COLOR_BG); // Draw outer ring drawGlowRing(CX, CY, 115, COLOR_CYAN); // Title tft.setTextSize(3); tft.setTextColor(COLOR_CYAN); int16_t x1, y1; uint16_t w, h; tft.getTextBounds("ALIVE", 0, 0, &x1, &y1, &w, &h); tft.setCursor(CX - w/2, 25); tft.print("ALIVE"); // Static center text tft.setTextSize(1); tft.setTextColor(COLOR_TEXT); tft.getTextBounds("SERVER MONITOR", 0, 0, &x1, &y1, &w, &h); int textX = CX - w/2; tft.setCursor(textX, 195); tft.print("SERVER MONITOR"); // Triangular scan animation - 0.75 seconds int triR = 45; int blinkState = 0; for (int frame = 0; frame < 25; frame++) { float angle = frame * 0.3; // Calculate 3 points of rotating triangle for (int p = 0; p < 3; p++) { float a1 = angle + (p * 2.094); // 120 degrees apart float a2 = angle + ((p + 1) * 2.094); int x1t = CX + cos(a1) * triR; int y1t = CY + sin(a1) * triR; int x2t = CX + cos(a2) * triR; int y2t = CY + sin(a2) * triR; tft.drawLine(x1t, y1t, x2t, y2t, COLOR_CYAN); } // Blinking chevrons - every 3 frames if (frame % 3 == 0) { // Clear chevron area tft.fillRect(textX - 22, 193, 20, 12, COLOR_BG); tft.fillRect(textX + w + 2, 193, 20, 12, COLOR_BG); tft.setTextColor(COLOR_CYAN); if (blinkState == 0) { // Outer chevrons tft.setCursor(textX - 20, 195); tft.print(">>"); tft.setCursor(textX + w + 4, 195); tft.print("<<"); } else { // Inner chevrons tft.setCursor(textX - 14, 195); tft.print(">"); tft.setCursor(textX + w + 8, 195); tft.print("<"); } blinkState = 1 - blinkState; } delay(30); // Clear triangle for (int p = 0; p < 3; p++) { float a1 = angle + (p * 2.094); float a2 = angle + ((p + 1) * 2.094); int x1t = CX + cos(a1) * triR; int y1t = CY + sin(a1) * triR; int x2t = CX + cos(a2) * triR; int y2t = CY + sin(a2) * triR; tft.drawLine(x1t, y1t, x2t, y2t, COLOR_BG); } } } // ============== WiFi CONNECTION ============== void connectWiFi() { tft.fillScreen(COLOR_BG); // Draw connecting screen drawGlowRing(CX, CY, 115, COLOR_YELLOW); tft.setTextSize(2); tft.setTextColor(COLOR_YELLOW); int16_t x1, y1; uint16_t w, h; tft.getTextBounds("WIFI", 0, 0, &x1, &y1, &w, &h); tft.setCursor(CX - w/2, CY - 40); tft.print("WIFI"); tft.setTextSize(1); tft.setTextColor(COLOR_TEXT); tft.getTextBounds("Connecting to:", 0, 0, &x1, &y1, &w, &h); tft.setCursor(CX - w/2, CY - 10); tft.print("Connecting to:"); tft.setTextColor(COLOR_CYAN); tft.getTextBounds(WIFI_SSID, 0, 0, &x1, &y1, &w, &h); tft.setCursor(CX - w/2, CY + 10); tft.print(WIFI_SSID); WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // Progress bar setup int barX = 40; int barY = CY + 35; int barW = 160; int barH = 12; int progress = 0; // Draw bar outline tft.drawRect(barX, barY, barW, barH, COLOR_CYAN); int attempts = 0; while (WiFi.status() != WL_CONNECTED && attempts < 30) { delay(50); Serial.print("."); attempts++; // Update progress (max 56% while connecting) progress = (attempts * 56) / 30; int fillW = (barW - 4) * progress / 100; tft.fillRect(barX + 2, barY + 2, fillW, barH - 4, COLOR_CYAN); // Show percentage tft.fillRect(CX - 15, barY + 18, 35, 12, COLOR_BG); tft.setTextSize(1); tft.setTextColor(COLOR_CYAN); tft.setCursor(CX - 12, barY + 20); tft.print(progress); tft.print("%"); } if (WiFi.status() == WL_CONNECTED) { Serial.println("\nWiFi connected!"); Serial.print("IP: "); Serial.println(WiFi.localIP()); tft.fillScreen(COLOR_BG); drawGlowRing(CX, CY, 115, COLOR_CYAN); // Glitch effect for CONNECTED text tft.setTextSize(2); tft.getTextBounds("CONNECTED", 0, 0, &x1, &y1, &w, &h); int connX = CX - w/2; int connY = CY - 25; for (int g = 0; g < 3; g++) { // Random offset for glitch int offX = random(-4, 5); int offY = random(-2, 3); uint16_t glitchColor = random(2) ? COLOR_CYAN : COLOR_MAGENTA; tft.fillRect(connX - 5, connY - 5, w + 10, h + 10, COLOR_BG); tft.setTextColor(glitchColor); tft.setCursor(connX + offX, connY + offY); tft.print("CONNECTED"); delay(20); } // Final stable text tft.fillRect(connX - 5, connY - 5, w + 10, h + 10, COLOR_BG); tft.setTextColor(COLOR_CYAN); tft.setCursor(connX, connY); tft.print("CONNECTED"); // Data stream around IP String ip = WiFi.localIP().toString(); tft.setTextSize(1); for (int d = 0; d < 4; d++) { // Random hex stream tft.setTextColor(0x0320); // Dim green for (int r = 0; r < 6; r++) { int rx = random(50, 190); int ry = CY + random(5, 50); tft.setCursor(rx, ry); tft.print(random(0, 16), HEX); } delay(30); } // Clear stream area and show IP tft.fillRect(40, CY + 5, 160, 50, COLOR_BG); tft.setTextColor(COLOR_GREEN); tft.getTextBounds(ip.c_str(), 0, 0, &x1, &y1, &w, &h); tft.setCursor(CX - w/2, CY + 15); tft.print(ip); delay(400); } else { Serial.println("\nWiFi failed!"); tft.fillScreen(COLOR_BG); drawGlowRing(CX, CY, 115, COLOR_MAGENTA); tft.setTextSize(2); tft.setTextColor(COLOR_MAGENTA); tft.getTextBounds("WIFI FAILED", 0, 0, &x1, &y1, &w, &h); tft.setCursor(CX - w/2, CY - 10); tft.print("WIFI FAILED"); delay(3000); } } // ============== CHECKING SERVER STATUS SCREEN ============== void drawCheckingScreen() { tft.fillScreen(COLOR_BG); drawGlowRing(CX, CY, 115, COLOR_MAGENTA); // Title tft.setTextSize(2); tft.setTextColor(COLOR_MAGENTA); int16_t x1, y1; uint16_t w, h; tft.getTextBounds("CHECKING", 0, 0, &x1, &y1, &w, &h); tft.setCursor(CX - w/2, CY - 30); tft.print("CHECKING"); tft.setTextSize(1); tft.setTextColor(COLOR_TEXT); tft.getTextBounds("Server Status", 0, 0, &x1, &y1, &w, &h); tft.setCursor(CX - w/2, CY); tft.print("Server Status"); // Animated dot box int boxX = CX - 40; int boxY = CY + 15; int boxW = 80; int boxH = 30; int dotSpacing = 8; // Top edge for (int x = boxX; x <= boxX + boxW; x += dotSpacing) { tft.fillCircle(x, boxY, 2, COLOR_CYAN); delay(20); } // Right edge for (int y = boxY; y <= boxY + boxH; y += dotSpacing) { tft.fillCircle(boxX + boxW, y, 2, COLOR_CYAN); delay(20); } // Bottom edge for (int x = boxX + boxW; x >= boxX; x -= dotSpacing) { tft.fillCircle(x, boxY + boxH, 2, COLOR_CYAN); delay(20); } // Left edge for (int y = boxY + boxH; y >= boxY; y -= dotSpacing) { tft.fillCircle(boxX, y, 2, COLOR_CYAN); delay(20); } } // ============== CHECK ALL SERVERS ============== void checkAllServers() { Serial.println("\n--- Checking servers ---"); allServersUp = true; for (int i = 0; i < NUM_SERVERS; i++) { Serial.print("Pinging "); Serial.print(servers[i]); Serial.print("... "); bool success = Ping.ping(servers[i], 2); serverStatus[i] = success; if (success) { Serial.println("OK"); } else { Serial.println("FAILED"); allServersUp = false; } } Serial.print("All servers up: "); Serial.println(allServersUp ? "YES" : "NO"); } // ============== UPDATE DISPLAY ============== void updateDisplay() { tft.fillScreen(COLOR_BG); // Draw outer status ring uint16_t ringColor = allServersUp ? COLOR_RING_OK : COLOR_RING_BAD; drawGlowRing(CX, CY, 115, ringColor); // Draw header tft.setTextSize(2); tft.setTextColor(COLOR_CYAN); int16_t x1, y1; uint16_t w, h; tft.getTextBounds("ALIVE", 0, 0, &x1, &y1, &w, &h); tft.setCursor(CX - w/2, 23); tft.print("ALIVE"); // Draw server status list int startY = 50; int lineHeight = 26; int maxVisible = 6; tft.setTextSize(1); for (int i = 0; i < NUM_SERVERS && i < maxVisible; i++) { int yPos = startY + (i * lineHeight); // Status indicator circle if (serverStatus[i]) { tft.fillCircle(45, yPos + 6, 6, COLOR_CYAN); } else { tft.fillCircle(45, yPos + 6, 6, COLOR_MAGENTA); tft.drawLine(42, yPos + 3, 48, yPos + 9, COLOR_BG); tft.drawLine(48, yPos + 3, 42, yPos + 9, COLOR_BG); } // Server name tft.setTextColor(COLOR_TEXT); tft.setCursor(58, yPos + 3); String name = String(serverNames[i]); if (name.length() > 12) { name = name.substring(0, 11) + "."; } tft.print(name); // Status text tft.setCursor(170, yPos + 3); if (serverStatus[i]) { tft.setTextColor(COLOR_GREEN); tft.print("OK"); } else { tft.setTextColor(COLOR_RED); tft.print("DOWN"); } } } // ============== UPDATE LEDs ============== void updateLEDs() { // Blue LED always on (power indicator) digitalWrite(LED_BLUE, HIGH); if (allServersUp) { // All OK: Red off digitalWrite(LED_RED, LOW); redLedState = false; } else { // Problem: Red flashing if (millis() - lastBlink >= 500) { redLedState = !redLedState; digitalWrite(LED_RED, redLedState ? HIGH : LOW); lastBlink = millis(); } } }
SETUP INSTRUCTIONS
- WiFi: Replace
YOUR_WIFI_SSIDandYOUR_WIFI_PASSWORDwith your WiFi credentials. - Servers: Edit the
servers[]array to add your server IP addresses or hostnames (up to 5 visible on display). - Server Names: Edit the
serverNames[]array to give friendly names to each server. - Check Interval: Modify
CHECK_INTERVALto change how often servers are pinged (default: 30 seconds). - Libraries: Install via Arduino Library Manager:
Adafruit GFX LibraryAdafruit GC9A01AESP32Pingby Matteo Dainese
- Board: Select "ESP32S3 Dev Module" in Arduino IDE.
- LEDs: Use 330 ohm resistors if using bare LEDs. If using LEDs with built-in resistors, connect directly.
- Display: Same pinout as WeatherClock project for easy hardware swap.
DISPLAY ROTATION
To rotate the display, add tft.setRotation(n); in setup() after tft.begin();
tft.setRotation(0); // 0° - Default orientation
tft.setRotation(1); // 90° - Rotate clockwise
tft.setRotation(2); // 180° - Upside down
tft.setRotation(3); // 270° - Rotate counter-clockwise
Note: This rotates the entire display output. Use this if your display is mounted upside down or at an angle.
ARDUINO PROJECT SETUP
- Create Project Folder: Create a folder named
ALIVEin your Arduino sketches directory (usuallyDocuments/Arduino/) - Main Sketch: Save the code as
ALIVE.ino- filename MUST match folder name - Single File Project: This project uses only one .ino file - no additional .h files required
ALIVE/
└── ALIVE.ino // Complete sketch (copy the code above)
COMPONENTS NEEDED
| ESP32-S3 Super Mini | Main microcontroller | AliExpress |
| GC9A01 Round Display | 240x240 1.28" TFT | Amazon |
| Blue LED (5mm) | Status indicator - all OK | Amazon |
| Red LED (5mm) | Status indicator - server down | Amazon |
| 330Ω Resistors (x2) | Current limiting for LEDs | Amazon |
| Dupont Wires | For connections | Amazon |
WIRING DIAGRAM
| GC9A01 Display | ESP32-S3 |
|---|---|
| VCC | 3.3V |
| GND | GND |
| SCL (SCK) | GPIO 8 |
| SDA (MOSI) | GPIO 9 |
| RES (RST) | GPIO 10 |
| DC | GPIO 11 |
| CS | GPIO 12 |
| BLK (Backlight) | GPIO 13 |
| Status LEDs | ESP32-S3 |
|---|---|
| Blue LED (+) → 330Ω | GPIO 5 |
| Red LED (+) → 330Ω | GPIO 4 |
| Both LED (-) | GND |
REQUIRED LIBRARIES
Install via Arduino Library Manager (Sketch → Include Library → Manage Libraries):
| Adafruit GFX Library | Graphics primitives |
| Adafruit GC9A01A | Round display driver |
| ESP32Ping | by Matteo Craciunescu - ICMP ping |
Note: ESP32Ping may need manual install from GitHub if not found in Library Manager.
ARDUINO IDE 3.x BOARD SETTINGS
| Board | ESP32S3 Dev Module |
| USB CDC On Boot | Enabled |
| Flash Size | 4MB (32Mb) |
| Partition Scheme | Default 4MB with spiffs |
| Upload Speed | 921600 |
ESP32 Board URL: Add this in File → Preferences → Additional Board Manager URLs:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
TROUBLESHOOTING
- Upload fails / Guru Meditation Error: Hold BOOT button while clicking Upload, release after "Connecting..." appears.
- Display is white/blank: Check wiring, especially CS, DC, and RST pins. Verify 3.3V power.
- WiFi won't connect: Double-check SSID and password. ESP32 only supports 2.4GHz networks.
- All servers show DOWN: Verify server IPs are correct and reachable. Check if firewall blocks ICMP ping.
- LEDs not working: Check LED polarity (long leg = positive). Verify 330Ω resistor is connected.
- Serial Monitor empty: Set baud rate to 115200. Enable "USB CDC On Boot" in board settings.
- ESP32Ping library not found: Install manually from GitHub
BUILD CHECKLIST
- □ Install Arduino IDE 3.x
- □ Add ESP32 board package URL and install ESP32 boards
- □ Install required libraries: Adafruit GFX, Adafruit GC9A01A, ESP32Ping
- □ Create project folder:
ALIVE/ - □ Copy sketch as
ALIVE.ino - □ Edit code: Set WiFi SSID and password
- □ Edit code: Add your server IPs to
servers[]array - □ Edit code: Set friendly names in
serverNames[]array - □ Wire GC9A01 display to ESP32-S3
- □ Wire status LEDs with 330Ω resistors
- □ Connect ESP32-S3 via USB-C
- □ Select ESP32S3 Dev Module and correct COM port
- □ Upload sketch (hold BOOT if needed)
- □ Check Serial Monitor for WiFi connection
- □ Verify servers appear on display - Done!