IoT Smart Energy Meter Using ESP32 and Blynk 2.0 Coding Explanation

Copy the code by clicking the “Copy Icon” situated in the righ upper corner of the code block.

#define BLYNK_TEMPLATE_ID "*********"  // Define Blynk template ID
#define BLYNK_TEMPLATE_NAME "*********"  // Define Blynk template name
#define BLYNK_PRINT Serial  // Enable Serial printing for Blynk debug information

#include "EmonLib.h"  // Include EmonLib for energy monitoring
#include <EEPROM.h>  // Include EEPROM library for storing data
#include <WiFi.h>  // Include WiFi library for network connectivity
#include <BlynkSimpleEsp32.h>  // Include Blynk library for ESP32
#include <Wire.h>  // Include Wire library for I2C communication
#include <LiquidCrystal_I2C.h>  // Include LiquidCrystal_I2C library for LCD display
#include <HTTPClient.h>  // Include HTTPClient library for HTTP requests
#include <ArduinoJson.h>  // Include ArduinoJson library for JSON handling

LiquidCrystal_I2C lcd(0x27, 16, 2);  // Initialize LCD with I2C address 0x27 and size 16x2

// Define your Telegram bot token and chat ID
const char* telegramBotToken = "*********";  // Telegram bot token
const char* telegramChatID = "*********";  // Telegram chat ID

// Constants for calibration
const float vCalibration = 42.5;  // Voltage calibration factor
const float currCalibration = 1.80;  // Current calibration factor

// Blynk and WiFi credentials
const char auth[] = "*********";  // Blynk authentication token
const char ssid[] = "*********";  // WiFi SSID
const char pass[] = "*********";  // WiFi password

// EnergyMonitor instance
EnergyMonitor emon;  // Create an instance of EnergyMonitor

// Timer for regular updates
BlynkTimer timer;  // Create a Blynk timer instance

// Variables for energy calculation
float kWh = 0.0;  // Variable to store energy consumed in kWh
float cost = 0.0;  // Variable to store cost of energy consumed
const float ratePerkWh = 6.5;  // Cost rate per kWh
unsigned long lastMillis = millis();  // Variable to store last time in milliseconds

// EEPROM addresses for each variable
const int addrKWh = 12;  // EEPROM address for kWh
const int addrCost = 16;  // EEPROM address for cost

// Display page variable
int displayPage = 0;  // Variable to track current LCD display page

// Reset button pin
const int resetButtonPin = 4;  // Pin for reset button (change to your button pin)

// Function prototypes
void sendEnergyDataToBlynk();  // Prototype for sending energy data to Blynk
void readEnergyDataFromEEPROM();  // Prototype for reading energy data from EEPROM
void saveEnergyDataToEEPROM();  // Prototype for saving energy data to EEPROM
void updateLCD();  // Prototype for updating LCD display
void changeDisplayPage();  // Prototype for changing LCD display page
void sendBillToTelegram();  // Prototype for sending bill to Telegram
void resetEEPROM();  // Prototype for resetting EEPROM data

void setup() {
  WiFi.begin(ssid, pass);  // Start WiFi connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);  // Wait for WiFi connection
  }
  Blynk.begin(auth, ssid, pass);  // Start Blynk connection

  // Initialize the LCD
  lcd.init();  // Initialize the LCD
  lcd.backlight();  // Turn on LCD backlight

  // Initialize EEPROM
  EEPROM.begin(32);  // Initialize EEPROM with 32 bytes of storage

  // Initialize the reset button pin
  pinMode(resetButtonPin, INPUT_PULLUP);  // Set reset button pin as input with pull-up resistor

  // Read stored data from EEPROM
  readEnergyDataFromEEPROM();  // Read energy data from EEPROM

  // Setup voltage and current inputs
  emon.voltage(35, vCalibration, 1.7);  // Configure voltage measurement: input pin, calibration, phase shift
  emon.current(34, currCalibration);  // Configure current measurement: input pin, calibration

  // Setup timers
  timer.setInterval(2000L, sendEnergyDataToBlynk);  // Set timer to send energy data to Blynk every 2 seconds
  timer.setInterval(2000L, changeDisplayPage);  // Set timer to change display page every 2 seconds
  timer.setInterval(60000L, sendBillToTelegram);  // Set timer to send bill to Telegram every 60 seconds
}

void loop() {
  Blynk.run();  // Run Blynk
  timer.run();  // Run timers

  // Check if the reset button is pressed
  if (digitalRead(resetButtonPin) == LOW) {  // If reset button is pressed (assuming button press connects to ground)
    delay(200);  // Debounce delay
    resetEEPROM();  // Reset EEPROM data
  }
}

void sendEnergyDataToBlynk() {
  emon.calcVI(20, 2000);  // Calculate voltage and current: number of half wavelengths (crossings), time-out
  float Vrms = emon.Vrms;  // Get root mean square voltage
  float Irms = emon.Irms;  // Get root mean square current
  float apparentPower = emon.apparentPower;  // Get apparent power

  // Calculate energy consumed in kWh
  unsigned long currentMillis = millis();  // Get current time in milliseconds
  kWh += apparentPower * (currentMillis - lastMillis) / 3600000000.0;  // Update kWh
  lastMillis = currentMillis;  // Update last time

  // Calculate the cost based on the rate per kWh
  cost = kWh * ratePerkWh;  // Calculate cost

  // Save the latest values to EEPROM
  saveEnergyDataToEEPROM();  // Save energy data to EEPROM

  // Send data to Blynk
  Blynk.virtualWrite(V0, Vrms);  // Send voltage to Blynk virtual pin V0
  Blynk.virtualWrite(V1, Irms);  // Send current to Blynk virtual pin V1
  Blynk.virtualWrite(V2, apparentPower);  // Send apparent power to Blynk virtual pin V2
  Blynk.virtualWrite(V3, kWh);  // Send energy in kWh to Blynk virtual pin V3
  Blynk.virtualWrite(V4, cost);  // Send cost to Blynk virtual pin V4

  // Update the LCD with the new values
  updateLCD();  // Update LCD display
}

void readEnergyDataFromEEPROM() {
  EEPROM.get(addrKWh, kWh);  // Read kWh from EEPROM
  EEPROM.get(addrCost, cost);  // Read cost from EEPROM

  // Initialize to zero if values are invalid
  if (isnan(kWh)) {
    kWh = 0.0;  // Set kWh to 0 if invalid
    saveEnergyDataToEEPROM();  // Save to EEPROM
  }
  if (isnan(cost)) {
    cost = 0.0;  // Set cost to 0 if invalid
    saveEnergyDataToEEPROM();  // Save to EEPROM
  }
}

void saveEnergyDataToEEPROM() {
  EEPROM.put(addrKWh, kWh);  // Save kWh to EEPROM
  EEPROM.put(addrCost, cost);  // Save cost to EEPROM
  EEPROM.commit();  // Commit EEPROM changes
}

void updateLCD() {
  lcd.clear();  // Clear LCD display
  if (displayPage == 0) {
    lcd.setCursor(0, 0);  // Set cursor to first row
    lcd.printf("V:%.fV I: %.fA", emon.Vrms, emon.Irms);  // Print voltage and current
    lcd.setCursor(0, 1);  // Set cursor to second row
    lcd.printf("P: %.f Watt", emon.apparentPower);  // Print power and energy
  } else if (displayPage == 1) {
    lcd.setCursor(0, 0);  // Set cursor to first row
    lcd.printf("Energy: %.2fkWh", kWh);  // Print energy
    lcd.setCursor(0, 1);  // Set cursor to second row
    lcd.printf("Cost: %.2f", cost);  // Print cost
  }
}

void changeDisplayPage() {
  displayPage = (displayPage + 1) % 2;  // Change display page
  updateLCD();  // Update LCD display
}

void sendBillToTelegram() {
  String message = "Total Energy Consumed: " + String(kWh, 2) + " kWh\nTotal Cost: ₹" + String(cost, 2);  // Create message
  HTTPClient http;  // Create HTTP client
  http.begin("https://api.telegram.org/bot" + String(telegramBotToken) + "/sendMessage");  // Begin HTTP request
  http.addHeader("Content-Type", "application/json");  // Add header

  DynamicJsonDocument jsonDoc(256);  // Create JSON document
  jsonDoc["chat_id"] = telegramChatID;  // Set chat ID
  jsonDoc["text"] = message;  // Set message text

  String jsonString;  // Create JSON string
  serializeJson(jsonDoc, jsonString);  // Serialize JSON document
  int httpCode = http.POST(jsonString);  // Send HTTP POST request

  // Optional: Handle HTTP errors here
  http.end();  // End HTTP request
}

void resetEEPROM() {
  kWh = 0.0;  // Reset kWh to 0
  cost = 0.0;  // Reset cost to 0
  saveEnergyDataToEEPROM();  // Save to EEPROM
}

This project is a Wi-Fi-enabled energy monitoring system built using the ESP32 microcontroller, capable of:

  • Measuring voltage, current, and apparent power,
  • Calculating energy usage in kilowatt-hours (kWh) and cost,
  • Displaying data on an I2C LCD display,
  • Sending data to the Blynk IoT mobile app,
  • Sending bills/notifications to a Telegram chat, and
  • Storing data persistently in EEPROM, even after reboot or power loss.

1. Define Blynk Template and Include Libraries

#define BLYNK_TEMPLATE_ID "*********"
#define BLYNK_TEMPLATE_NAME "*********"
#define BLYNK_PRINT Serial

These lines define the template ID and name assigned by the Blynk IoT platform. These are required for device-cloud identification.

  • BLYNK_PRINT Serial: This line allows debug output to be printed to the Serial Monitor, which is useful for troubleshooting during development.

Libraries Included:

#include "EmonLib.h"
#include <EEPROM.h>
#include <WiFi.h>
#include <BlynkSimpleEsp32.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

What each library does:

  • EmonLib: For measuring voltage, current, and calculating power using sensors.
  • EEPROM: Allows storing variables in memory even when the device is restarted.
  • WiFi: Used to connect the ESP32 to a wireless network.
  • BlynkSimpleEsp32: Connects the ESP32 to the Blynk IoT platform for remote monitoring and control.
  • Wire + LiquidCrystal_I2C: For communicating with an I2C-compatible LCD screen.
  • HTTPClient + ArduinoJson: Used to send formatted data (JSON) via HTTP POST to the Telegram Bot API.

2. Hardware Components Initialization

LiquidCrystal_I2C lcd(0x27, 16, 2);
  • Initializes the I2C LCD with address 0x27, size 16x2 (16 columns, 2 rows).
const char* telegramBotToken = "*********";
const char* telegramChatID = "*********";
  • Sets your Telegram bot’s access token and the chat ID where messages will be sent.

3. Constants and Configuration Parameters

const float vCalibration = 42.5;
const float currCalibration = 1.80;
  • Calibration values to fine-tune voltage and current measurements from the sensors.
const char auth[] = "*********";
const char ssid[] = "*********";
const char pass[] = "*********";
  • Blynk authentication token and your Wi-Fi SSID/password.
EnergyMonitor emon;
BlynkTimer timer;
  • emon: Object from EmonLib to handle power measurements.
  • timer: Blynk’s built-in timer object, used for running functions periodically.

4. Energy and Billing Variables

float kWh = 0.0;
float cost = 0.0;
const float ratePerkWh = 6.5;
unsigned long lastMillis = millis();
  • kWh: Stores the total energy consumed.
  • cost: Total cost calculated using kWh * ratePerkWh.
  • ratePerkWh: The price of electricity per kilowatt-hour (₹6.5 in this case).
  • lastMillis: Used to calculate the time between readings to accurately compute energy.

5. EEPROM Storage Addresses

const int addrKWh = 12;
const int addrCost = 16;
  • EEPROM storage addresses to save kWh and cost. These are memory locations on the chip where data is stored non-volatilely.

6. Display & Input

int displayPage = 0;
const int resetButtonPin = 4;
  • displayPage: Tracks whether the LCD should show voltage/current or energy/cost.
  • resetButtonPin: Connected to a physical button to manually reset the stored energy and cost.

7. Function Prototypes

Before defining the functions, we declare them here so that they can be referenced from setup() and loop().

8. setup() – Initialize Everything

void setup() {
  WiFi.begin(ssid, pass);
  ...
}

What it does:

  • Connects to Wi-Fi and Blynk.
  • Initializes the LCD and enables backlight.
  • Starts EEPROM with 32 bytes.
  • Reads previous energy & cost from EEPROM.
  • Configures sensor pins for voltage and current input.
  • Initializes timers to:
    • Send data to Blynk every 2 seconds.
    • Change LCD display page every 2 seconds.
    • Send bill to Telegram every 60 seconds.
  • Prepares the reset button with a pull-up resistor.

9. loop() – Run Continuously

void loop() {
  Blynk.run();
  timer.run();
  ...
}

Tasks inside the loop:

  • Keep Blynk connected and responsive.
  • Continuously check for scheduled tasks via timer.run().
  • Monitor the reset button; if pressed, it resets kWh and cost in EEPROM.

10. sendEnergyDataToBlynk() – Core Function

void sendEnergyDataToBlynk() {
  emon.calcVI(20, 2000);
  ...
}
  • Collects real-time voltage (Vrms), current (Irms), and apparent power.
  • Calculates energy in kWh: cppCopyEditkWh += apparentPower * (currentMillis - lastMillis) / 3600000000.0; Converts watts and milliseconds into kWh (by dividing by 3.6 billion).
  • Updates the cost: cppCopyEditcost = kWh * ratePerkWh;
  • Saves the values to EEPROM.
  • Sends data to Blynk virtual pins (V0 to V4).
  • Calls updateLCD() to reflect values on the display.

11. EEPROM Functions

readEnergyDataFromEEPROM()

Reads stored values:

EEPROM.get(addrKWh, kWh);
EEPROM.get(addrCost, cost);
  • Initializes to 0.0 if data is corrupted or invalid.

saveEnergyDataToEEPROM()

Stores updated values:

EEPROM.put(addrKWh, kWh);
EEPROM.put(addrCost, cost);
EEPROM.commit();

12. updateLCD() – Display Data

if (displayPage == 0) {
  lcd.printf("V:%.fV I: %.fA", ...);
} else {
  lcd.printf("Energy: %.2fkWh", kWh);
}
  • LCD cycles between two views:
    • Voltage + Current + Power
    • Energy Used + Total Cost

13. changeDisplayPage()

displayPage = (displayPage + 1) % 2;
updateLCD();
  • Called every 2 seconds by a timer.
  • Switches between LCD display pages.

14. sendBillToTelegram()

String message = "Total Energy Consumed: " + String(kWh, 2) + ...
  • Formats a message with total energy and cost.
  • Sends it via HTTP POST to the Telegram Bot API: bashCopyEdithttps://api.telegram.org/bot<token>/sendMessage
  • Uses JSON payload to send to your Telegram chat.

15. resetEEPROM()

kWh = 0.0;
cost = 0.0;
saveEnergyDataToEEPROM();
  • Resets all saved energy data and persists it to EEPROM.

Final Outcome

By running this code, you get a complete Smart Electricity Monitor that:

  • Monitors and logs energy usage.
  • Displays real-time power and cost data.
  • Automatically sends updates to your Blynk app and Telegram chat.
  • Recovers saved data after power loss.
  • Lets you manually reset data with a hardware button.

Add-ons & Suggestions

Want to improve this further? Here are a few ideas:

  • Add over-voltage/current alert notifications via Telegram or Blynk.
  • Integrate Google Sheets logging via Webhooks.
  • Add deep sleep for power savings in solar/battery setups.
  • Add monthly reset or billing using RTC and NTP.
  • Use hall effect sensors for non-invasive current sensing.

Conclusion

The Smart Energy Monitoring System using ESP32, Blynk, LCD, Telegram, and EEPROM is a powerful, compact, and real-time solution for tracking household or industrial electricity usage. By leveraging the ESP32’s Wi-Fi capabilities, this project allows seamless integration with the Blynk IoT platform for mobile monitoring and Telegram Bot for automatic notifications. It also features an I2C LCD for local display and EEPROM for data persistence, ensuring that consumption history and cost are not lost during power outages.

This system not only monitors voltage, current, and apparent power, but also intelligently calculates energy consumption in kWh and the total cost based on a configurable unit rate. The EEPROM functionality makes it a reliable tool for long-term usage, while features like manual reset, periodic updates, and real-time notifications make it user-friendly and practical for everyday energy management.

In conclusion, this project combines IoT, embedded systems, and smart home technology to promote energy awareness, efficiency, and cost-saving habits—all in a scalable and customizable format suitable for DIY enthusiasts, students, and professionals alike.

Leave a Comment