FQRCREATIVES.ID
Published

NeonGlow: Standalone ESP32 Web-Based Smart Control Panel

An aesthetic, high-performance dark-mode web dashboard hosted directly inside an ESP32 to control room devices locally no external servers,

BeginnerFull instructions provided28
NeonGlow: Standalone ESP32 Web-Based Smart Control Panel

Things used in this project

Story

Read more

Schematics

ESP32_Local_Wiring_Diagram.png

Code

ESP32_Local_Web_Control.ino

C/C++
#include <WiFi.h>
#include <WebServer.h>

// Replace with your WiFi SSID and Password
const char* ssid = "*****";
const char* password = "*****";

WebServer server(80);

// 4 Pins for each LED/Relay. 
// You can change this according to the ESP32 GPIO pins you are using.
const int ledPins[4] = {2, 4, 16, 17}; 

void handleRoot() {
  String html = "<!DOCTYPE html><html>";
  html += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
  html += "<title>Smart Home Control</title>";
  html += "<style>";
  html += "body { background-color: #050505; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; min-height: 100vh; margin: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; color: white; padding: 40px 20px; box-sizing: border-box; }";
  
  // Headline styling
  html += "h1 { margin-bottom: 40px; letter-spacing: 2px; font-weight: 300; text-align: center; text-transform: uppercase; font-size: 24px; color: #fff; text-shadow: 0 0 10px rgba(255,255,255,0.2); }";
  
  html += ".grid { display: flex; flex-direction: column; align-items: center; gap: 20px; }";
  html += "@media (min-width: 768px) { .grid { flex-direction: row; justify-content: center; gap: 30px; } }";
  html += ".toggle-box { border: 1px solid #222; border-radius: 15px; padding: 25px 50px; display: flex; flex-direction: column; align-items: center; background: #0a0a0a; box-shadow: 0 4px 15px rgba(0,0,0,0.8); }";
  html += ".icon { margin-bottom: 20px; transition: .4s; }";
  html += ".icon svg { width: 35px; height: 35px; stroke: #555; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: .4s; }";
  
  // Specific colors for each icon when active
  html += ".magenta.active svg { stroke: #ff00ff; filter: drop-shadow(0 0 8px #ff00ff); }";
  html += ".orange.active svg { stroke: #ff8c00; filter: drop-shadow(0 0 8px #ff8c00); }";
  html += ".green.active svg { stroke: #66ff00; filter: drop-shadow(0 0 8px #66ff00); }";
  html += ".cyan.active svg { stroke: #00ffff; filter: drop-shadow(0 0 8px #00ffff); }";
  
  // Base styling for toggle
  html += ".switch { position: relative; display: inline-block; width: 90px; height: 45px; }";
  html += ".switch input { opacity: 0; width: 0; height: 0; }";
  html += ".slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: transparent; border: 2px solid #333; transition: .4s; border-radius: 45px; box-shadow: inset 0 0 5px rgba(0,0,0,0.5); }";
  html += ".slider:before { position: absolute; content: \"\"; height: 35px; width: 35px; left: 3px; bottom: 3px; background-color: #fff; transition: .4s; border-radius: 50%; box-shadow: 0 0 5px rgba(255,255,255,0.3); }";
  
  // Slider right movement
  html += "input:checked + .slider { border-color: transparent; }";
  html += "input:checked + .slider:before { transform: translateX(45px); box-shadow: 0 0 10px #fff; }";
  
  // Colorful glow for each toggle
  html += ".switch.magenta input:checked + .slider { background-color: #ff00ff; box-shadow: 0 0 15px #ff00ff, 0 0 30px #ff00ff; }";
  html += ".switch.orange input:checked + .slider { background-color: #ff8c00; box-shadow: 0 0 15px #ff8c00, 0 0 30px #ff8c00; }";
  html += ".switch.green input:checked + .slider { background-color: #66ff00; box-shadow: 0 0 15px #66ff00, 0 0 30px #66ff00; }";
  html += ".switch.cyan input:checked + .slider { background-color: #00ffff; box-shadow: 0 0 15px #00ffff, 0 0 30px #00ffff; }";
  
  html += "</style></head>";
  html += "<body>";
  
  // Headline
  html += "<h1>ESP32 Control Panel</h1>";
  
  html += "<div class=\"grid\">";
  
  String colors[] = {"magenta", "orange", "green", "cyan"};
  String icons[] = {
    "<svg viewBox=\"0 0 24 24\"><rect x=\"2\" y=\"6\" width=\"20\" height=\"12\" rx=\"2\" ry=\"2\"></rect><line x1=\"6\" y1=\"12\" x2=\"10\" y2=\"12\"></line><line x1=\"8\" y1=\"10\" x2=\"8\" y2=\"14\"></line><line x1=\"15\" y1=\"13\" x2=\"15.01\" y2=\"13\"></line><line x1=\"18\" y1=\"11\" x2=\"18.01\" y2=\"11\"></line></svg>", // Gamepad (Magenta)
    "<svg viewBox=\"0 0 24 24\"><rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\" ry=\"2\"></rect><line x1=\"8\" y1=\"21\" x2=\"16\" y2=\"21\"></line><line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"21\"></line></svg>", // Monitor (Orange)
    "<svg viewBox=\"0 0 24 24\"><path d=\"M18.36 6.64a9 9 0 1 1-12.73 0\"></path><line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"12\"></line></svg>", // Power/Light (Green)
    "<svg viewBox=\"0 0 24 24\"><path d=\"M9 18V5l12-2v13\"></path><circle cx=\"6\" cy=\"18\" r=\"3\"></circle><circle cx=\"18\" cy=\"16\" r=\"3\"></circle></svg>" // Music (Cyan)
  };
  
  for(int i=0; i<4; i++) {
    bool state = digitalRead(ledPins[i]);
    html += "<div class=\"toggle-box\">";
    html += "<div class=\"icon " + colors[i] + (state ? " active" : "") + "\" id=\"icon" + String(i) + "\">" + icons[i] + "</div>";
    html += "<label class=\"switch " + colors[i] + "\">";
    html += "<input type=\"checkbox\" onchange=\"toggleLED(" + String(i) + ", this)\" " + (state ? "checked" : "") + ">";
    html += "<span class=\"slider\"></span>";
    html += "</label>";
    html += "</div>";
  }
  
  html += "</div>";
  
  html += "<script>";
  html += "function toggleLED(id, cb) {";
  html += "  fetch('/led?id=' + id + '&state=' + (cb.checked ? 'on' : 'off'));";
  html += "  var icon = document.getElementById('icon' + id);";
  html += "  if(cb.checked) { icon.classList.add('active'); }";
  html += "  else { icon.classList.remove('active'); }";
  html += "}";
  html += "</script>";
  html += "</body></html>";
  
  server.send(200, "text/html", html);
}

// Dynamic endpoint to handle 4 LEDs
void handleLed() {
  if (server.hasArg("id") && server.hasArg("state")) {
    int id = server.arg("id").toInt();
    String state = server.arg("state");
    
    // Validate that ID is in the 0-3 range
    if (id >= 0 && id < 4) {
      if (state == "on") {
        digitalWrite(ledPins[id], HIGH);
      } else {
        digitalWrite(ledPins[id], LOW);
      }
      server.send(200, "text/plain", "OK");
      return;
    }
  }
  server.send(400, "text/plain", "Bad Request");
}

void setup() {
  Serial.begin(115200);
  
  // Set all LED pins as OUTPUT and turn them off initially
  for(int i=0; i<4; i++) {
    pinMode(ledPins[i], OUTPUT);
    digitalWrite(ledPins[i], LOW);
  }

  // Start WiFi connection
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Set URL routing
  server.on("/", handleRoot);
  server.on("/led", handleLed); // Single endpoint logic
  
  // Start server
  server.begin();
  Serial.println("HTTP server started");
}

void loop() {
  server.handleClient();
}

Credits

FQRCREATIVES.ID
1 project • 0 followers
Full-stack developer & hardware enthusiast. I enjoy blending embedded systems, PCB design, and web tech to create functional IoT solutions.

Comments