Build an ESP32 Smart Energy Monitor with Web Dashboard (Voltage, Current, Power & Energy Tracking)

Build an ESP32 Smart Energy Monitor with Web Dashboard (Voltage, Current, Power & Energy Tracking)

By Dinesh D | DIY Electronics & IoT Projects | 2026 

⭐ Introduction

Electricity monitoring is becoming increasingly important in modern homes and industries. Understanding how much power your devices consume helps reduce energy waste, improve efficiency, and lower electricity bills.

In this project, you will learn how to build a smart AC energy monitoring system using ESP32 that can measure:

  • Voltage (V)
  • Current (A)
  • Active Power (W)
  • Apparent Power (VA)
  • Reactive Power (VAR)
  • Power Factor
  • Energy Consumption (Wh)

The system also features:

✔ Real-time web dashboard
✔ LCD display output
✔ Wi-Fi connectivity
✔ Auto-refresh live data

This is a complete IoT-based energy monitoring system suitable for students, hobbyists, and final-year projects.

⚙️ How the Energy Monitor Works

The ESP32 reads analog signals from:

  • ZMPT101B → Voltage sensor
  • ACS712 → Current sensor

The microcontroller processes these signals to calculate:

  • RMS Voltage & Current
  • Real-time Power values
  • Energy consumption over time

Then:

  • Data is shown on a 16x2 LCD
  • A web dashboard is hosted using ESP32 Wi-Fi
  • Users can monitor values live from any browser 

๐Ÿ’ป ESP32 Program

The program includes:

/*
 * ============================================================
 *  ESP32 AC Energy Monitor
 *  Features:
 *    - RMS Voltage & Current measurement
 *    - Power Factor estimation (via zero-cross phase shift)
 *    - Active / Apparent / Reactive Power
 *    - LCD 16x2 I2C display (rotating pages)
 *    - Wi-Fi Web Dashboard (auto-refresh)
 * ============================================================
 *
 *  WIRING SUMMARY
 *  ──────────────────────────────────────────────────────────
 *  Voltage sensor (ZMPT101B)   → GPIO 35  (ADC1_CH7)
 *  Current sensor (ACS712)     → GPIO 34  (ADC1_CH6)
 *  LCD SDA                     → GPIO 21
 *  LCD SCL                     → GPIO 22
 *  LCD I2C address             → 0x27  (change if needed)
 *
 *  LIBRARIES REQUIRED  (install via Arduino Library Manager)
 *  ──────────────────────────────────────────────────────────
 *  - LiquidCrystal_I2C   by Frank de Brabander
 *  - WiFi                (built-in ESP32)
 *  - WebServer           (built-in ESP32)
 * ============================================================
 */

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <WiFi.h>
#include <WebServer.h>
#include <math.h>

// ─────────────────────────────────────────
//  Wi-Fi Credentials  ← CHANGE THESE
// ─────────────────────────────────────────
const char* ssid     = "POCO C71";
const char* password = "12345666";

// ─────────────────────────────────────────
//  Pin Definitions
// ─────────────────────────────────────────
#define VOLTAGE_PIN  35
#define CURRENT_PIN  34

// ─────────────────────────────────────────
//  Sensor Calibration
// ─────────────────────────────────────────
float voltageCal       = 510.0;   // ZMPT101B calibration factor
float sensitivity      = 0.1;     // ACS712-5A: 0.185 V/A | 20A: 0.1 | 30A: 0.066
float currentCalFactor = 0.45;    // Fine-tune until ammeter matches

// ─────────────────────────────────────────
//  Offset variables (calculated at boot)
// ─────────────────────────────────────────
float offsetV = 0;
float offsetI = 0;

// ─────────────────────────────────────────
//  Output variables (smoothed)
// ─────────────────────────────────────────
float voltage      = 0;
float current      = 0;
float activePower  = 0;
float apparentPower= 0;
float reactivePower= 0;
float powerFactor  = 0;
float energyWh     = 0;       // accumulated energy in Wh

// ─────────────────────────────────────────
//  LCD — 16 columns, 2 rows, I2C addr 0x27
// ─────────────────────────────────────────
LiquidCrystal_I2C lcd(0x27, 16, 2);

// ─────────────────────────────────────────
//  Web Server on port 80
// ─────────────────────────────────────────
WebServer server(80);

// ─────────────────────────────────────────
//  Timing
// ─────────────────────────────────────────
unsigned long lastMeasureMs  = 0;
unsigned long lastLCDMs      = 0;
unsigned long startMs        = 0;
int           lcdPage        = 0;

// ═══════════════════════════════════════════════════════════
//  SETUP
// ═══════════════════════════════════════════════════════════
void setup() {
  Serial.begin(115200);

  // ── LCD init ──
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0); lcd.print("  Energy Monitor");
  lcd.setCursor(0, 1); lcd.print("  Initialising..");
  delay(1000);

  // ── Offset calibration ──
  lcd.clear();
  lcd.setCursor(0, 0); lcd.print("Calibrating...");
  lcd.setCursor(0, 1); lcd.print("Remove all loads");

  Serial.println("Calculating Offsets (NO AC & NO LOAD)...");

  for (int i = 0; i < 1500; i++) {
    offsetV += analogRead(VOLTAGE_PIN) * (3.3 / 4095.0);
    delayMicroseconds(200);
  }
  offsetV /= 1500;

  for (int i = 0; i < 1500; i++) {
    offsetI += analogRead(CURRENT_PIN) * (3.3 / 4095.0);
    delayMicroseconds(200);
  }
  offsetI /= 1500;

  Serial.print("offsetV: "); Serial.println(offsetV, 4);
  Serial.print("offsetI: "); Serial.println(offsetI, 4);

  // ── Wi-Fi ──
  lcd.clear();
  lcd.setCursor(0, 0); lcd.print("Connecting WiFi");
  lcd.setCursor(0, 1); lcd.print(ssid);

  WiFi.begin(ssid, password);
  int tries = 0;
  while (WiFi.status() != WL_CONNECTED && tries < 30) {
    delay(500);
    Serial.print(".");
    tries++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nWiFi connected: " + WiFi.localIP().toString());
    lcd.clear();
    lcd.setCursor(0, 0); lcd.print("WiFi connected!");
    lcd.setCursor(0, 1); lcd.print(WiFi.localIP().toString());
    delay(2000);
  } else {
    Serial.println("\nWiFi FAILED — running offline");
    lcd.clear();
    lcd.setCursor(0, 0); lcd.print("WiFi failed.");
    lcd.setCursor(0, 1); lcd.print("Offline mode.");
    delay(2000);
  }

  // ── Web routes ──
  server.on("/",        handleRoot);
  server.on("/data",    handleData);
  server.on("/reset",   handleReset);
  server.begin();

  startMs = millis();
  Serial.println("Ready.");
}

// ═══════════════════════════════════════════════════════════
//  LOOP
// ═══════════════════════════════════════════════════════════
void loop() {
  server.handleClient();

  unsigned long now = millis();

  // ── Measure every 1 second ──
  if (now - lastMeasureMs >= 1000) {
    lastMeasureMs = now;
    takeMeasurement();
    printSerial();
  }

  // ── Rotate LCD page every 3 seconds ──
  if (now - lastLCDMs >= 3000) {
    lastLCDMs = now;
    updateLCD();
    lcdPage = (lcdPage + 1) % 3;
  }
}

// ═══════════════════════════════════════════════════════════
//  MEASUREMENT
//
//  KEY FIX: V and I are read in the SAME loop iteration so
//  they are truly simultaneous. Active power = mean(v * i)
//  then scaled using the same factors as Vrms and Irms.
//
//  Active Power  P = mean(vScaled * iScaled)
//              = mean(vRaw * voltageCal  *  iRaw * (calFactor/sensitivity))
//              = meanP * voltageCal * (calFactor / sensitivity)
//
//  This gives Watts directly — no guesswork.
// ═══════════════════════════════════════════════════════════
void takeMeasurement() {
  const int samples = 600;

  float sumV = 0, sumI = 0, sumP = 0;

  for (int i = 0; i < samples; i++) {
    // ── Read BOTH pins in the same iteration (simultaneous) ──
    float vRaw = analogRead(VOLTAGE_PIN) * (3.3 / 4095.0) - offsetV;
    float iRaw = analogRead(CURRENT_PIN) * (3.3 / 4095.0) - offsetI;

    sumV += vRaw * vRaw;
    sumI += iRaw * iRaw;
    sumP += vRaw * iRaw;   // true instantaneous power accumulation
  }

  float rmsV  = sqrt(sumV / samples);
  float rmsI  = sqrt(sumI / samples);
  float meanP = sumP / samples;

  // ── Voltage (V) ──
  float newVoltage = 0;
  if (rmsV < 0.1) {
    voltage = 0;
  } else {
    newVoltage = rmsV * voltageCal;
    if (newVoltage > 260) newVoltage = voltage;  // spike reject
    if (newVoltage < 180) newVoltage = 0;         // noise floor
    voltage = voltage * 0.9 + newVoltage * 0.1;
  }

  // ── Current (A) ──
  float rawCurrent = (rmsI / sensitivity) * currentCalFactor;

  if (voltage < 10.0) {
    // No AC present — zero everything
    current       = 0;
    activePower   = 0;
    apparentPower = 0;
    reactivePower = 0;
    powerFactor   = 0;
    return;
  }

  if (rawCurrent < 0.15) rawCurrent = 0;
  current = current * 0.7 + rawCurrent * 0.3;

  // ── Active Power (W) ──
  // meanP is in (ADC_volts)^2. Scale it exactly as Vrms and Irms are scaled:
  //   Vscaled = vRaw * voltageCal
  //   Iscaled = iRaw * (currentCalFactor / sensitivity)
  //   P = mean(Vscaled * Iscaled) = meanP * voltageCal * (currentCalFactor / sensitivity)
  float rawActivePower = meanP * voltageCal * (currentCalFactor / sensitivity);

  // Reject negative (capacitive loads read slightly negative — treat as near-unity)
  if (rawActivePower < 0) rawActivePower = -rawActivePower;

  // Noise floor — below 1W treat as zero
  if (rawActivePower < 1.0) rawActivePower = 0;

  activePower = activePower * 0.8 + rawActivePower * 0.2;

  // ── Apparent Power (VA) ──
  apparentPower = voltage * current;

  // Safety clamp — active can never exceed apparent
  if (activePower > apparentPower) activePower = apparentPower;

  // ── Reactive Power (VAR) ──
  reactivePower = sqrt(max(0.0f, apparentPower * apparentPower - activePower * activePower));

  // ── Power Factor ──
  powerFactor = (apparentPower > 1.0) ? (activePower / apparentPower) : 0;
  powerFactor = constrain(powerFactor, 0.0, 1.0);

  // ── Energy accumulation (Wh) — called every ~1 second ──
  energyWh += activePower / 3600.0;
}

// ═══════════════════════════════════════════════════════════
//  LCD DISPLAY  (3 rotating pages)
// ═══════════════════════════════════════════════════════════
void updateLCD() {
  lcd.clear();

  switch (lcdPage) {

    case 0:  // Voltage & Current
      lcd.setCursor(0, 0);
      lcd.print("V:");
      lcd.print(voltage, 1);
      lcd.print("V  I:");
      lcd.print(current, 2);
      lcd.print("A");
      lcd.setCursor(0, 1);
      lcd.print("P:");
      lcd.print(activePower, 1);
      lcd.print("W  PF:");
      lcd.print(powerFactor, 2);
      break;

    case 1:  // Power breakdown
      lcd.setCursor(0, 0);
      lcd.print("App:");
      lcd.print(apparentPower, 1);
      lcd.print("VA");
      lcd.setCursor(0, 1);
      lcd.print("Rea:");
      lcd.print(reactivePower, 1);
      lcd.print("VAR");
      break;

    case 2:  // Energy & IP
      lcd.setCursor(0, 0);
      lcd.print("Energy:");
      lcd.print(energyWh, 2);
      lcd.print("Wh");
      lcd.setCursor(0, 1);
      if (WiFi.status() == WL_CONNECTED) {
        lcd.print(WiFi.localIP().toString());
      } else {
        lcd.print("WiFi offline");
      }
      break;
  }
}

// ═══════════════════════════════════════════════════════════
//  SERIAL OUTPUT
// ═══════════════════════════════════════════════════════════
void printSerial() {
  Serial.print("V: ");    Serial.print(voltage, 1);
  Serial.print(" V | I: "); Serial.print(current, 2);
  Serial.print(" A | P: "); Serial.print(activePower, 1);
  Serial.print(" W | S: "); Serial.print(apparentPower, 1);
  Serial.print(" VA | Q: ");Serial.print(reactivePower, 1);
  Serial.print(" VAR | PF: "); Serial.print(powerFactor, 2);
  Serial.print(" | E: "); Serial.print(energyWh, 3);
  Serial.println(" Wh");
}

// ═══════════════════════════════════════════════════════════
//  WEB SERVER — Main Dashboard
// ═══════════════════════════════════════════════════════════
void handleRoot() {
  String html = R"rawhtml(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Energy Monitor</title>
<style>
  @import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Rajdhani:wght@400;600;700&display=swap');

  :root {
    --bg: #0a0e1a;
    --panel: #0f1628;
    --border: #1e2d50;
    --accent: #00d4ff;
    --accent2: #ff6b35;
    --accent3: #39ff14;
    --warn: #ffcc00;
    --text: #c8d8f0;
    --dim: #4a6080;
    --glow: 0 0 20px rgba(0,212,255,0.3);
  }

  * { box-sizing: border-box; margin: 0; padding: 0; }

  body {
    background: var(--bg);
    color: var(--text);
    font-family: 'Rajdhani', sans-serif;
    min-height: 100vh;
    padding: 20px;
    background-image:
      radial-gradient(ellipse at 20% 20%, rgba(0,212,255,0.04) 0%, transparent 60%),
      radial-gradient(ellipse at 80% 80%, rgba(255,107,53,0.04) 0%, transparent 60%);
  }

  header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px 24px;
    border: 1px solid var(--border);
    border-radius: 8px;
    margin-bottom: 24px;
    background: var(--panel);
    box-shadow: var(--glow);
  }

  header h1 {
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: 3px;
    text-transform: uppercase;
    color: var(--accent);
  }

  .status {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.75rem;
    color: var(--dim);
  }

  .dot {
    width: 8px; height: 8px;
    border-radius: 50%;
    background: var(--accent3);
    box-shadow: 0 0 8px var(--accent3);
    animation: pulse 1.5s ease-in-out infinite;
  }

  @keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.3; }
  }

  .grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px;
    margin-bottom: 24px;
  }

  .card {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 20px;
    position: relative;
    overflow: hidden;
    transition: border-color 0.3s;
  }

  .card:hover { border-color: var(--accent); }

  .card::before {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 2px;
    background: linear-gradient(90deg, transparent, var(--card-accent, var(--accent)), transparent);
  }

  .card.voltage  { --card-accent: var(--accent); }
  .card.current  { --card-accent: var(--accent2); }
  .card.power    { --card-accent: var(--accent3); }
  .card.apparent { --card-accent: var(--warn); }
  .card.reactive { --card-accent: #b44fff; }
  .card.pf       { --card-accent: #ff4fa3; }
  .card.energy   { --card-accent: var(--accent3); }

  .card-label {
    font-size: 0.7rem;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--dim);
    margin-bottom: 8px;
  }

  .card-value {
    font-family: 'Share Tech Mono', monospace;
    font-size: 2.2rem;
    font-weight: 400;
    color: #fff;
    line-height: 1;
  }

  .card-value span {
    font-size: 1rem;
    color: var(--dim);
    margin-left: 4px;
  }

  .card-icon {
    position: absolute;
    top: 16px; right: 16px;
    font-size: 1.4rem;
    opacity: 0.25;
  }

  /* Power Factor bar */
  .pf-bar-wrap {
    margin-top: 12px;
    height: 4px;
    background: var(--border);
    border-radius: 2px;
    overflow: hidden;
  }

  .pf-bar {
    height: 100%;
    background: linear-gradient(90deg, var(--accent2), var(--accent3));
    border-radius: 2px;
    transition: width 0.8s ease;
  }

  /* Trend graph placeholder */
  .graph-card {
    grid-column: 1 / -1;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 20px;
  }

  .graph-card h3 {
    font-size: 0.7rem;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--dim);
    margin-bottom: 16px;
  }

  canvas#chart {
    width: 100% !important;
    height: 160px !important;
  }

  /* Reset button */
  .btn-reset {
    background: transparent;
    border: 1px solid var(--accent2);
    color: var(--accent2);
    padding: 8px 20px;
    border-radius: 4px;
    font-family: 'Rajdhani', sans-serif;
    font-size: 0.85rem;
    letter-spacing: 1px;
    cursor: pointer;
    transition: all 0.2s;
  }

  .btn-reset:hover {
    background: var(--accent2);
    color: #000;
  }

  footer {
    text-align: center;
    color: var(--dim);
    font-size: 0.7rem;
    letter-spacing: 1px;
    margin-top: 20px;
  }
</style>
</head>
<body>

<header>
  <h1>⚡ Energy Monitor</h1>
  <div class="status">
    <div class="dot"></div>
    <span id="ts">LIVE</span>
    &nbsp;&nbsp;
    <button class="btn-reset" onclick="resetEnergy()">RESET ENERGY</button>
  </div>
</header>

<div class="grid">
  <div class="card voltage">
    <div class="card-icon">๐Ÿ”Œ</div>
    <div class="card-label">Voltage</div>
    <div class="card-value" id="v">—<span>V</span></div>
  </div>
  <div class="card current">
    <div class="card-icon">〜</div>
    <div class="card-label">Current</div>
    <div class="card-value" id="i">—<span>A</span></div>
  </div>
  <div class="card power">
    <div class="card-icon">⚡</div>
    <div class="card-label">Active Power</div>
    <div class="card-value" id="p">—<span>W</span></div>
  </div>
  <div class="card apparent">
    <div class="card-icon">๐Ÿ“</div>
    <div class="card-label">Apparent Power</div>
    <div class="card-value" id="s">—<span>VA</span></div>
  </div>
  <div class="card reactive">
    <div class="card-icon">๐Ÿ”„</div>
    <div class="card-label">Reactive Power</div>
    <div class="card-value" id="q">—<span>VAR</span></div>
  </div>
  <div class="card pf">
    <div class="card-icon">๐Ÿ“Š</div>
    <div class="card-label">Power Factor</div>
    <div class="card-value" id="pf">—</div>
    <div class="pf-bar-wrap"><div class="pf-bar" id="pf-bar" style="width:0%"></div></div>
  </div>
  <div class="card energy">
    <div class="card-icon">๐Ÿ”‹</div>
    <div class="card-label">Energy Consumed</div>
    <div class="card-value" id="e">—<span>Wh</span></div>
  </div>
</div>

<div class="graph-card">
  <h3>Active Power — Last 60 seconds</h3>
  <canvas id="chart"></canvas>
</div>

<footer>ESP32 ENERGY MONITOR &nbsp;|&nbsp; AUTO-REFRESH 1s</footer>

<script>
const maxPoints = 60;
const history   = new Array(maxPoints).fill(null);
let   canvas, ctx, W, H;

function initCanvas() {
  canvas = document.getElementById('chart');
  resize();
  window.addEventListener('resize', resize);
}

function resize() {
  W = canvas.offsetWidth;
  H = 160;
  canvas.width  = W;
  canvas.height = H;
}

function drawChart() {
  if (!ctx) ctx = canvas.getContext('2d');
  ctx.clearRect(0, 0, W, H);

  const valid = history.filter(v => v !== null);
  const maxVal = valid.length ? Math.max(...valid, 10) * 1.2 : 100;

  // Grid lines
  ctx.strokeStyle = 'rgba(30,45,80,0.8)';
  ctx.lineWidth = 1;
  for (let g = 0; g <= 4; g++) {
    const y = H - (g / 4) * H;
    ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke();
    ctx.fillStyle = 'rgba(74,96,128,0.7)';
    ctx.font = '10px Share Tech Mono';
    ctx.fillText(Math.round(maxVal * g / 4) + 'W', 4, y - 3);
  }

  // Line
  const step = W / (maxPoints - 1);
  ctx.beginPath();
  ctx.strokeStyle = '#00d4ff';
  ctx.lineWidth = 2;
  ctx.shadowBlur = 8;
  ctx.shadowColor = '#00d4ff';
  let started = false;
  for (let i = 0; i < maxPoints; i++) {
    if (history[i] === null) continue;
    const x = i * step;
    const y = H - (history[i] / maxVal) * H;
    if (!started) { ctx.moveTo(x, y); started = true; }
    else ctx.lineTo(x, y);
  }
  ctx.stroke();
  ctx.shadowBlur = 0;

  // Fill
  ctx.lineTo((maxPoints - 1) * step, H);
  ctx.lineTo(0, H);
  ctx.closePath();
  const grad = ctx.createLinearGradient(0, 0, 0, H);
  grad.addColorStop(0, 'rgba(0,212,255,0.15)');
  grad.addColorStop(1, 'rgba(0,212,255,0)');
  ctx.fillStyle = grad;
  ctx.fill();
}

async function fetchData() {
  try {
    const r = await fetch('/data');
    const d = await r.json();

    document.getElementById('v').innerHTML  = parseFloat(d.v).toFixed(1)  + '<span>V</span>';
    document.getElementById('i').innerHTML  = parseFloat(d.i).toFixed(2)  + '<span>A</span>';
    document.getElementById('p').innerHTML  = parseFloat(d.p).toFixed(1)  + '<span>W</span>';
    document.getElementById('s').innerHTML  = parseFloat(d.s).toFixed(1)  + '<span>VA</span>';
    document.getElementById('q').innerHTML  = parseFloat(d.q).toFixed(1)  + '<span>VAR</span>';
    document.getElementById('pf').innerHTML = parseFloat(d.pf).toFixed(2);
    document.getElementById('e').innerHTML  = parseFloat(d.e).toFixed(2)  + '<span>Wh</span>';

    document.getElementById('pf-bar').style.width = (parseFloat(d.pf) * 100) + '%';
    document.getElementById('ts').textContent = new Date().toLocaleTimeString();

    history.shift();
    history.push(parseFloat(d.p));
    drawChart();
  } catch(e) {
    console.error('Fetch error:', e);
  }
}

async function resetEnergy() {
  await fetch('/reset');
}

initCanvas();
fetchData();
setInterval(fetchData, 1000);
</script>
</body>
</html>
)rawhtml";

  server.send(200, "text/html", html);
}

// ─────────────────────────────────────────
//  /data  → JSON endpoint
// ─────────────────────────────────────────
void handleData() {
  String json = "{";
  json += "\"v\":"  + String(voltage,       1) + ",";
  json += "\"i\":"  + String(current,       2) + ",";
  json += "\"p\":"  + String(activePower,   1) + ",";
  json += "\"s\":"  + String(apparentPower, 1) + ",";
  json += "\"q\":"  + String(reactivePower, 1) + ",";
  json += "\"pf\":" + String(powerFactor,   2) + ",";
  json += "\"e\":"  + String(energyWh,      3);
  json += "}";
  server.send(200, "application/json", json);
}

// ─────────────────────────────────────────
//  /reset  → clear energy counter
// ─────────────────────────────────────────
void handleReset() {
  energyWh = 0;
  server.send(200, "text/plain", "OK");
}

๐Ÿ“ก How to Use the System

  1. Upload the code to ESP32
  2. Connect sensors and LCD
  3. Power ON the system
  4. Connect to Wi-Fi
  5. Open Serial Monitor to get IP
  6. Enter IP in browser

You will see a live energy monitoring dashboard

๐Ÿงช Testing Tips

  • Start with small loads (like bulbs)
  • Adjust calibration values:
    • voltageCal
    • currentCalFactor
  • Compare readings with a multimeter

๐ŸŽฏ Applications

  • Home energy monitoring
  • Smart meter projects
  • Industrial monitoring
  • IoT dashboards
  • College final year projects

๐ŸŽ‰ Final Result

After completing this project, you will have:

✔ A real-time smart energy monitor
✔ Web-based dashboard system
✔ Accurate power measurement
✔ Practical IoT experience


Comments

Popular posts from this blog

๐ŸŽฏ Build Your Own RFID Attendance System Using Arduino + ESP32 + Google Sheets

How to make a Rfid id tag to turn on the Motor

Restoring an RC Car from E-Waste – DIY Bluetooth-Controlled RC Car Using Arduino