Smart Earthing Monitoring System - IoT-Based Autonomous Earth Leakage and Earth Pit Health Monitoring System for Public Lighting Infrastructure

Published Jun 09, 2026
 72 hours to build
 Intermediate

This smart system continuously monitors earth leakage currents and earth pit moisture at public streetlights to protect the public from hidden shock hazards.

display image

Components Used

ESP32 WROOM
WiFi Development Tools - 802.11 ESP32 General Development Kit, embeds ESP32-WROOM-32E, 4MB flash.
1
LCD 16x2 with I2C Interface
LCD 16x2 with I2C Interface
1
Soil Moisture Sensor
Soil moisture sensor is used to measure the water content (moisture) in soil. It is used in agriculture applications, irrigation and gardening systems, etc.
1
Buzzer 5V
Buzzer 5V
1
Red LED 3mm
Standard LEDs - Through Hole Lamp 3mm LC AlGaAs
3
Resistor 330 Ohms
Metal Film Resistors - Through Hole FRN25 5% 330R
3
Connecting Wire Jumper Wires
Connecting Wire Breadboard wires
16
ACS712 Current Sensor
Monitors current flow in the distribution line and helps identify wire breakage by detecting abnormal current conditions.
2
ZMPT101B voltage sensor module
Measuring voltage across load
1
Perfboard
a pre-drilled, dotted prototyping board used to manually arrange, wire, and solder electronic components when building a permanent circuit prototype.
1
Bulb
converts electrical energy into light and heat
2
Bulb holder
Used to hold the bulb
2
Description

Smart Earthing Monitoring System

"In large public areas like parking lots and railway stations, electrical pole lights are equipped with earthing systems for safety. However, if the earthing malfunctions or the earth resistance becomes too high, it can leave dangerous leakage current on the conductor — posing a serious risk of electric shock to the public. Manually inspecting every earthing point requires huge manpower and is time consuming."

"The Smart Earthing Monitoring System addresses this challenge by continuously and automatically monitoring two critical parameters — leakage current and earth resistance — in real time. The end result is a smart, sensor-based system that instantly detects any abnormality, triggers a buzzer and LED alert on-site, and simultaneously sends a notification to the online monitoring dashboard, enabling maintenance staff to identify and resolve the issue quickly — before it becomes a threat to human life."

Step 1: Hardware Components

Before starting the assembly, ensure you have all the necessary components ready.

           Components  Quantity                                                       Photo
ESP32 Wroom 32        1
ACS712 Current Sensor         2
ZMPT101B Voltage Sensor        1
Soil Moisture Sensor         1
16x2 LCD with I2C Module         1
5V Buzzer         1
Red LED's         3
Perf board         1
330ohm Resistor         3
Jumper Wiresas required
Bulb and Bulb holder   2 each

 

Step 2: Microcontroller & Power Setup

  • Fix the ESP32 board securely on the perf board.
  • Then solder the ESP32 board with perf board.
  • Ensure the ESP32 is securely mounted so the micro-USB port is easily accessible to power up the ESP32 and programming

 

Step 3: Sensor Wiring & Pin Mapping

Sensors:

  • ZMPT101B (Voltage): VCC to 5V, GND to GND, OUT to GPIO 34
  • ACS712 (Load Current): VCC to 5V, GND to GND, OUT to GPIO 35
  • ACS712 (Leakage Current): VCC to 5V, GND to GND, OUT to GPIO 33
  • Soil Moisture Sensor: VCC to 3.3V, GND to GND, Analog OUT to GPIO 32

Display & Indicators:

  • 16x2 LCD Display with I2C Module: VCC to 5V, GND to GND, SDA to GPIO 21, SCL to GPIO 22
  • LED (Fault): Anode to GPIO 5 (via resistor)
  • LED (OK): Anode to GPIO 18 (via resistor)
  • LED (Moisture): Anode to GPIO 17 (via resistor)
  • Active Buzzer: Positive terminal to GPIO 23

Safety Warning: When connecting the AC load to the ZMPT101B and ACS712 sensors, ensure all power is completely disconnected.

Step 4: Configuring the Blynk IoT Dashboard

Set up the cloud receiver, before uploading the code.

   Log into the Blynk IoT and create a new Template named "Smart Earthing Monitoring System".

   Go to Datastreams and create five Virtual Pins:

  • V0 (Double): Line Voltage
  • V1 (Double): Load Current
  • V2 (Double): Leakage Current
  • V3 (Integer): Moisture Percentage
  • V4 (String): System Status

   Build your Web Dashboard using three Gauges (for V0, V1, V2), one Level/Gauge (V3), and a Value Display (V4).

   Copy your BLYNK_AUTH_TOKEN from the device info page.

 

Step 5: Uploading the code to the ESP32 

  • Open the Arduino IDE.
  • Ensure you have the ESP32 Board Manager installed, along with the Blynk and LiquidCrystal_I2C libraries.
  • Paste the code into the IDE.
  • Update the Wi-Fi credentials (ssid and password) and paste your unique BLYNK_AUTH_TOKEN at the top of the code.
  • Select your specific ESP32 board model from the Tools menu, choose the correct COM port, and click Upload.
#define BLYNK_TEMPLATE_ID "TMPL38Z--_573"
#define BLYNK_TEMPLATE_NAME "Smart Earthing Monitoring System"
#define BLYNK_PRINT Serial

#include <Wire.h>
#include <WiFi.h>
#include <BlynkSimpleEsp32.h>
#include <LiquidCrystal_I2C.h>

// ================= WIFI & BLYNK =================
char auth[] = "2Ip70ugs9wabVCnyFGyY5bVyYJ0BQ4jO";
const char* ssid = "ss";
const char* password = "12345678";

// ================= PIN DEFINITIONS =================
#define VOLTAGE_PIN        34   // ZMPT101B
#define LOAD_CURRENT_PIN   35   // ACS712 - Load current
#define LEAK_CURRENT_PIN   33   // ACS712 - Leakage current
#define MOISTURE_PIN       32

#define LED_FAULT     5
#define LED_OK        18
#define LED_MOISTURE  17
#define BUZZER        23

// ================= LCD =================
LiquidCrystal_I2C lcd(0x27, 16, 2);

// ================= CONSTANTS =================
#define ADC_MAX 4095.0
#define VREF 3.3
#define ACS_SENSITIVITY 0.100     // ACS712-20A (100 mV/A)
#define LOAD_CAL_FACTOR   0.111
#define LEAK_CAL_FACTOR   0.098

#define LEAKAGE_THRESHOLD 0.30     // 20 mA

#define MOISTURE_DRY 3300
#define MOISTURE_WET 1200

// ================= VARIABLES =================
float Vrms = 0;
float LoadIrms = 0;
float LeakIrms = 0;

float loadOffset = 0;
float leakOffset = 0;

int moistureValue = 0;
unsigned long lastRead = 0;
unsigned long leakStartTime = 0;
unsigned long lastLCD = 0;


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

  analogReadResolution(12);
  analogSetAttenuation(ADC_11db);

  analogSetPinAttenuation(VOLTAGE_PIN, ADC_11db);
  analogSetPinAttenuation(LOAD_CURRENT_PIN, ADC_11db);
  analogSetPinAttenuation(LEAK_CURRENT_PIN, ADC_11db);
  analogSetPinAttenuation(MOISTURE_PIN, ADC_11db);

  pinMode(LED_FAULT, OUTPUT);
  pinMode(LED_OK, OUTPUT);
  pinMode(LED_MOISTURE, OUTPUT);
  pinMode(BUZZER, OUTPUT);

  Wire.begin(21, 22);
  lcd.init();
  lcd.backlight();

  lcd.setCursor(0,0);
  lcd.print("Smart Earthing");
  lcd.setCursor(0,1);
  lcd.print("Initializing...");
  delay(2000);
  lcd.clear();

  calibrateCurrentSensors();

  WiFi.begin(ssid, password);
  Blynk.config(auth);
  Blynk.connect();
}

// ================= LOOP =================
void loop() {
  Blynk.run();


  if (millis() - lastRead >= 1000) {
    lastRead = millis();

    readVoltage();
    readLoadCurrent();
    readLeakageCurrent();
    readMoisture();

if (LoadIrms < 0.05) {LeakIrms = 0.0;}



    if (millis() - lastLCD >= 1000) {
    lastLCD = millis();
    updateLCD();
}

    sendToBlynk();
    handleAlerts();
  }
}

// ================= VOLTAGE RMS =================
void readVoltage() {
  const int samples = 1000;
  float sumSq = 0;

  for (int i = 0; i < samples; i++) {
    float adc = analogRead(VOLTAGE_PIN);
    float voltage = (adc * VREF) / ADC_MAX;
    float ac = voltage - (VREF / 2.0);
    sumSq += ac * ac;
    delayMicroseconds(200);
  }

  float sensorVrms = sqrt(sumSq / samples);
  float VOLT_CAL = 645.71;   // Adjust with multimeter
  Vrms = sensorVrms * VOLT_CAL;
}

// ================= CALIBRATION =================
void calibrateCurrentSensors() {
  const int samples = 1500;
  long sumLoad = 0;
  long sumLeak = 0;

  Serial.println("Calibrating ACS712...");
  Serial.println("REMOVE ALL LOADS!");
  delay(4000);

  for (int i = 0; i < samples; i++) {
    sumLoad += analogRead(LOAD_CURRENT_PIN);
    sumLeak += analogRead(LEAK_CURRENT_PIN);
    delayMicroseconds(200);
  }

  loadOffset = (sumLoad / (float)samples) * VREF / ADC_MAX;
  leakOffset = (sumLeak / (float)samples) * VREF / ADC_MAX;

  Serial.print("Load Offset: ");
  Serial.println(loadOffset, 4);
  Serial.print("Leak Offset: ");
  Serial.println(leakOffset, 4);
}

// ================= LOAD CURRENT =================
void readLoadCurrent() {
  const int samples = 1000;
  float sumSq = 0;

  for (int i = 0; i < samples; i++) {
    float adc = analogRead(LOAD_CURRENT_PIN);
    float voltage = (adc * VREF) / ADC_MAX;
    float ac = voltage - loadOffset;
    float current = (ac / ACS_SENSITIVITY) * LOAD_CAL_FACTOR;
    sumSq += current * current;
    delayMicroseconds(200);
  }

  LoadIrms = sqrt(sumSq / samples);
  if (LoadIrms < 0.01) LoadIrms = 0.0;
}


void readLeakageCurrent() {

  // If no load is flowing, leakage must be zero
  if (LoadIrms < 0.05) {
    LeakIrms = 0.0;
    return;
  }

  const int samples = 1000;
  float sumSq = 0;

  for (int i = 0; i < samples; i++) {
    float adc = analogRead(LEAK_CURRENT_PIN);
    float voltage = (adc * VREF) / ADC_MAX;
    float ac = voltage - leakOffset;
    float current = (ac / ACS_SENSITIVITY) * LEAK_CAL_FACTOR;
    sumSq += current * current;
    delayMicroseconds(200);
  }

  LeakIrms = sqrt(sumSq / samples);

  // HARD clamp
  if (LeakIrms < 0.1) LeakIrms = 0.0;
}



// ================= MOISTURE =================
void readMoisture() {
  int raw = analogRead(MOISTURE_PIN);

  moistureValue = map(raw, MOISTURE_DRY, MOISTURE_WET, 100, 0);
  moistureValue = constrain(moistureValue, 0, 100);
}

// ================= LCD =================
void updateLCD() {
  lcd.clear();

  lcd.setCursor(0,0);
  lcd.print("L:");
  lcd.print(LoadIrms,2);
  lcd.print("A ");

  lcd.print("E:");
  lcd.print(LeakIrms,3);

  lcd.setCursor(0,1);
  lcd.print("Moist:");
  lcd.print(moistureValue);
  lcd.print("%");
}


// ================= BLYNK =================
void sendToBlynk() {
  Blynk.virtualWrite(V0, Vrms);
  Blynk.virtualWrite(V1, LoadIrms);
  Blynk.virtualWrite(V2, LeakIrms);
  Blynk.virtualWrite(V3, moistureValue);

  String alert = "OK";

  if (LeakIrms > LEAKAGE_THRESHOLD && LoadIrms > 0.1) {
    alert = "EARTH FAULT!";
  }else if (moistureValue < 30) {
    alert = "LOW MOISTURE";
  }

  Blynk.virtualWrite(V4, alert);
}

// ================= ALERTS =================
void handleAlerts() {
  digitalWrite(LED_OK, LOW);
  digitalWrite(LED_FAULT, LOW);
  digitalWrite(LED_MOISTURE, LOW);
  digitalWrite(BUZZER, LOW);

  // ---------- EARTH FAULT CONFIRMATION ----------
  if (LeakIrms > LEAKAGE_THRESHOLD && LoadIrms > 0.1) {

    if (leakStartTime == 0) {
      leakStartTime = millis();   // start timing
    }

    if (millis() - leakStartTime >= 3000) {
      // EARTH FAULT CONFIRMED
      digitalWrite(LED_FAULT, HIGH);
      digitalWrite(BUZZER, HIGH);
    }

  } else {
    leakStartTime = 0;   // reset timer
    digitalWrite(LED_OK, HIGH);
  }

  // ---------- MOISTURE ALERT ----------
  if (moistureValue < 30) {
    digitalWrite(LED_MOISTURE, HIGH);
    digitalWrite(BUZZER, HIGH);
  }
}

 

Step 6: Testing & Calibration

  • Wait for the initialization to finish.
  • Turn on your simulated streetlight load (the incandescent bulbs). The LCD and Blynk dashboard should immediately show a stable voltage and a normal load current.
  • Test the Soil Sensor: Remove the moisture probe from the soil. The LED(LED_MOISTURE) should light up, the buzzer will sound, and the dashboard will warn "LOW MOISTURE".
  • Test the Leakage Fault: Carefully introduce your bypass ground fault. The system will wait 3 seconds to verify the fault, then trigger the RED LED(LED_FAULT), the buzzer, and update the cloud status to "EARTH FAULT!".

 

Project Link: https://youtu.be/02oAADcOJzc

Codes

Downloads

Schematic - Smart Earthing Monitoring System Download
Legend of Schematic Download
Comments
Ad