Top 5 ESP32 Smart Energy Monitor Projects (With Code & Circuit)

Top 5 ESP32 Smart Energy Monitor Projects (With Code & Circuit)

With electricity bills at record highs, energy monitoring is one of the most practically useful things you can build. These 5 projects let you measure exactly how much power your appliances, solar panels, and batteries are using — in real time, on your phone, or logged to the cloud. All are non-invasive (no mains wiring needed) or use safe low-voltage DC measurements.

Project 1 — Smart Plug Power Meter

Project Description

A PZEM-004T module measures the voltage, current, power, frequency, and energy (kWh) of any mains appliance plugged into it — all in one chip. The ESP32 reads the data over UART and displays it on a web page. Great for finding out which appliances are silently costing you money.

Components: ESP32, PZEM-004T v3.0 module (includes CT clamp), IEC socket and plug for enclosure, breadboard, jumper wires. Note: the PZEM-004T connects to mains — have a qualified electrician build the enclosure if you are unfamiliar with mains wiring.

Circuit Description

PZEM-004TESP32 Pin
TXGPIO 16 (UART2 RX)
RXGPIO 17 (UART2 TX)
5V5V (Vin)
GNDGND

Code

#include <WiFi.h>
#include <WebServer.h>
#include <PZEM004Tv30.h>

PZEM004Tv30 pzem(Serial2, 16, 17);
WebServer server(80);
const char* ssid     = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

void handleRoot() {
  float voltage  = pzem.voltage();
  float current  = pzem.current();
  float power    = pzem.power();
  float energy   = pzem.energy();
  float freq     = pzem.frequency();
  float pf       = pzem.pf();

  String html = "";
  html += "";
  html += "Power Meter";
  html += "";
  html += "

⚡ Smart Power Meter

"; html += ""; html += ""; html += ""; html += ""; html += ""; html += ""; html += ""; html += "
ParameterValue
Voltage" + String(voltage,1) + " V
Current" + String(current,3) + " A
Power" + String(power,1) + " W
Energy" + String(energy,3) + " kWh
Frequency"+ String(freq,1) + " Hz
Power Factor" + String(pf,2) + "
"; server.send(200, "text/html", html); } void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nIP: " + WiFi.localIP().toString()); server.on("/", handleRoot); server.begin(); } void loop() { server.handleClient(); }

Conclusion

The PZEM-004T gives you professional-grade power measurement in a module that costs under £5. Next step: log kWh readings to ThingSpeak every hour and calculate your monthly electricity cost automatically.

Project 2 — Solar Panel Output Monitor

Project Description

Monitor a 12V solar panel's voltage and current output using a voltage divider and an INA219 current sensor. The ESP32 calculates power output in watts, logs daily energy generation, and pushes everything to a ThingSpeak dashboard. See exactly when your panel peaks and how much energy it generated each day.

Components: ESP32, INA219 current/power sensor module, voltage divider (2 x resistors), 12V solar panel, breadboard, jumper wires.

Circuit Description

INA219ESP32 Pin
SDAGPIO 21
SCLGPIO 22
VIN+Solar panel + terminal
VIN–Load + terminal (shunt in series)
VCC3.3V
GNDGND

The INA219 measures up to 26V and 3.2A. For higher current panels, use the INA226 instead.

Code

#include <WiFi.h>
#include <HTTPClient.h>
#include <Adafruit_INA219.h>

Adafruit_INA219 ina219;
const char* ssid       = "YOUR_WIFI_SSID";
const char* password   = "YOUR_WIFI_PASSWORD";
const char* tsApiKey   = "YOUR_THINGSPEAK_WRITE_KEY";

float dailyEnergyWh = 0;
unsigned long lastLog = 0;

void setup() {
  Serial.begin(115200);
  ina219.begin();
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  Serial.println("Solar monitor online.");
}

void loop() {
  float voltage = ina219.getBusVoltage_V();
  float current = ina219.getCurrent_mA() / 1000.0; // convert to A
  float power   = voltage * current;

  // Accumulate energy (Wh) — readings every 60s
  dailyEnergyWh += power / 60.0; // Wh = W * (1/60) hour

  Serial.printf("V:%.2f  I:%.3f A  P:%.2f W  Daily:%.2f Wh\n",
                voltage, current, power, dailyEnergyWh);

  // Push to ThingSpeak every 60 seconds
  if (millis() - lastLog >= 60000) {
    lastLog = millis();
    HTTPClient http;
    String url = "https://api.thingspeak.com/update?api_key=" + String(tsApiKey) +
                 "&field1=" + String(voltage,2) +
                 "&field2=" + String(current,3) +
                 "&field3=" + String(power,2) +
                 "&field4=" + String(dailyEnergyWh,2);
    http.begin(url); http.GET(); http.end();
  }
  delay(60000);
}

Conclusion

Monitoring solar output with real data helps you optimise panel placement and understand seasonal variation. Next step: add a DS3231 RTC to reset the daily energy counter at midnight, and store 30-day history on an SD card.

Project 3 — LiPo / Lead-Acid Battery Health Monitor

Project Description

Monitor a battery's voltage, state of charge (%), and charge/discharge current. A voltage divider scales the battery voltage to the ESP32's 3.3V ADC range; the INA219 measures current direction (charging vs discharging). An OLED shows all readings in real time and a Telegram alert fires when charge drops below 20%.

Components: ESP32, INA219, 0.96" OLED, 2 x resistors for voltage divider (100kΩ + 33kΩ for 12V battery), breadboard, jumper wires.

Circuit Description

ComponentESP32 Pin
INA219 SDA/SCLGPIO 21/22
OLED SDA/SCLGPIO 21/22 (shared bus)
Voltage divider midpointGPIO 34 (ADC)

Voltage divider: 100kΩ from battery+ to GPIO34, 33kΩ from GPIO34 to GND. This scales 0–16V down to 0–3.3V for the ESP32 ADC. Calibrate by measuring actual battery voltage with a multimeter.

Code

#include <Wire.h>
#include <Adafruit_INA219.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <HTTPClient.h>

Adafruit_INA219 ina219;
Adafruit_SSD1306 display(128, 64, &Wire, -1);

const char* ssid     = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* botToken = "YOUR_BOT_TOKEN";
const char* chatID   = "YOUR_CHAT_ID";

#define VOLT_PIN     34
#define VOLT_FACTOR  4.818  // calibrate: (R1+R2)/R2 = (100k+33k)/33k
#define BATT_MAX     12.6   // fully charged LiPo 3S
#define BATT_MIN     10.5   // cutoff voltage
bool lowAlertSent = false;

float batteryPercent(float v) {
  return constrain(map((int)(v*100), (int)(BATT_MIN*100), (int)(BATT_MAX*100), 0, 100), 0, 100);
}

void setup() {
  Serial.begin(115200);
  ina219.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextColor(WHITE);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) delay(500);
}

void loop() {
  int raw     = analogRead(VOLT_PIN);
  float vBatt = (raw / 4095.0) * 3.3 * VOLT_FACTOR;
  float curr  = ina219.getCurrent_mA();
  float pct   = batteryPercent(vBatt);
  String state = (curr > 10) ? "CHG" : (curr < -10) ? "DIS" : "IDLE";

  display.clearDisplay();
  display.setTextSize(1); display.setCursor(0,0); display.println("Battery Monitor");
  display.setTextSize(2); display.setCursor(0,16);
  display.printf("%.1fV", vBatt);
  display.setTextSize(1); display.setCursor(0,40);
  display.printf("%.0f%% | %.0fmA | %s", pct, curr, state.c_str());
  display.display();

  Serial.printf("%.2fV | %.0f%% | %.0fmA | %s\n", vBatt, pct, curr, state.c_str());

  if (pct < 20 && !lowAlertSent) {
    HTTPClient http;
    String url = "https://api.telegram.org/bot" + String(botToken) +
                 "/sendMessage?chat_id=" + chatID +
                 "&text=🔋 Battery LOW: " + String(pct,0) + "% (" + String(vBatt,2) + "V)";
    http.begin(url); http.GET(); http.end();
    lowAlertSent = true;
  }
  if (pct >= 25) lowAlertSent = false;
  delay(5000);
}

Conclusion

Battery monitoring is critical for any off-grid system. Next step: add a relay that disconnects the load at the cutoff voltage to protect the battery from deep discharge, and another relay to disconnect charging at 100% to prevent overcharge.

Project 4 — EV Charger Session Display

Project Description

Monitor your home EV charger's energy usage using a PZEM-004T on the charger's supply line. The ESP32 tracks session start time, kWh consumed, cost, and estimated range added — displayed on a 2.4" TFT screen in your garage. Finally know exactly how much each charge costs.

Components: ESP32, PZEM-004T v3.0, 2.4" ILI9341 TFT display, reed switch on charger cable (to detect plug-in), breadboard, jumper wires.

Circuit Description

ComponentESP32 Pin
PZEM TX/RXGPIO 16/17 (UART2)
TFT MOSI/SCK/CS/DC/RSTGPIO 23/18/15/2/4
Reed switch (charger connected)GPIO 5 (INPUT_PULLUP)

Code

#include <PZEM004Tv30.h>
#include <TFT_eSPI.h>

PZEM004Tv30 pzem(Serial2, 16, 17);
TFT_eSPI tft = TFT_eSPI();

#define REED_PIN      5
#define COST_PER_KWH  0.28  // £/kWh — adjust to your tariff
#define KM_PER_KWH    6.0   // typical EV efficiency (adjust for your car)

float sessionStartEnergy = 0;
unsigned long sessionStart = 0;
bool charging = false;

void setup() {
  Serial.begin(115200);
  tft.init(); tft.setRotation(1);
  tft.fillScreen(TFT_BLACK);
  pinMode(REED_PIN, INPUT_PULLUP);

  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.setTextSize(2);
  tft.setCursor(20, 100); tft.println("Waiting for EV...");
}

void loop() {
  bool plugged = (digitalRead(REED_PIN) == LOW);

  if (plugged && !charging) {
    charging = true;
    sessionStartEnergy = pzem.energy();
    sessionStart = millis();
    tft.fillScreen(TFT_DARKGREEN);
    Serial.println("Charging session started.");
  } else if (!plugged && charging) {
    charging = false;
    tft.fillScreen(TFT_BLACK);
    tft.setCursor(10, 100); tft.setTextSize(2);
    tft.println("Session complete!");
  }

  if (charging) {
    float kwhUsed  = pzem.energy() - sessionStartEnergy;
    float power    = pzem.power();
    float cost     = kwhUsed * COST_PER_KWH;
    float km       = kwhUsed * KM_PER_KWH;
    unsigned long elapsed = (millis() - sessionStart) / 60000;

    tft.fillScreen(TFT_NAVY);
    tft.setTextColor(TFT_WHITE, TFT_NAVY);
    tft.setTextSize(1); tft.setCursor(10, 5); tft.println("EV Charge Session");
    tft.setTextSize(2);
    tft.setCursor(10, 25); tft.printf("%.2f kWh", kwhUsed);
    tft.setCursor(10, 55); tft.printf("%.2f W",   power);
    tft.setCursor(10, 85); tft.printf("%.2f GBP",  cost);
    tft.setCursor(10,115); tft.printf("+%.0f km",  km);
    tft.setTextSize(1);
    tft.setCursor(10,145); tft.printf("Time: %lu min", elapsed);
  }
  delay(2000);
}

Conclusion

Knowing the exact cost of each EV charge session makes a real difference to household budgeting — especially with time-of-use tariffs. Next step: connect to WiFi and log every session to a spreadsheet automatically, or add a schedule relay to only charge during cheap overnight tariff hours.

Project 5 — Whole-Home Power Dashboard

Project Description

Three SCT-013 non-invasive current transformers clipped onto the main consumer unit feed, the solar inverter output, and the EV charger circuit give you a complete picture: total consumption, solar generation, and net grid import/export — all on a live web dashboard. This is the DIY version of commercial whole-home energy monitors costing hundreds of pounds.

Components: ESP32, 3 x SCT-013-030 (30A) current transformers, 3 x 10µF capacitors, 6 x 10kΩ resistors, breadboard, jumper wires.

Circuit Description

CircuitSCT-013ESP32 ADC Pin
Main feedSCT #1GPIO 34
Solar inverterSCT #2GPIO 35
EV chargerSCT #3GPIO 32

Each SCT-013 needs its own 2.5V bias circuit (two 10kΩ resistors from 3.3V to GND, midpoint to ADC pin) and a 10µF bypass capacitor. The SCT-013-030 has a built-in 33Ω burden resistor.

Code

#include <WiFi.h>
#include <WebServer.h>
#include "EmonLib.h"

EnergyMonitor emon1, emon2, emon3;
WebServer server(80);
const char* ssid     = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

#define VOLTAGE      230.0
#define CALIBRATION  111.1

void handleRoot() {
  double i1 = emon1.calcIrms(1480); double p1 = i1 * VOLTAGE;
  double i2 = emon2.calcIrms(1480); double p2 = i2 * VOLTAGE;
  double i3 = emon3.calcIrms(1480); double p3 = i3 * VOLTAGE;
  double net = p1 - p2; // positive = importing, negative = exporting

  String html = "";
  html += "Home Energy";
  html += "";
  html += "

🏠 Home Energy Dashboard

"; html += "
" + String(p1,0) + "W
Total Consumption
"; html += "
" + String(p2,0) + "W
Solar Generation
"; html += "
" + String(p3,0) + "W
EV Charger
"; String netLabel = (net > 0) ? "Importing" : "Exporting"; html += "
" + String(abs(net),0) + "W
Grid " + netLabel + "
"; html += "
"; server.send(200, "text/html", html); } void setup() { Serial.begin(115200); emon1.current(34, CALIBRATION); emon2.current(35, CALIBRATION); emon3.current(32, CALIBRATION); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nDashboard: http://" + WiFi.localIP().toString()); server.on("/", handleRoot); server.begin(); } void loop() { server.handleClient(); }

Conclusion

A whole-home energy dashboard gives you the data to make real decisions — running the dishwasher when solar is peaking, charging the EV overnight on a cheap tariff, or identifying the appliance costing you £50/month. Next step: push readings to Home Assistant over MQTT and set automations that turn on heavy loads when solar generation is high.


More ESP32 & Arduino Project Guides

Post a Comment

0 Comments