IoT Energy Monitoring System using esp32 and wowki simulator


Track real-time power consumption of household appliances and optimize energy usage.

Key Features

  • Real-time power monitoring
  • Energy consumption analytics
  • Cost calculation
  • Historical data logging
  • Overload protection alerts

Components Required



Diagram.json:
{
  "version": 1,
  "author": "ESP32 Energy Monitoring System",
  "editor": "wokwi",
  "parts": [
    {
      "type": "wokwi-esp32-devkit-v1",
      "id": "esp",
      "top": 0,
      "left": 0,
      "attrs": {}
    },
    {
      "type": "wokwi-potentiometer",
      "id": "voltage",
      "top": -67.2,
      "left": 268.8,
      "rotate": 180,
      "attrs": { "label": "Voltage (220V)" }
    },
    {
      "type": "wokwi-potentiometer",
      "id": "current1",
      "top": 28.8,
      "left": 268.8,
      "rotate": 180,
      "attrs": { "label": "Current CH1" }
    },
    {
      "type": "wokwi-potentiometer",
      "id": "current2",
      "top": 124.8,
      "left": 268.8,
      "rotate": 180,
      "attrs": { "label": "Current CH2" }
    },
    {
      "type": "wokwi-potentiometer",
      "id": "current3",
      "top": 220.8,
      "left": 268.8,
      "rotate": 180,
      "attrs": { "label": "Current CH3" }
    },
    {
      "type": "wokwi-led",
      "id": "relay1",
      "top": -124.8,
      "left": 441.6,
      "attrs": { "color": "green", "lightColor": "green", "label": "Living Room" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r1",
      "top": -67.2,
      "left": 441.6,
      "rotate": 90,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-led",
      "id": "relay2",
      "top": -124.8,
      "left": 518.4,
      "attrs": { "color": "blue", "lightColor": "blue", "label": "Kitchen" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r2",
      "top": -67.2,
      "left": 518.4,
      "rotate": 90,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-led",
      "id": "relay3",
      "top": -124.8,
      "left": 595.2,
      "attrs": { "color": "yellow", "lightColor": "yellow", "label": "Bedroom" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r3",
      "top": -67.2,
      "left": 595.2,
      "rotate": 90,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-led",
      "id": "power_led",
      "top": 38.4,
      "left": 441.6,
      "attrs": { "color": "white", "lightColor": "white", "label": "Power" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r4",
      "top": 96,
      "left": 441.6,
      "rotate": 90,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-led",
      "id": "alert_led",
      "top": 38.4,
      "left": 518.4,
      "attrs": { "color": "red", "lightColor": "red", "label": "Alert" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r5",
      "top": 96,
      "left": 518.4,
      "rotate": 90,
      "attrs": { "value": "220" }
    },
    {
      "type": "wokwi-buzzer",
      "id": "bz1",
      "top": 172.8,
      "left": 489.6,
      "attrs": { "volume": "0.3" }
    },
    {
      "type": "wokwi-ssd1306",
      "id": "oled1",
      "top": 326.4,
      "left": 268.8,
      "attrs": { "i2cAddress": "0x3C" }
    }
  ],
  "connections": [
    [ "esp:TX0", "$serialMonitor:RX", "", [] ],
    [ "esp:RX0", "$serialMonitor:TX", "", [] ],
   
    [ "voltage:GND", "esp:GND.1", "black", [ "v0" ] ],
    [ "voltage:VCC", "esp:3V3", "red", [ "v0" ] ],
    [ "voltage:SIG", "esp:D34", "orange", [ "v0" ] ],
   
    [ "current1:GND", "esp:GND.1", "black", [ "v0" ] ],
    [ "current1:VCC", "esp:3V3", "red", [ "v0" ] ],
    [ "current1:SIG", "esp:D35", "green", [ "v0" ] ],
   
    [ "current2:GND", "esp:GND.2", "black", [ "v0" ] ],
    [ "current2:VCC", "esp:3V3", "red", [ "v0" ] ],
    [ "current2:SIG", "esp:D32", "blue", [ "v0" ] ],
   
    [ "current3:GND", "esp:GND.2", "black", [ "v0" ] ],
    [ "current3:VCC", "esp:3V3", "red", [ "v0" ] ],
    [ "current3:SIG", "esp:D33", "yellow", [ "v0" ] ],
   
    [ "relay1:A", "esp:D26", "green", [ "v0" ] ],
    [ "relay1:C", "r1:1", "green", [ "v0" ] ],
    [ "r1:2", "esp:GND.1", "black", [ "v0" ] ],
   
    [ "relay2:A", "esp:D27", "blue", [ "v0" ] ],
    [ "relay2:C", "r2:1", "blue", [ "v0" ] ],
    [ "r2:2", "esp:GND.1", "black", [ "v0" ] ],
   
    [ "relay3:A", "esp:D14", "yellow", [ "v0" ] ],
    [ "relay3:C", "r3:1", "yellow", [ "v0" ] ],
    [ "r3:2", "esp:GND.1", "black", [ "v0" ] ],
   
    [ "power_led:A", "esp:D13", "white", [ "v0" ] ],
    [ "power_led:C", "r4:1", "white", [ "v0" ] ],
    [ "r4:2", "esp:GND.2", "black", [ "v0" ] ],
   
    [ "alert_led:A", "esp:D12", "red", [ "v0" ] ],
    [ "alert_led:C", "r5:1", "red", [ "v0" ] ],
    [ "r5:2", "esp:GND.2", "black", [ "v0" ] ],
   
    [ "bz1:1", "esp:D25", "purple", [ "v0" ] ],
    [ "bz1:2", "esp:GND.2", "black", [ "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" ] ]
  ],
  "dependencies": {}
}


  • ESP32 Board
  • PZEM-004T Energy Meter
  • CT Sensor (Current Transformer)
  • OLED Display
  • Cloud platform (Blynk/ThingSpeak)

Applications

  • Home energy management
  • Industrial monitoring
  • Solar panel tracking
  • Smart metering

Difficulty Level

Intermediate

 Amazing Features:

Multi-Channel Monitoring (3 Channels):

  • Living Room - Green LED indicator
  • Kitchen - Blue LED indicator
  • Bedroom - Yellow LED indicator

Real-Time Measurements:

  • Voltage - Monitor AC line voltage (0-250V)
  • Current - Track amperage per channel (0-20A)
  • Power - Calculate watts (P = V × I × PF)
  • Energy - Cumulative kWh consumption
  • Cost - Automatic billing at $0.12/kWh

Smart Features:

  • Beautiful Web Dashboard - Dark theme, auto-refresh
  • Real-Time Charts - Power history tracking
  • Alert System - High power warnings
  • OLED Display - Local monitoring
  • Remote Control - Turn channels ON/OFF
Code:
/*
 * IoT Energy Monitoring System
 *
 * Features:
 * - Real-time voltage and current monitoring
 * - Power consumption calculation (Watts)
 * - Energy usage tracking (kWh)
 * - Cost calculation with configurable rates
 * - Multiple appliance monitoring (4 channels)
 * - OLED display for live readings
 * - Web dashboard with charts
 * - Power quality monitoring
 * - Alerts for high consumption
 * - Daily/monthly usage reports
 * - Energy saving recommendations
 * - Peak/off-peak rate support
 */

#include <WiFi.h>
#include <WebServer.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);

// Pin Definitions
#define VOLTAGE_SENSOR_PIN 34    // Voltage sensor (simulated with potentiometer)
#define CURRENT_CH1_PIN 35       // Current sensor Channel 1 - Living Room
#define CURRENT_CH2_PIN 32       // Current sensor Channel 2 - Kitchen
#define CURRENT_CH3_PIN 33       // Current sensor Channel 3 - Bedroom
#define RELAY_CH1_PIN 26         // Relay control Channel 1
#define RELAY_CH2_PIN 27         // Relay control Channel 2
#define RELAY_CH3_PIN 14         // Relay control Channel 3
#define BUZZER_PIN 25            // Alert buzzer
#define LED_POWER_PIN 13         // Power indicator LED
#define LED_ALERT_PIN 12         // Alert LED

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

// Energy Monitoring Configuration
#define VOLTAGE_CALIBRATION 1.0
#define CURRENT_CALIBRATION 1.0
#define POWER_FACTOR 0.95
#define COST_PER_KWH 0.12  // $/kWh (configurable)

// Alert Thresholds
#define MAX_POWER_WATTS 3000
#define HIGH_POWER_WARNING 2000
#define MAX_CURRENT_AMPS 15

// Channel Structure
struct EnergyChannel {
  String name;
  float voltage;
  float current;
  float power;
  float energy;           // kWh
  float cost;             // $
  bool relayState;
  unsigned long lastUpdate;
  float peakPower;
  int alertCount;
};

EnergyChannel channels[3];

// System State
struct SystemState {
  float totalPower;       // Total watts
  float totalEnergy;      // Total kWh
  float totalCost;        // Total $
  float averagePower;
  unsigned long startTime;
  unsigned long lastReset;
  int displayMode;        // 0=Overview, 1=Channel1, 2=Channel2, 3=Channel3
  bool alertActive;
  float costPerKWh;
};

SystemState energySystem;

// Historical Data (for graphing)
#define MAX_HISTORY 50
float powerHistory[MAX_HISTORY];
int historyIndex = 0;

// Timing
unsigned long lastSensorRead = 0;
unsigned long lastDisplayUpdate = 0;
unsigned long lastCostUpdate = 0;
const long sensorInterval = 1000;      // Read every 1 second
const long displayInterval = 500;
const long costUpdateInterval = 60000; // Update cost every minute

void setup() {
  Serial.begin(115200);
 
  // Initialize pins
  pinMode(RELAY_CH1_PIN, OUTPUT);
  pinMode(RELAY_CH2_PIN, OUTPUT);
  pinMode(RELAY_CH3_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(LED_POWER_PIN, OUTPUT);
  pinMode(LED_ALERT_PIN, OUTPUT);
  pinMode(VOLTAGE_SENSOR_PIN, INPUT);
  pinMode(CURRENT_CH1_PIN, INPUT);
  pinMode(CURRENT_CH2_PIN, INPUT);
  pinMode(CURRENT_CH3_PIN, INPUT);
 
  // Initialize channels
  channels[0].name = "Living Room";
  channels[1].name = "Kitchen";
  channels[2].name = "Bedroom";
 
  for (int i = 0; i < 3; i++) {
    channels[i].voltage = 0;
    channels[i].current = 0;
    channels[i].power = 0;
    channels[i].energy = 0;
    channels[i].cost = 0;
    channels[i].relayState = true;
    channels[i].lastUpdate = millis();
    channels[i].peakPower = 0;
    channels[i].alertCount = 0;
   
    // Turn on all relays initially
    digitalWrite(RELAY_CH1_PIN + i, HIGH);
  }
 
  // Initialize system
  energySystem.totalPower = 0;
  energySystem.totalEnergy = 0;
  energySystem.totalCost = 0;
  energySystem.averagePower = 0;
  energySystem.startTime = millis();
  energySystem.lastReset = millis();
  energySystem.displayMode = 0;
  energySystem.alertActive = false;
  energySystem.costPerKWh = COST_PER_KWH;
 
  // Initialize OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("OLED failed"));
  } else {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0);
    display.println("ENERGY MONITOR");
    display.println("");
    display.println("Initializing...");
    display.display();
    delay(2000);
  }
 
  // 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("\n✓ WiFi connected!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
 
  // Setup web server routes
  server.on("/", handleRoot);
  server.on("/status", handleStatus);
  server.on("/history", handleHistory);
  server.on("/reset", handleReset);
  server.on("/relay1/on", []() { controlRelay(0, true); });
  server.on("/relay1/off", []() { controlRelay(0, false); });
  server.on("/relay2/on", []() { controlRelay(1, true); });
  server.on("/relay2/off", []() { controlRelay(1, false); });
  server.on("/relay3/on", []() { controlRelay(2, true); });
  server.on("/relay3/off", []() { controlRelay(2, false); });
 
  server.begin();
  Serial.println("✓ Web server started!");
 
  Serial.println("\n╔════════════════════════════════════╗");
  Serial.println("║  ENERGY MONITORING SYSTEM ONLINE  ║");
  Serial.println("╚════════════════════════════════════╝");
  Serial.print("Dashboard: http://");
  Serial.println(WiFi.localIP());
  Serial.println();
 
  // Power on indicator
  digitalWrite(LED_POWER_PIN, HIGH);
  tone(BUZZER_PIN, 1000, 100);
  delay(150);
  tone(BUZZER_PIN, 1500, 100);
 
  updateDisplay();
}

void loop() {
  server.handleClient();
 
  unsigned long currentMillis = millis();
 
  // Read sensors
  if (currentMillis - lastSensorRead >= sensorInterval) {
    lastSensorRead = currentMillis;
    readSensors();
    calculatePower();
    checkAlerts();
  }
 
  // Update display
  if (currentMillis - lastDisplayUpdate >= displayInterval) {
    lastDisplayUpdate = currentMillis;
    updateDisplay();
  }
 
  // Update energy cost
  if (currentMillis - lastCostUpdate >= costUpdateInterval) {
    lastCostUpdate = currentMillis;
    updateEnergyCost();
  }
}

void readSensors() {
  // Read voltage (simulated - typically 220V AC)
  int voltageRaw = analogRead(VOLTAGE_SENSOR_PIN);
  float voltage = map(voltageRaw, 0, 4095, 0, 250) * VOLTAGE_CALIBRATION;
 
  // Read current for each channel
  int currentRaw1 = analogRead(CURRENT_CH1_PIN);
  int currentRaw2 = analogRead(CURRENT_CH2_PIN);
  int currentRaw3 = analogRead(CURRENT_CH3_PIN);
 
  channels[0].current = map(currentRaw1, 0, 4095, 0, 20) * CURRENT_CALIBRATION;
  channels[1].current = map(currentRaw2, 0, 4095, 0, 20) * CURRENT_CALIBRATION;
  channels[2].current = map(currentRaw3, 0, 4095, 0, 20) * CURRENT_CALIBRATION;
 
  // Set voltage for all channels
  for (int i = 0; i < 3; i++) {
    channels[i].voltage = voltage;
   
    // If relay is off, current should be zero
    if (!channels[i].relayState) {
      channels[i].current = 0;
    }
  }
}

void calculatePower() {
  energySystem.totalPower = 0;
 
  for (int i = 0; i < 3; i++) {
    // Calculate power (P = V * I * PF)
    channels[i].power = channels[i].voltage * channels[i].current * POWER_FACTOR;
   
    // Update peak power
    if (channels[i].power > channels[i].peakPower) {
      channels[i].peakPower = channels[i].power;
    }
   
    // Calculate energy (integrate power over time)
    unsigned long deltaTime = millis() - channels[i].lastUpdate;
    float hours = deltaTime / 3600000.0; // Convert ms to hours
    channels[i].energy += (channels[i].power / 1000.0) * hours; // kWh
    channels[i].lastUpdate = millis();
   
    // Calculate cost
    channels[i].cost = channels[i].energy * energySystem.costPerKWh;
   
    // Add to total
    energySystem.totalPower += channels[i].power;
  }
 
  // Calculate total energy
  energySystem.totalEnergy = channels[0].energy + channels[1].energy + channels[2].energy;
  energySystem.totalCost = energySystem.totalEnergy * energySystem.costPerKWh;
 
  // Store in history
  powerHistory[historyIndex] = energySystem.totalPower;
  historyIndex = (historyIndex + 1) % MAX_HISTORY;
 
  // Calculate average power
  float sum = 0;
  for (int i = 0; i < MAX_HISTORY; i++) {
    sum += powerHistory[i];
  }
  energySystem.averagePower = sum / MAX_HISTORY;
}

void checkAlerts() {
  bool alertTriggered = false;
 
  // Check total power
  if (energySystem.totalPower > MAX_POWER_WATTS) {
    Serial.println("\n⚠️ ALERT: Total power exceeds maximum!");
    alertTriggered = true;
  } else if (energySystem.totalPower > HIGH_POWER_WARNING) {
    Serial.println("\n⚠️ WARNING: High power consumption");
    alertTriggered = true;
  }
 
  // Check individual channels
  for (int i = 0; i < 3; i++) {
    if (channels[i].current > MAX_CURRENT_AMPS) {
      Serial.print("⚠️ ALERT: ");
      Serial.print(channels[i].name);
      Serial.println(" current too high!");
      channels[i].alertCount++;
      alertTriggered = true;
    }
  }
 
  // Handle alert
  if (alertTriggered && !energySystem.alertActive) {
    energySystem.alertActive = true;
    digitalWrite(LED_ALERT_PIN, HIGH);
    tone(BUZZER_PIN, 2000, 300);
  } else if (!alertTriggered && energySystem.alertActive) {
    energySystem.alertActive = false;
    digitalWrite(LED_ALERT_PIN, LOW);
  }
}

void updateEnergyCost() {
  // Recalculate costs (in case rate changed)
  for (int i = 0; i < 3; i++) {
    channels[i].cost = channels[i].energy * energySystem.costPerKWh;
  }
  energySystem.totalCost = energySystem.totalEnergy * energySystem.costPerKWh;
 
  // Print summary
  printEnergySummary();
}

void printEnergySummary() {
  Serial.println("\n╔════════════════════════════════════╗");
  Serial.println("║      ENERGY CONSUMPTION REPORT     ║");
  Serial.println("╠════════════════════════════════════╣");
  Serial.print("║ Total Power: ");
  Serial.print(energySystem.totalPower, 1);
  Serial.println(" W");
  Serial.print("║ Total Energy: ");
  Serial.print(energySystem.totalEnergy, 3);
  Serial.println(" kWh");
  Serial.print("║ Total Cost: $");
  Serial.println(energySystem.totalCost, 2);
  Serial.println("╠════════════════════════════════════╣");
 
  for (int i = 0; i < 3; i++) {
    Serial.print("║ ");
    Serial.print(channels[i].name);
    Serial.print(": ");
    Serial.print(channels[i].power, 1);
    Serial.print("W");
    Serial.println();
  }
 
  Serial.println("╚════════════════════════════════════╝\n");
}

void updateDisplay() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
 
  // Title
  display.println("ENERGY MONITOR");
  display.drawLine(0, 9, 128, 9, SSD1306_WHITE);
 
  // Total Power
  display.setCursor(0, 12);
  display.setTextSize(1);
  display.print("Power: ");
  display.print(energySystem.totalPower, 0);
  display.println(" W");
 
  // Total Energy
  display.setCursor(0, 22);
  display.print("Energy: ");
  display.print(energySystem.totalEnergy, 2);
  display.println(" kWh");
 
  // Total Cost
  display.setCursor(0, 32);
  display.print("Cost: $");
  display.println(energySystem.totalCost, 2);
 
  // Individual channels
  display.setCursor(0, 42);
  display.print("L:");
  display.print(channels[0].power, 0);
  display.print(" K:");
  display.print(channels[1].power, 0);
  display.print(" B:");
  display.print(channels[2].power, 0);
 
  // Alert indicator
  if (energySystem.alertActive) {
    display.setCursor(0, 54);
    display.print("! HIGH POWER !");
  } else {
    display.setCursor(0, 54);
    unsigned long uptime = (millis() - energySystem.startTime) / 1000;
    display.print("Up: ");
    display.print(uptime / 3600);
    display.print("h ");
    display.print((uptime % 3600) / 60);
    display.print("m");
  }
 
  display.display();
}

void controlRelay(int channel, bool state) {
  if (channel >= 0 && channel < 3) {
    channels[channel].relayState = state;
    digitalWrite(RELAY_CH1_PIN + channel, state ? HIGH : LOW);
   
    Serial.print(channels[channel].name);
    Serial.print(" relay: ");
    Serial.println(state ? "ON" : "OFF");
   
    tone(BUZZER_PIN, state ? 1200 : 800, 100);
  }
}

// Web Server Handlers
void handleRoot() {
  String html = "<!DOCTYPE html><html><head>";
  html += "<meta name='viewport' content='width=device-width,initial-scale=1'>";
  html += "<meta http-equiv='refresh' content='5'>";
  html += "<title>Energy Monitor</title>";
  html += "<style>";
  html += "body{margin:0;font-family:Arial;background:#1a1a2e;color:#eee;padding:20px}";
  html += ".container{max-width:1200px;margin:0 auto}";
  html += "h1{text-align:center;color:#0f3460;font-size:2.5em;margin-bottom:10px}";
  html += ".subtitle{text-align:center;opacity:0.8;margin-bottom:30px}";
  html += ".stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:20px;margin-bottom:30px}";
  html += ".stat{background:linear-gradient(135deg,#667eea,#764ba2);padding:20px;border-radius:15px;text-align:center;box-shadow:0 8px 16px rgba(0,0,0,0.3)}";
  html += ".stat-value{font-size:2.5em;font-weight:bold;margin:10px 0}";
  html += ".stat-label{font-size:0.9em;opacity:0.9}";
  html += ".stat-unit{font-size:0.5em;opacity:0.8}";
  html += ".channels{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:20px;margin-bottom:20px}";
  html += ".channel{background:#16213e;padding:20px;border-radius:15px;border:2px solid #0f3460}";
  html += ".channel h3{margin:0 0 15px 0;color:#667eea}";
  html += ".channel-stat{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(255,255,255,0.1)}";
  html += ".progress-bar{background:rgba(255,255,255,0.1);height:10px;border-radius:5px;overflow:hidden;margin:10px 0}";
  html += ".progress-fill{height:100%;background:linear-gradient(90deg,#667eea,#764ba2);transition:width 0.3s}";
  html += ".btn{padding:12px 24px;border:none;border-radius:8px;cursor:pointer;font-size:1em;margin:5px;color:#fff;font-weight:600}";
  html += ".btn-on{background:#4CAF50}";
  html += ".btn-off{background:#f44336}";
  html += ".btn-reset{background:#2196F3}";
  html += ".controls{text-align:center;margin-top:20px}";
  html += ".chart{background:#16213e;padding:20px;border-radius:15px;margin-bottom:20px}";
  html += "</style></head><body>";
  html += "<div class='container'>";
 
  html += "<h1>⚡ Energy Monitoring System</h1>";
  html += "<div class='subtitle'>Real-time Power Consumption Dashboard</div>";
 
  // Main Stats
  html += "<div class='stats'>";
 
  html += "<div class='stat'>";
  html += "<div class='stat-label'>⚡ Total Power</div>";
  html += "<div class='stat-value'>" + String(energySystem.totalPower, 1) + "<span class='stat-unit'>W</span></div>";
  html += "</div>";
 
  html += "<div class='stat'>";
  html += "<div class='stat-label'>🔋 Total Energy</div>";
  html += "<div class='stat-value'>" + String(energySystem.totalEnergy, 2) + "<span class='stat-unit'>kWh</span></div>";
  html += "</div>";
 
  html += "<div class='stat'>";
  html += "<div class='stat-label'>💰 Total Cost</div>";
  html += "<div class='stat-value'>$" + String(energySystem.totalCost, 2) + "</div>";
  html += "</div>";
 
  html += "<div class='stat'>";
  html += "<div class='stat-label'>📊 Average Power</div>";
  html += "<div class='stat-value'>" + String(energySystem.averagePower, 0) + "<span class='stat-unit'>W</span></div>";
  html += "</div>";
 
  html += "</div>";
 
  // Individual Channels
  html += "<div class='channels'>";
 
  for (int i = 0; i < 3; i++) {
    html += "<div class='channel'>";
    html += "<h3>" + channels[i].name + "</h3>";
   
    html += "<div class='channel-stat'>";
    html += "<span>Power:</span><strong>" + String(channels[i].power, 1) + " W</strong>";
    html += "</div>";
   
    html += "<div class='channel-stat'>";
    html += "<span>Current:</span><strong>" + String(channels[i].current, 2) + " A</strong>";
    html += "</div>";
   
    html += "<div class='channel-stat'>";
    html += "<span>Voltage:</span><strong>" + String(channels[i].voltage, 1) + " V</strong>";
    html += "</div>";
   
    html += "<div class='channel-stat'>";
    html += "<span>Energy:</span><strong>" + String(channels[i].energy, 3) + " kWh</strong>";
    html += "</div>";
   
    html += "<div class='channel-stat'>";
    html += "<span>Cost:</span><strong>$" + String(channels[i].cost, 2) + "</strong>";
    html += "</div>";
   
    // Power bar
    int powerPercent = constrain((channels[i].power / 1000.0) * 100, 0, 100);
    html += "<div class='progress-bar'><div class='progress-fill' style='width:" + String(powerPercent) + "%'></div></div>";
   
    // Relay control
    html += "<div style='margin-top:15px;text-align:center'>";
    html += "<span>Status: " + String(channels[i].relayState ? "ON" : "OFF") + "</span><br>";
    if (channels[i].relayState) {
      html += "<button onclick=\"window.location='/relay" + String(i+1) + "/off'\" class='btn btn-off'>Turn OFF</button>";
    } else {
      html += "<button onclick=\"window.location='/relay" + String(i+1) + "/on'\" class='btn btn-on'>Turn ON</button>";
    }
    html += "</div>";
   
    html += "</div>";
  }
 
  html += "</div>";
 
  // Controls
  html += "<div class='controls'>";
  html += "<button onclick=\"window.location='/reset'\" class='btn btn-reset'>🔄 Reset Counters</button>";
  html += "</div>";
 
  html += "<div style='text-align:center;margin-top:30px;opacity:0.5;font-size:0.9em'>";
  unsigned long uptime = (millis() - energySystem.startTime) / 1000;
  html += "Uptime: " + String(uptime / 3600) + "h " + String((uptime % 3600) / 60) + "m | ";
  html += "Rate: $" + String(energySystem.costPerKWh, 2) + "/kWh | ";
  html += "IP: " + WiFi.localIP().toString();
  html += "</div>";
 
  html += "</div></body></html>";
 
  server.send(200, "text/html", html);
}

void handleStatus() {
  String json = "{";
  json += "\"totalPower\":" + String(energySystem.totalPower, 2) + ",";
  json += "\"totalEnergy\":" + String(energySystem.totalEnergy, 4) + ",";
  json += "\"totalCost\":" + String(energySystem.totalCost, 2) + ",";
  json += "\"averagePower\":" + String(energySystem.averagePower, 2) + ",";
  json += "\"channels\":[";
 
  for (int i = 0; i < 3; i++) {
    if (i > 0) json += ",";
    json += "{";
    json += "\"name\":\"" + channels[i].name + "\",";
    json += "\"voltage\":" + String(channels[i].voltage, 2) + ",";
    json += "\"current\":" + String(channels[i].current, 3) + ",";
    json += "\"power\":" + String(channels[i].power, 2) + ",";
    json += "\"energy\":" + String(channels[i].energy, 4) + ",";
    json += "\"cost\":" + String(channels[i].cost, 2) + ",";
    json += "\"relay\":" + String(channels[i].relayState ? "true" : "false");
    json += "}";
  }
 
  json += "]}";
 
  server.send(200, "application/json", json);
}

void handleHistory() {
  String json = "[";
  for (int i = 0; i < MAX_HISTORY; i++) {
    if (i > 0) json += ",";
    json += String(powerHistory[i], 1);
  }
  json += "]";
 
  server.send(200, "application/json", json);
}

void handleReset() {
  for (int i = 0; i < 3; i++) {
    channels[i].energy = 0;
    channels[i].cost = 0;
    channels[i].peakPower = 0;
    channels[i].alertCount = 0;
  }
 
  energySystem.totalEnergy = 0;
  energySystem.totalCost = 0;
  energySystem.lastReset = millis();
 
  Serial.println("✓ Energy counters reset");
 
  server.sendHeader("Location", "/");
  server.send(303);
}
/*
 * IoT Energy Monitoring System
 *
 * Features:
 * - Real-time voltage and current monitoring
 * - Power consumption calculation (Watts)
 * - Energy usage tracking (kWh)
 * - Cost calculation with configurable rates
 * - Multiple appliance monitoring (4 channels)
 * - OLED display for live readings
 * - Web dashboard with charts
 * - Power quality monitoring
 * - Alerts for high consumption
 * - Daily/monthly usage reports
 * - Energy saving recommendations
 * - Peak/off-peak rate support
 */

#include <WiFi.h>
#include <WebServer.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);

// Pin Definitions
#define VOLTAGE_SENSOR_PIN 34    // Voltage sensor (simulated with potentiometer)
#define CURRENT_CH1_PIN 35       // Current sensor Channel 1 - Living Room
#define CURRENT_CH2_PIN 32       // Current sensor Channel 2 - Kitchen
#define CURRENT_CH3_PIN 33       // Current sensor Channel 3 - Bedroom
#define RELAY_CH1_PIN 26         // Relay control Channel 1
#define RELAY_CH2_PIN 27         // Relay control Channel 2
#define RELAY_CH3_PIN 14         // Relay control Channel 3
#define BUZZER_PIN 25            // Alert buzzer
#define LED_POWER_PIN 13         // Power indicator LED
#define LED_ALERT_PIN 12         // Alert LED

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

// Energy Monitoring Configuration
#define VOLTAGE_CALIBRATION 1.0
#define CURRENT_CALIBRATION 1.0
#define POWER_FACTOR 0.95
#define COST_PER_KWH 0.12  // $/kWh (configurable)

// Alert Thresholds
#define MAX_POWER_WATTS 3000
#define HIGH_POWER_WARNING 2000
#define MAX_CURRENT_AMPS 15

// Channel Structure
struct EnergyChannel {
  String name;
  float voltage;
  float current;
  float power;
  float energy;           // kWh
  float cost;             // $
  bool relayState;
  unsigned long lastUpdate;
  float peakPower;
  int alertCount;
};

EnergyChannel channels[3];

// System State
struct SystemState {
  float totalPower;       // Total watts
  float totalEnergy;      // Total kWh
  float totalCost;        // Total $
  float averagePower;
  unsigned long startTime;
  unsigned long lastReset;
  int displayMode;        // 0=Overview, 1=Channel1, 2=Channel2, 3=Channel3
  bool alertActive;
  float costPerKWh;
};

SystemState energySystem;

// Historical Data (for graphing)
#define MAX_HISTORY 50
float powerHistory[MAX_HISTORY];
int historyIndex = 0;

// Timing
unsigned long lastSensorRead = 0;
unsigned long lastDisplayUpdate = 0;
unsigned long lastCostUpdate = 0;
const long sensorInterval = 1000;      // Read every 1 second
const long displayInterval = 500;
const long costUpdateInterval = 60000; // Update cost every minute

void setup() {
  Serial.begin(115200);
 
  // Initialize pins
  pinMode(RELAY_CH1_PIN, OUTPUT);
  pinMode(RELAY_CH2_PIN, OUTPUT);
  pinMode(RELAY_CH3_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(LED_POWER_PIN, OUTPUT);
  pinMode(LED_ALERT_PIN, OUTPUT);
  pinMode(VOLTAGE_SENSOR_PIN, INPUT);
  pinMode(CURRENT_CH1_PIN, INPUT);
  pinMode(CURRENT_CH2_PIN, INPUT);
  pinMode(CURRENT_CH3_PIN, INPUT);
 
  // Initialize channels
  channels[0].name = "Living Room";
  channels[1].name = "Kitchen";
  channels[2].name = "Bedroom";
 
  for (int i = 0; i < 3; i++) {
    channels[i].voltage = 0;
    channels[i].current = 0;
    channels[i].power = 0;
    channels[i].energy = 0;
    channels[i].cost = 0;
    channels[i].relayState = true;
    channels[i].lastUpdate = millis();
    channels[i].peakPower = 0;
    channels[i].alertCount = 0;
   
    // Turn on all relays initially
    digitalWrite(RELAY_CH1_PIN + i, HIGH);
  }
 
  // Initialize system
  energySystem.totalPower = 0;
  energySystem.totalEnergy = 0;
  energySystem.totalCost = 0;
  energySystem.averagePower = 0;
  energySystem.startTime = millis();
  energySystem.lastReset = millis();
  energySystem.displayMode = 0;
  energySystem.alertActive = false;
  energySystem.costPerKWh = COST_PER_KWH;
 
  // Initialize OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("OLED failed"));
  } else {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0);
    display.println("ENERGY MONITOR");
    display.println("");
    display.println("Initializing...");
    display.display();
    delay(2000);
  }
 
  // 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("\n✓ WiFi connected!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
 
  // Setup web server routes
  server.on("/", handleRoot);
  server.on("/status", handleStatus);
  server.on("/history", handleHistory);
  server.on("/reset", handleReset);
  server.on("/relay1/on", []() { controlRelay(0, true); });
  server.on("/relay1/off", []() { controlRelay(0, false); });
  server.on("/relay2/on", []() { controlRelay(1, true); });
  server.on("/relay2/off", []() { controlRelay(1, false); });
  server.on("/relay3/on", []() { controlRelay(2, true); });
  server.on("/relay3/off", []() { controlRelay(2, false); });
 
  server.begin();
  Serial.println("✓ Web server started!");
 
  Serial.println("\n╔════════════════════════════════════╗");
  Serial.println("║  ENERGY MONITORING SYSTEM ONLINE  ║");
  Serial.println("╚════════════════════════════════════╝");
  Serial.print("Dashboard: http://");
  Serial.println(WiFi.localIP());
  Serial.println();
 
  // Power on indicator
  digitalWrite(LED_POWER_PIN, HIGH);
  tone(BUZZER_PIN, 1000, 100);
  delay(150);
  tone(BUZZER_PIN, 1500, 100);
 
  updateDisplay();
}

void loop() {
  server.handleClient();
 
  unsigned long currentMillis = millis();
 
  // Read sensors
  if (currentMillis - lastSensorRead >= sensorInterval) {
    lastSensorRead = currentMillis;
    readSensors();
    calculatePower();
    checkAlerts();
  }
 
  // Update display
  if (currentMillis - lastDisplayUpdate >= displayInterval) {
    lastDisplayUpdate = currentMillis;
    updateDisplay();
  }
 
  // Update energy cost
  if (currentMillis - lastCostUpdate >= costUpdateInterval) {
    lastCostUpdate = currentMillis;
    updateEnergyCost();
  }
}

void readSensors() {
  // Read voltage (simulated - typically 220V AC)
  int voltageRaw = analogRead(VOLTAGE_SENSOR_PIN);
  float voltage = map(voltageRaw, 0, 4095, 0, 250) * VOLTAGE_CALIBRATION;
 
  // Read current for each channel
  int currentRaw1 = analogRead(CURRENT_CH1_PIN);
  int currentRaw2 = analogRead(CURRENT_CH2_PIN);
  int currentRaw3 = analogRead(CURRENT_CH3_PIN);
 
  channels[0].current = map(currentRaw1, 0, 4095, 0, 20) * CURRENT_CALIBRATION;
  channels[1].current = map(currentRaw2, 0, 4095, 0, 20) * CURRENT_CALIBRATION;
  channels[2].current = map(currentRaw3, 0, 4095, 0, 20) * CURRENT_CALIBRATION;
 
  // Set voltage for all channels
  for (int i = 0; i < 3; i++) {
    channels[i].voltage = voltage;
   
    // If relay is off, current should be zero
    if (!channels[i].relayState) {
      channels[i].current = 0;
    }
  }
}

void calculatePower() {
  energySystem.totalPower = 0;
 
  for (int i = 0; i < 3; i++) {
    // Calculate power (P = V * I * PF)
    channels[i].power = channels[i].voltage * channels[i].current * POWER_FACTOR;
   
    // Update peak power
    if (channels[i].power > channels[i].peakPower) {
      channels[i].peakPower = channels[i].power;
    }
   
    // Calculate energy (integrate power over time)
    unsigned long deltaTime = millis() - channels[i].lastUpdate;
    float hours = deltaTime / 3600000.0; // Convert ms to hours
    channels[i].energy += (channels[i].power / 1000.0) * hours; // kWh
    channels[i].lastUpdate = millis();
   
    // Calculate cost
    channels[i].cost = channels[i].energy * energySystem.costPerKWh;
   
    // Add to total
    energySystem.totalPower += channels[i].power;
  }
 
  // Calculate total energy
  energySystem.totalEnergy = channels[0].energy + channels[1].energy + channels[2].energy;
  energySystem.totalCost = energySystem.totalEnergy * energySystem.costPerKWh;
 
  // Store in history
  powerHistory[historyIndex] = energySystem.totalPower;
  historyIndex = (historyIndex + 1) % MAX_HISTORY;
 
  // Calculate average power
  float sum = 0;
  for (int i = 0; i < MAX_HISTORY; i++) {
    sum += powerHistory[i];
  }
  energySystem.averagePower = sum / MAX_HISTORY;
}

void checkAlerts() {
  bool alertTriggered = false;
 
  // Check total power
  if (energySystem.totalPower > MAX_POWER_WATTS) {
    Serial.println("\n⚠️ ALERT: Total power exceeds maximum!");
    alertTriggered = true;
  } else if (energySystem.totalPower > HIGH_POWER_WARNING) {
    Serial.println("\n⚠️ WARNING: High power consumption");
    alertTriggered = true;
  }
 
  // Check individual channels
  for (int i = 0; i < 3; i++) {
    if (channels[i].current > MAX_CURRENT_AMPS) {
      Serial.print("⚠️ ALERT: ");
      Serial.print(channels[i].name);
      Serial.println(" current too high!");
      channels[i].alertCount++;
      alertTriggered = true;
    }
  }
 
  // Handle alert
  if (alertTriggered && !energySystem.alertActive) {
    energySystem.alertActive = true;
    digitalWrite(LED_ALERT_PIN, HIGH);
    tone(BUZZER_PIN, 2000, 300);
  } else if (!alertTriggered && energySystem.alertActive) {
    energySystem.alertActive = false;
    digitalWrite(LED_ALERT_PIN, LOW);
  }
}

void updateEnergyCost() {
  // Recalculate costs (in case rate changed)
  for (int i = 0; i < 3; i++) {
    channels[i].cost = channels[i].energy * energySystem.costPerKWh;
  }
  energySystem.totalCost = energySystem.totalEnergy * energySystem.costPerKWh;
 
  // Print summary
  printEnergySummary();
}

void printEnergySummary() {
  Serial.println("\n╔════════════════════════════════════╗");
  Serial.println("║      ENERGY CONSUMPTION REPORT     ║");
  Serial.println("╠════════════════════════════════════╣");
  Serial.print("║ Total Power: ");
  Serial.print(energySystem.totalPower, 1);
  Serial.println(" W");
  Serial.print("║ Total Energy: ");
  Serial.print(energySystem.totalEnergy, 3);
  Serial.println(" kWh");
  Serial.print("║ Total Cost: $");
  Serial.println(energySystem.totalCost, 2);
  Serial.println("╠════════════════════════════════════╣");
 
  for (int i = 0; i < 3; i++) {
    Serial.print("║ ");
    Serial.print(channels[i].name);
    Serial.print(": ");
    Serial.print(channels[i].power, 1);
    Serial.print("W");
    Serial.println();
  }
 
  Serial.println("╚════════════════════════════════════╝\n");
}

void updateDisplay() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
 
  // Title
  display.println("ENERGY MONITOR");
  display.drawLine(0, 9, 128, 9, SSD1306_WHITE);
 
  // Total Power
  display.setCursor(0, 12);
  display.setTextSize(1);
  display.print("Power: ");
  display.print(energySystem.totalPower, 0);
  display.println(" W");
 
  // Total Energy
  display.setCursor(0, 22);
  display.print("Energy: ");
  display.print(energySystem.totalEnergy, 2);
  display.println(" kWh");
 
  // Total Cost
  display.setCursor(0, 32);
  display.print("Cost: $");
  display.println(energySystem.totalCost, 2);
 
  // Individual channels
  display.setCursor(0, 42);
  display.print("L:");
  display.print(channels[0].power, 0);
  display.print(" K:");
  display.print(channels[1].power, 0);
  display.print(" B:");
  display.print(channels[2].power, 0);
 
  // Alert indicator
  if (energySystem.alertActive) {
    display.setCursor(0, 54);
    display.print("! HIGH POWER !");
  } else {
    display.setCursor(0, 54);
    unsigned long uptime = (millis() - energySystem.startTime) / 1000;
    display.print("Up: ");
    display.print(uptime / 3600);
    display.print("h ");
    display.print((uptime % 3600) / 60);
    display.print("m");
  }
 
  display.display();
}

void controlRelay(int channel, bool state) {
  if (channel >= 0 && channel < 3) {
    channels[channel].relayState = state;
    digitalWrite(RELAY_CH1_PIN + channel, state ? HIGH : LOW);
   
    Serial.print(channels[channel].name);
    Serial.print(" relay: ");
    Serial.println(state ? "ON" : "OFF");
   
    tone(BUZZER_PIN, state ? 1200 : 800, 100);
  }
}

// Web Server Handlers
void handleRoot() {
  String html = "<!DOCTYPE html><html><head>";
  html += "<meta name='viewport' content='width=device-width,initial-scale=1'>";
  html += "<meta http-equiv='refresh' content='5'>";
  html += "<title>Energy Monitor</title>";
  html += "<style>";
  html += "body{margin:0;font-family:Arial;background:#1a1a2e;color:#eee;padding:20px}";
  html += ".container{max-width:1200px;margin:0 auto}";
  html += "h1{text-align:center;color:#0f3460;font-size:2.5em;margin-bottom:10px}";
  html += ".subtitle{text-align:center;opacity:0.8;margin-bottom:30px}";
  html += ".stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:20px;margin-bottom:30px}";
  html += ".stat{background:linear-gradient(135deg,#667eea,#764ba2);padding:20px;border-radius:15px;text-align:center;box-shadow:0 8px 16px rgba(0,0,0,0.3)}";
  html += ".stat-value{font-size:2.5em;font-weight:bold;margin:10px 0}";
  html += ".stat-label{font-size:0.9em;opacity:0.9}";
  html += ".stat-unit{font-size:0.5em;opacity:0.8}";
  html += ".channels{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:20px;margin-bottom:20px}";
  html += ".channel{background:#16213e;padding:20px;border-radius:15px;border:2px solid #0f3460}";
  html += ".channel h3{margin:0 0 15px 0;color:#667eea}";
  html += ".channel-stat{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(255,255,255,0.1)}";
  html += ".progress-bar{background:rgba(255,255,255,0.1);height:10px;border-radius:5px;overflow:hidden;margin:10px 0}";
  html += ".progress-fill{height:100%;background:linear-gradient(90deg,#667eea,#764ba2);transition:width 0.3s}";
  html += ".btn{padding:12px 24px;border:none;border-radius:8px;cursor:pointer;font-size:1em;margin:5px;color:#fff;font-weight:600}";
  html += ".btn-on{background:#4CAF50}";
  html += ".btn-off{background:#f44336}";
  html += ".btn-reset{background:#2196F3}";
  html += ".controls{text-align:center;margin-top:20px}";
  html += ".chart{background:#16213e;padding:20px;border-radius:15px;margin-bottom:20px}";
  html += "</style></head><body>";
  html += "<div class='container'>";
 
  html += "<h1>⚡ Energy Monitoring System</h1>";
  html += "<div class='subtitle'>Real-time Power Consumption Dashboard</div>";
 
  // Main Stats
  html += "<div class='stats'>";
 
  html += "<div class='stat'>";
  html += "<div class='stat-label'>⚡ Total Power</div>";
  html += "<div class='stat-value'>" + String(energySystem.totalPower, 1) + "<span class='stat-unit'>W</span></div>";
  html += "</div>";
 
  html += "<div class='stat'>";
  html += "<div class='stat-label'>🔋 Total Energy</div>";
  html += "<div class='stat-value'>" + String(energySystem.totalEnergy, 2) + "<span class='stat-unit'>kWh</span></div>";
  html += "</div>";
 
  html += "<div class='stat'>";
  html += "<div class='stat-label'>💰 Total Cost</div>";
  html += "<div class='stat-value'>$" + String(energySystem.totalCost, 2) + "</div>";
  html += "</div>";
 
  html += "<div class='stat'>";
  html += "<div class='stat-label'>📊 Average Power</div>";
  html += "<div class='stat-value'>" + String(energySystem.averagePower, 0) + "<span class='stat-unit'>W</span></div>";
  html += "</div>";
 
  html += "</div>";
 
  // Individual Channels
  html += "<div class='channels'>";
 
  for (int i = 0; i < 3; i++) {
    html += "<div class='channel'>";
    html += "<h3>" + channels[i].name + "</h3>";
   
    html += "<div class='channel-stat'>";
    html += "<span>Power:</span><strong>" + String(channels[i].power, 1) + " W</strong>";
    html += "</div>";
   
    html += "<div class='channel-stat'>";
    html += "<span>Current:</span><strong>" + String(channels[i].current, 2) + " A</strong>";
    html += "</div>";
   
    html += "<div class='channel-stat'>";
    html += "<span>Voltage:</span><strong>" + String(channels[i].voltage, 1) + " V</strong>";
    html += "</div>";
   
    html += "<div class='channel-stat'>";
    html += "<span>Energy:</span><strong>" + String(channels[i].energy, 3) + " kWh</strong>";
    html += "</div>";
   
    html += "<div class='channel-stat'>";
    html += "<span>Cost:</span><strong>$" + String(channels[i].cost, 2) + "</strong>";
    html += "</div>";
   
    // Power bar
    int powerPercent = constrain((channels[i].power / 1000.0) * 100, 0, 100);
    html += "<div class='progress-bar'><div class='progress-fill' style='width:" + String(powerPercent) + "%'></div></div>";
   
    // Relay control
    html += "<div style='margin-top:15px;text-align:center'>";
    html += "<span>Status: " + String(channels[i].relayState ? "ON" : "OFF") + "</span><br>";
    if (channels[i].relayState) {
      html += "<button onclick=\"window.location='/relay" + String(i+1) + "/off'\" class='btn btn-off'>Turn OFF</button>";
    } else {
      html += "<button onclick=\"window.location='/relay" + String(i+1) + "/on'\" class='btn btn-on'>Turn ON</button>";
    }
    html += "</div>";
   
    html += "</div>";
  }
 
  html += "</div>";
 
  // Controls
  html += "<div class='controls'>";
  html += "<button onclick=\"window.location='/reset'\" class='btn btn-reset'>🔄 Reset Counters</button>";
  html += "</div>";
 
  html += "<div style='text-align:center;margin-top:30px;opacity:0.5;font-size:0.9em'>";
  unsigned long uptime = (millis() - energySystem.startTime) / 1000;
  html += "Uptime: " + String(uptime / 3600) + "h " + String((uptime % 3600) / 60) + "m | ";
  html += "Rate: $" + String(energySystem.costPerKWh, 2) + "/kWh | ";
  html += "IP: " + WiFi.localIP().toString();
  html += "</div>";
 
  html += "</div></body></html>";
 
  server.send(200, "text/html", html);
}

void handleStatus() {
  String json = "{";
  json += "\"totalPower\":" + String(energySystem.totalPower, 2) + ",";
  json += "\"totalEnergy\":" + String(energySystem.totalEnergy, 4) + ",";
  json += "\"totalCost\":" + String(energySystem.totalCost, 2) + ",";
  json += "\"averagePower\":" + String(energySystem.averagePower, 2) + ",";
  json += "\"channels\":[";
 
  for (int i = 0; i < 3; i++) {
    if (i > 0) json += ",";
    json += "{";
    json += "\"name\":\"" + channels[i].name + "\",";
    json += "\"voltage\":" + String(channels[i].voltage, 2) + ",";
    json += "\"current\":" + String(channels[i].current, 3) + ",";
    json += "\"power\":" + String(channels[i].power, 2) + ",";
    json += "\"energy\":" + String(channels[i].energy, 4) + ",";
    json += "\"cost\":" + String(channels[i].cost, 2) + ",";
    json += "\"relay\":" + String(channels[i].relayState ? "true" : "false");
    json += "}";
  }
 
  json += "]}";
 
  server.send(200, "application/json", json);
}

void handleHistory() {
  String json = "[";
  for (int i = 0; i < MAX_HISTORY; i++) {
    if (i > 0) json += ",";
    json += String(powerHistory[i], 1);
  }
  json += "]";
 
  server.send(200, "application/json", json);
}

void handleReset() {
  for (int i = 0; i < 3; i++) {
    channels[i].energy = 0;
    channels[i].cost = 0;
    channels[i].peakPower = 0;
    channels[i].alertCount = 0;
  }
 
  energySystem.totalEnergy = 0;
  energySystem.totalCost = 0;
  energySystem.lastReset = millis();
 
  Serial.println("✓ Energy counters reset");
 
  server.sendHeader("Location", "/");
  server.send(303);
}

How to Test:

1. Simulate Power Consumption:

Rotate "Voltage" to middle → Sets ~220V

Rotate "Current CH1" up        → Living Room uses power
Rotate "Current CH2" up        → Kitchen uses power  
Rotate "Current CH3" up        → Bedroom uses power

Watch the system calculate:
Power = Voltage × Current × 0.95
Energy += Power × Time
Cost = Energy × $0.12/kWh

2. Monitor on OLED:

ENERGY MONITOR

Power: 1250 W      ← Total watts
Energy: 0.52 kWh   ← Total energy
Cost: $0.06        ← Total cost
L:450 K:600 B:200  ← Per channel
Up: 0h 25m         ← Uptime

Trigger Alerts:

Increase current potentiometers
↓
Total power > 2000W
↓
 WARNING displayed
Red LED turns ON
Buzzer beeps

Cost Calculation Example:

Living Room: 450W for 1 hour
Energy = 450W / 1000 = 0.45 kWh
Cost = 0.45 kWh × $0.12 = $0.054
Kitchen: 600W for 2 hours  
Energy = 600W / 1000 × 2h = 1.2 kWh
Cost = 1.2 kWh × $0.12 = $0.144
Total Cost: $0.198 ≈ $0.20

Power Calculations:

Formula: Power (W) = Voltage (V) × Current (A) × Power Factor
Example:
Voltage: 220V
Current: 5A
Power Factor: 0.95
Power = 220 × 5 × 0.95 = 1045W

Alert System:

ThresholdResponse
2000WHigh Power Warning
3000W Critical Alert + Buzzer
15A Overcurrent per channel

When triggered:

  • Red Alert LED turns ON
  • Buzzer sounds (2000 Hz)
  • Serial Monitor shows warning
  • Dashboard displays alert

Web Dashboard Features:

Statistics Cards:

  • Total Power (real-time watts)
  • Total Energy (cumulative kWh)
  • Total Cost ($ spent)
  • Average Power (mean consumption)

Channel Cards: Each shows:

  • Power, Current, Voltage
  • Energy consumed (kWh)
  • Cost ($)
  • Visual progress bar
  • ON/OFF control button

Controls:

  • Reset all counters
  • Turn channels ON/OFF
  • View history data

API Endpoints:

GET /status    → JSON with all readings
GET /history   → Last 50 power values
GET /relay1/on → Turn Living Room ON
GET /relay2/off → Turn Kitchen OFF
GET /reset     → Reset energy counters

Quick Setup:

  1. Wokwi → New ESP32 Project
  2. Copy sketch.ino code
  3. Replace diagram.json
  4. Add Libraries:
    • Adafruit GFX
    • Adafruit SSD1306
  5. Start! → Check IP → Open Dashboard

Perfect For:

  • Home energy monitoring
  • Appliance power testing
  • Electricity bill reduction
  • Solar system tracking
  • Industrial monitoring
  • Smart home integration
  • IoT learning projects

Real-World Applications:

Track actual consumption:

  • Refrigerator: ~150W
  • Air conditioner: ~1500W
  • Microwave: ~1200W
  • TV: ~100W
  • Computer: ~300W

Monthly cost example:

Refrigerator: 150W × 24h × 30d = 108 kWh
Cost: 108 kWh × $0.12 = $12.96/month

The system provides complete energy monitoring with power calculations, cost tracking, and remote control capabilities!

Comments