/*
* 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);
}