ALIVE SERVER MONITOR

Arduino Sketch - Server Status Dashboard

CAPTIVE PORTAL VERSION →

KEY FEATURES

>
ICMP Ping Monitoring Pings multiple servers at configurable intervals
>
Visual Status Display Round LCD shows server status with color coding
>
LED Alerts Blue = All OK, Red Flashing = Server Down
>
Auto WiFi Reconnect Automatically reconnects on connection loss

DISPLAY PREVIEW

BLUE (OK)
RED (ALERT)

TOGGLE SERVERS

Ready - Click RUN BOOT SEQUENCE

ARDUINO SKETCH

Alive.ino
/*
 * 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_SSID and YOUR_WIFI_PASSWORD with 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_INTERVAL to change how often servers are pinged (default: 30 seconds).
  • Libraries: Install via Arduino Library Manager:
    • Adafruit GFX Library
    • Adafruit GC9A01A
    • ESP32Ping by 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 ALIVE in your Arduino sketches directory (usually Documents/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

  1. □ Install Arduino IDE 3.x
  2. □ Add ESP32 board package URL and install ESP32 boards
  3. □ Install required libraries: Adafruit GFX, Adafruit GC9A01A, ESP32Ping
  4. □ Create project folder: ALIVE/
  5. □ Copy sketch as ALIVE.ino
  6. □ Edit code: Set WiFi SSID and password
  7. □ Edit code: Add your server IPs to servers[] array
  8. □ Edit code: Set friendly names in serverNames[] array
  9. □ Wire GC9A01 display to ESP32-S3
  10. □ Wire status LEDs with 330Ω resistors
  11. □ Connect ESP32-S3 via USB-C
  12. □ Select ESP32S3 Dev Module and correct COM port
  13. □ Upload sketch (hold BOOT if needed)
  14. □ Check Serial Monitor for WiFi connection
  15. □ Verify servers appear on display - Done!