ESP32 Web Server for Remote Device Control

 

3. ESP32 Web Server for Remote Device Control

Project Overview

Create a custom web interface hosted on ESP32 to control devices from any browser on your network.

Key Features

  • Responsive web interface
  • Real-time control
  • No internet dependency (local network)
  • Customizable UI
  • API support

Components Required



  • ESP32 DevKit
  • LEDs/Relays for testing
  • Smartphone/Computer
  • Router for Wi-Fi
Diagram.json:

{
  "version": 1,
  "author": "ESP32 Web Server Control",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-esp32-devkit-v1", "id": "esp", "top": 0, "left": 0, "attrs": {} },
    {
      "type": "wokwi-led",
      "id": "led1",
      "top": -124.8,
      "left": 230.4,
      "attrs": { "color": "yellow", "lightColor": "yellow", "label": "Living Room" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r1",
      "top": -67.2,
      "left": 230.4,
      "rotate": 90,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-led",
      "id": "led2",
      "top": -124.8,
      "left": 307.2,
      "attrs": { "color": "white", "lightColor": "white", "label": "Bedroom" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r2",
      "top": -67.2,
      "left": 307.2,
      "rotate": 90,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-led",
      "id": "led3",
      "top": -124.8,
      "left": 384,
      "attrs": { "color": "blue", "lightColor": "blue", "label": "Fan" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r3",
      "top": -67.2,
      "left": 384,
      "rotate": 90,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-led",
      "id": "led4",
      "top": -124.8,
      "left": 460.8,
      "attrs": { "color": "red", "lightColor": "orange", "label": "Heater" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r4",
      "top": -67.2,
      "left": 460.8,
      "rotate": 90,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-led",
      "id": "led5",
      "top": -124.8,
      "left": 537.6,
      "attrs": { "color": "green", "lightColor": "green", "label": "Door Lock" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r5",
      "top": -67.2,
      "left": 537.6,
      "rotate": 90,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-dht22",
      "id": "dht1",
      "top": -86.4,
      "left": 105.6,
      "attrs": { "temperature": "25", "humidity": "55" }
    },
    {
      "type": "wokwi-ssd1306",
      "id": "oled1",
      "top": 141.1,
      "left": 289.9,
      "attrs": { "i2cAddress": "0x3C" }
    },
    {
      "type": "wokwi-buzzer",
      "id": "bz1",
      "top": 96,
      "left": 518.4,
      "attrs": { "volume": "0.3" }
    }
  ],
  "connections": [
    [ "esp:TX0", "$serialMonitor:RX", "", [] ],
    [ "esp:RX0", "$serialMonitor:TX", "", [] ],
    [ "led1:A", "esp:D26", "yellow", [ "v0" ] ],
    [ "led1:C", "r1:1", "yellow", [ "v0" ] ],
    [ "r1:2", "esp:GND.1", "black", [ "v0" ] ],
    [ "led2:A", "esp:D27", "white", [ "v0" ] ],
    [ "led2:C", "r2:1", "white", [ "v0" ] ],
    [ "r2:2", "esp:GND.1", "black", [ "v0" ] ],
    [ "led3:A", "esp:D14", "blue", [ "v0" ] ],
    [ "led3:C", "r3:1", "blue", [ "v0" ] ],
    [ "r3:2", "esp:GND.1", "black", [ "v0" ] ],
    [ "led4:A", "esp:D12", "red", [ "v0" ] ],
    [ "led4:C", "r4:1", "red", [ "v0" ] ],
    [ "r4:2", "esp:GND.1", "black", [ "v0" ] ],
    [ "led5:A", "esp:D13", "green", [ "v0" ] ],
    [ "led5:C", "r5:1", "green", [ "v0" ] ],
    [ "r5:2", "esp:GND.1", "black", [ "v0" ] ],
    [ "dht1:VCC", "esp:3V3", "red", [ "v0" ] ],
    [ "dht1:GND", "esp:GND.1", "black", [ "v0" ] ],
    [ "dht1:SDA", "esp:D15", "green", [ "v0" ] ],
    [ "oled1:VCC", "esp:3V3", "red", [ "h0" ] ],
    [ "oled1:GND", "esp:GND.2", "black", [ "h0" ] ],
    [ "oled1:SDA", "esp:D21", "blue", [ "h0" ] ],
    [ "oled1:SCL", "esp:D22", "yellow", [ "h0" ] ],
    [ "bz1:1", "esp:D25", "purple", [ "v0" ] ],
    [ "bz1:2", "esp:GND.2", "black", [ "v0" ] ]
  ],
  "dependencies": {}
}

Applications

  • Laboratory equipment control
  • Workshop automation
  • Smart office systems
  • Educational demonstrations

Difficulty Level

Beginner - Great starting project

Code:

/*
 * ESP32 Web Server for Remote Device Control
 *
 * Features:
 * - Control 4 different devices (Lights, Fan, Door Lock, Heater)
 * - Real-time status updates
 * - Beautiful responsive web interface
 * - Slider controls for dimmable devices
 * - Toggle switches for on/off devices
 * - Scheduling support
 * - Password protection
 * - Mobile-friendly design
 * - OLED display for status
 * - Temperature monitoring
 */

#include <WiFi.h>
#include <WebServer.h>
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// WiFi Credentials
const char* ssid = "Wokwi-GUEST";
const char* password = "";

// Web Server
WebServer server(80);

// Authentication
const char* webUsername = "admin";
const char* webPassword = "1234";
bool isAuthenticated = false;

// Pin Definitions
#define LED1_PIN 26      // Living Room Light
#define LED2_PIN 27      // Bedroom Light
#define FAN_PIN 14       // Fan (PWM)
#define HEATER_PIN 12    // Heater
#define DOOR_LOCK_PIN 13 // Door Lock Servo/Relay
#define DHT_PIN 15       // Temperature Sensor
#define BUZZER_PIN 25    // Alert Buzzer

// PWM Settings
#define PWM_FREQ 5000
#define PWM_RESOLUTION 8

// DHT Sensor
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);

// OLED Display
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Device States
struct DeviceState {
  bool livingRoomLight;
  bool bedroomLight;
  bool doorLock;
  bool heater;
  int fanSpeed;        // 0-100
  float temperature;
  float humidity;
  unsigned long lastUpdate;
};

DeviceState devices;

// Schedule Structure
struct Schedule {
  bool enabled;
  int hour;
  int minute;
  String device;
  bool action;
};

Schedule schedules[5];
int scheduleCount = 0;

// Timing
unsigned long lastSensorRead = 0;
const long sensorInterval = 2000;

void setup() {
  Serial.begin(115200);
 
  // Initialize pins
  pinMode(LED1_PIN, OUTPUT);
  pinMode(LED2_PIN, OUTPUT);
  pinMode(HEATER_PIN, OUTPUT);
  pinMode(DOOR_LOCK_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
 
  // Setup PWM for fan (compatible with ESP32 Arduino Core 3.x)
  ledcAttach(FAN_PIN, PWM_FREQ, PWM_RESOLUTION);
 
  // Initialize DHT
  dht.begin();
 
  // Initialize OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
  } else {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0);
    display.println("Remote Control");
    display.println("Starting...");
    display.display();
    delay(2000);
  }
 
  // Initialize device states
  devices.livingRoomLight = false;
  devices.bedroomLight = false;
  devices.doorLock = true;  // Locked by default
  devices.heater = false;
  devices.fanSpeed = 0;
  devices.temperature = 0;
  devices.humidity = 0;
 
  // Apply initial states
  applyDeviceStates();
 
  // Connect to WiFi
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
 
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println("Connecting WiFi...");
  display.display();
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("\nWiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
 
  // Setup web server routes
  server.on("/", handleRoot);
  server.on("/login", handleLogin);
  server.on("/logout", handleLogout);
  server.on("/control", handleControl);
  server.on("/status", handleStatus);
  server.on("/living-room/on", []() { handleDeviceControl("living-room", true); });
  server.on("/living-room/off", []() { handleDeviceControl("living-room", false); });
  server.on("/bedroom/on", []() { handleDeviceControl("bedroom", true); });
  server.on("/bedroom/off", []() { handleDeviceControl("bedroom", false); });
  server.on("/fan/set", handleFanControl);
  server.on("/heater/on", []() { handleDeviceControl("heater", true); });
  server.on("/heater/off", []() { handleDeviceControl("heater", false); });
  server.on("/door/lock", []() { handleDoorControl(true); });
  server.on("/door/unlock", []() { handleDoorControl(false); });
  server.on("/all/on", handleAllOn);
  server.on("/all/off", handleAllOff);
 
  server.begin();
  Serial.println("HTTP server started");
  Serial.println("\n=== Remote Control System Ready ===");
  Serial.println("Access the control panel at:");
  Serial.print("http://");
  Serial.println(WiFi.localIP());
  Serial.println("Username: admin");
  Serial.println("Password: 1234");
  Serial.println("=====================================\n");
 
  updateDisplay();
  playStartupSound();
}

void loop() {
  server.handleClient();
 
  // Read sensors periodically
  unsigned long currentMillis = millis();
  if (currentMillis - lastSensorRead >= sensorInterval) {
    lastSensorRead = currentMillis;
    readSensors();
    updateDisplay();
  }
}

void readSensors() {
  devices.temperature = dht.readTemperature();
  devices.humidity = dht.readHumidity();
 
  if (isnan(devices.temperature) || isnan(devices.humidity)) {
    devices.temperature = 0;
    devices.humidity = 0;
  }
 
  devices.lastUpdate = millis();
}

void applyDeviceStates() {
  digitalWrite(LED1_PIN, devices.livingRoomLight ? HIGH : LOW);
  digitalWrite(LED2_PIN, devices.bedroomLight ? HIGH : LOW);
  digitalWrite(HEATER_PIN, devices.heater ? HIGH : LOW);
  digitalWrite(DOOR_LOCK_PIN, devices.doorLock ? HIGH : LOW);
  ledcWrite(FAN_PIN, map(devices.fanSpeed, 0, 100, 0, 255));
}

void updateDisplay() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
 
  display.println("SMART HOME");
  display.drawLine(0, 9, 128, 9, SSD1306_WHITE);
 
  // Temperature
  display.setCursor(0, 12);
  display.print("Temp: ");
  display.print(devices.temperature, 1);
  display.println("C");
 
  // Device Status
  display.setCursor(0, 22);
  display.print("Living: ");
  display.println(devices.livingRoomLight ? "ON" : "OFF");
 
  display.setCursor(0, 32);
  display.print("Bedroom: ");
  display.println(devices.bedroomLight ? "ON" : "OFF");
 
  display.setCursor(0, 42);
  display.print("Fan: ");
  display.print(devices.fanSpeed);
  display.println("%");
 
  display.setCursor(0, 52);
  display.print("Door: ");
  display.println(devices.doorLock ? "LOCKED" : "UNLOCKED");
 
  display.display();
}

void playStartupSound() {
  tone(BUZZER_PIN, 1000, 100);
  delay(150);
  tone(BUZZER_PIN, 1500, 100);
}

void playClickSound() {
  tone(BUZZER_PIN, 800, 50);
}

// Web Server Handlers
void handleRoot() {
  // Check authentication
  if (!server.authenticate(webUsername, webPassword)) {
    return server.requestAuthentication();
  }
 
  String html = generateHTML();
  server.send(200, "text/html", html);
}

String generateHTML() {
  String html = "<!DOCTYPE html><html><head>";
  html += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>";
  html += "<title>Smart Home Control</title>";
  html += "<style>";
  html += "* { margin: 0; padding: 0; box-sizing: border-box; }";
  html += "body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; ";
  html += "background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); ";
  html += "min-height: 100vh; padding: 20px; color: white; }";
  html += ".container { max-width: 1200px; margin: 0 auto; }";
  html += "h1 { text-align: center; font-size: 2.5em; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); }";
  html += ".subtitle { text-align: center; font-size: 1em; margin-bottom: 30px; opacity: 0.9; }";
  html += ".stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px; margin-bottom: 30px; }";
  html += ".stat-card { background: rgba(255,255,255,0.2); padding: 15px; border-radius: 10px; text-align: center; backdrop-filter: blur(10px); }";
  html += ".stat-value { font-size: 2em; font-weight: bold; }";
  html += ".stat-label { font-size: 0.9em; opacity: 0.8; margin-top: 5px; }";
  html += ".devices-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 20px; }";
  html += ".device-card { background: rgba(255,255,255,0.15); backdrop-filter: blur(10px); ";
  html += "padding: 25px; border-radius: 15px; box-shadow: 0 8px 32px rgba(0,0,0,0.1); border: 1px solid rgba(255,255,255,0.18); }";
  html += ".device-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }";
  html += ".device-icon { font-size: 2.5em; }";
  html += ".device-name { font-size: 1.3em; font-weight: bold; }";
  html += ".device-status { font-size: 0.9em; opacity: 0.8; margin-bottom: 15px; }";
  html += ".controls { display: flex; gap: 10px; flex-wrap: wrap; }";
  html += ".btn { padding: 12px 24px; border: none; border-radius: 8px; cursor: pointer; ";
  html += "font-size: 1em; font-weight: 600; transition: all 0.3s; text-decoration: none; display: inline-block; color: white; }";
  html += ".btn-on { background: #4CAF50; }";
  html += ".btn-on:hover { background: #45a049; transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.2); }";
  html += ".btn-off { background: #f44336; }";
  html += ".btn-off:hover { background: #da190b; transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.2); }";
  html += ".btn-action { background: #2196F3; }";
  html += ".btn-action:hover { background: #0b7dda; transform: translateY(-2px); }";
  html += ".btn-warning { background: #ff9800; }";
  html += ".slider-container { margin: 15px 0; }";
  html += ".slider { width: 100%; height: 8px; border-radius: 5px; background: rgba(255,255,255,0.3); ";
  html += "outline: none; -webkit-appearance: none; }";
  html += ".slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; ";
  html += "width: 20px; height: 20px; border-radius: 50%; background: white; cursor: pointer; }";
  html += ".slider::-moz-range-thumb { width: 20px; height: 20px; border-radius: 50%; background: white; cursor: pointer; border: none; }";
  html += ".slider-value { text-align: center; font-size: 1.2em; margin-top: 10px; font-weight: bold; }";
  html += ".quick-actions { background: rgba(255,255,255,0.2); padding: 20px; border-radius: 15px; margin-top: 20px; }";
  html += ".quick-actions h3 { margin-bottom: 15px; }";
  html += ".status-indicator { display: inline-block; width: 12px; height: 12px; border-radius: 50%; margin-right: 8px; }";
  html += ".status-on { background: #4CAF50; box-shadow: 0 0 10px #4CAF50; }";
  html += ".status-off { background: #666; }";
  html += "@media (max-width: 768px) { .devices-grid { grid-template-columns: 1fr; } }";
  html += "</style>";
  html += "<script>";
  html += "function updateSlider(val) { document.getElementById('fanValue').innerHTML = val + '%'; }";
  html += "function setFan() { var speed = document.getElementById('fanSlider').value; ";
  html += "fetch('/fan/set?speed=' + speed).then(() => location.reload()); }";
  html += "setInterval(() => fetch('/status').then(r => r.json()).then(data => {";
  html += "document.getElementById('temp').innerHTML = data.temperature.toFixed(1);";
  html += "document.getElementById('humid').innerHTML = data.humidity.toFixed(1);";
  html += "}), 3000);";
  html += "</script>";
  html += "</head><body>";
  html += "<div class='container'>";
 
  // Header
  html += "<h1>🏠 Smart Home Control Panel</h1>";
  html += "<div class='subtitle'>Remote Device Management System</div>";
 
  // Environment Stats
  html += "<div class='stats'>";
  html += "<div class='stat-card'>";
  html += "<div class='stat-value' id='temp'>" + String(devices.temperature, 1) + "</div>";
  html += "<div class='stat-label'>Temperature °C</div>";
  html += "</div>";
  html += "<div class='stat-card'>";
  html += "<div class='stat-value' id='humid'>" + String(devices.humidity, 1) + "</div>";
  html += "<div class='stat-label'>Humidity %</div>";
  html += "</div>";
  html += "<div class='stat-card'>";
  html += "<div class='stat-value'>" + String(WiFi.RSSI()) + "</div>";
  html += "<div class='stat-label'>WiFi Signal dBm</div>";
  html += "</div>";
  html += "</div>";
 
  // Devices Grid
  html += "<div class='devices-grid'>";
 
  // Living Room Light
  html += "<div class='device-card'>";
  html += "<div class='device-header'>";
  html += "<div><div class='device-icon'>💡</div><div class='device-name'>Living Room</div></div>";
  html += "</div>";
  html += "<div class='device-status'>";
  html += "<span class='status-indicator " + String(devices.livingRoomLight ? "status-on" : "status-off") + "'></span>";
  html += String(devices.livingRoomLight ? "Currently ON" : "Currently OFF");
  html += "</div>";
  html += "<div class='controls'>";
  html += "<a href='/living-room/on' class='btn btn-on'>Turn ON</a>";
  html += "<a href='/living-room/off' class='btn btn-off'>Turn OFF</a>";
  html += "</div>";
  html += "</div>";
 
  // Bedroom Light
  html += "<div class='device-card'>";
  html += "<div class='device-header'>";
  html += "<div><div class='device-icon'>🛏️</div><div class='device-name'>Bedroom</div></div>";
  html += "</div>";
  html += "<div class='device-status'>";
  html += "<span class='status-indicator " + String(devices.bedroomLight ? "status-on" : "status-off") + "'></span>";
  html += String(devices.bedroomLight ? "Currently ON" : "Currently OFF");
  html += "</div>";
  html += "<div class='controls'>";
  html += "<a href='/bedroom/on' class='btn btn-on'>Turn ON</a>";
  html += "<a href='/bedroom/off' class='btn btn-off'>Turn OFF</a>";
  html += "</div>";
  html += "</div>";
 
  // Fan with Speed Control
  html += "<div class='device-card'>";
  html += "<div class='device-header'>";
  html += "<div><div class='device-icon'>🌀</div><div class='device-name'>Ceiling Fan</div></div>";
  html += "</div>";
  html += "<div class='device-status'>";
  html += "<span class='status-indicator " + String(devices.fanSpeed > 0 ? "status-on" : "status-off") + "'></span>";
  html += "Speed: " + String(devices.fanSpeed) + "%";
  html += "</div>";
  html += "<div class='slider-container'>";
  html += "<input type='range' min='0' max='100' value='" + String(devices.fanSpeed) + "' ";
  html += "class='slider' id='fanSlider' oninput='updateSlider(this.value)'>";
  html += "<div class='slider-value' id='fanValue'>" + String(devices.fanSpeed) + "%</div>";
  html += "</div>";
  html += "<div class='controls'>";
  html += "<button onclick='setFan()' class='btn btn-action'>Apply Speed</button>";
  html += "</div>";
  html += "</div>";
 
  // Heater
  html += "<div class='device-card'>";
  html += "<div class='device-header'>";
  html += "<div><div class='device-icon'>🔥</div><div class='device-name'>Heater</div></div>";
  html += "</div>";
  html += "<div class='device-status'>";
  html += "<span class='status-indicator " + String(devices.heater ? "status-on" : "status-off") + "'></span>";
  html += String(devices.heater ? "Currently ON" : "Currently OFF");
  html += "</div>";
  html += "<div class='controls'>";
  html += "<a href='/heater/on' class='btn btn-on'>Turn ON</a>";
  html += "<a href='/heater/off' class='btn btn-off'>Turn OFF</a>";
  html += "</div>";
  html += "</div>";
 
  // Door Lock
  html += "<div class='device-card'>";
  html += "<div class='device-header'>";
  html += "<div><div class='device-icon'>🚪</div><div class='device-name'>Door Lock</div></div>";
  html += "</div>";
  html += "<div class='device-status'>";
  html += "<span class='status-indicator " + String(devices.doorLock ? "status-on" : "status-off") + "'></span>";
  html += String(devices.doorLock ? "🔒 LOCKED" : "🔓 UNLOCKED");
  html += "</div>";
  html += "<div class='controls'>";
  html += "<a href='/door/lock' class='btn btn-warning'>🔒 Lock</a>";
  html += "<a href='/door/unlock' class='btn btn-off'>🔓 Unlock</a>";
  html += "</div>";
  html += "</div>";
 
  html += "</div>"; // End devices grid
 
  // Quick Actions
  html += "<div class='quick-actions'>";
  html += "<h3>⚡ Quick Actions</h3>";
  html += "<div class='controls'>";
  html += "<a href='/all/on' class='btn btn-on'>Turn All Lights ON</a>";
  html += "<a href='/all/off' class='btn btn-off'>Turn All Lights OFF</a>";
  html += "<a href='/logout' class='btn btn-action'>Logout</a>";
  html += "</div>";
  html += "</div>";
 
  html += "<div style='text-align: center; margin-top: 30px; opacity: 0.7; font-size: 0.9em;'>";
  html += "IP: " + WiFi.localIP().toString() + " | Uptime: " + String(millis()/1000) + "s";
  html += "</div>";
 
  html += "</div></body></html>";
 
  return html;
}

void handleLogin() {
  server.send(200, "text/html", "Login handled by HTTP Basic Auth");
}

void handleLogout() {
  server.send(401, "text/html", "Logged out. Please refresh the page.");
}

void handleDeviceControl(String device, bool state) {
  if (!server.authenticate(webUsername, webPassword)) {
    return server.requestAuthentication();
  }
 
  if (device == "living-room") {
    devices.livingRoomLight = state;
  } else if (device == "bedroom") {
    devices.bedroomLight = state;
  } else if (device == "heater") {
    devices.heater = state;
  }
 
  applyDeviceStates();
  playClickSound();
 
  Serial.print(device);
  Serial.print(" turned ");
  Serial.println(state ? "ON" : "OFF");
 
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleFanControl() {
  if (!server.authenticate(webUsername, webPassword)) {
    return server.requestAuthentication();
  }
 
  if (server.hasArg("speed")) {
    devices.fanSpeed = server.arg("speed").toInt();
    devices.fanSpeed = constrain(devices.fanSpeed, 0, 100);
    applyDeviceStates();
    playClickSound();
   
    Serial.print("Fan speed set to: ");
    Serial.println(devices.fanSpeed);
  }
 
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleDoorControl(bool lock) {
  if (!server.authenticate(webUsername, webPassword)) {
    return server.requestAuthentication();
  }
 
  devices.doorLock = lock;
  applyDeviceStates();
 
  // Play different sound for door
  if (lock) {
    tone(BUZZER_PIN, 1200, 100);
    delay(150);
    tone(BUZZER_PIN, 1000, 100);
  } else {
    tone(BUZZER_PIN, 800, 100);
    delay(150);
    tone(BUZZER_PIN, 600, 100);
  }
 
  Serial.print("Door ");
  Serial.println(lock ? "LOCKED" : "UNLOCKED");
 
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleAllOn() {
  if (!server.authenticate(webUsername, webPassword)) {
    return server.requestAuthentication();
  }
 
  devices.livingRoomLight = true;
  devices.bedroomLight = true;
  applyDeviceStates();
  playClickSound();
 
  Serial.println("All lights turned ON");
 
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleAllOff() {
  if (!server.authenticate(webUsername, webPassword)) {
    return server.requestAuthentication();
  }
 
  devices.livingRoomLight = false;
  devices.bedroomLight = false;
  devices.heater = false;
  devices.fanSpeed = 0;
  applyDeviceStates();
  playClickSound();
 
  Serial.println("All devices turned OFF");
 
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleControl() {
  if (!server.authenticate(webUsername, webPassword)) {
    return server.requestAuthentication();
  }
 
  String html = "<h1>Control Panel</h1>";
  server.send(200, "text/html", html);
}

void handleStatus() {
  String json = "{";
  json += "\"livingRoomLight\":" + String(devices.livingRoomLight ? "true" : "false") + ",";
  json += "\"bedroomLight\":" + String(devices.bedroomLight ? "true" : "false") + ",";
  json += "\"fanSpeed\":" + String(devices.fanSpeed) + ",";
  json += "\"heater\":" + String(devices.heater ? "true" : "false") + ",";
  json += "\"doorLock\":" + String(devices.doorLock ? "true" : "false") + ",";
  json += "\"temperature\":" + String(devices.temperature, 2) + ",";
  json += "\"humidity\":" + String(devices.humidity, 2) + ",";
  json += "\"uptime\":" + String(millis() / 1000);
  json += "}";
 
  server.send(200, "application/json", json);
}

Comments