Mains voltage (230 V) — can kill. The low-voltage side (board, sensors, display, relay module) is safe to build yourself. The mains side must be wired by a licensed electrician, and tested with the circuit switched off at the MCB.
- Never switch a motor with a relay module. Motors pull several times their running current at start-up; hobby relay contacts weld or overheat. The relay only drives a contactor’s coil (or your pump starter’s coil input).
- Contactor rated for motors (AC-3) at or above the load’s nameplate current.
- Protect and earth: MCB on live, RCCB/ELCB on the circuit, metal bodies earthed. Switch live only — never neutral or earth.
- Keep the two worlds apart: mains and low-voltage wiring in separate compartments of a closed, non-metallic enclosure; no bare 230 V terminals.
- Fail safe: the code switches the load off when a sensor stops answering. Still fit a manual override at the starter.
Parts list
| Part | Qty | Approx. | Buy |
|---|---|---|---|
| ESP32 DevKit V1 (30-pin) | 1 | ₹380 | Buy ESP32 DevKit V1 (30-pin) |
| ZMPT101B AC voltage sensor module | 1 | ₹180 | Buy ZMPT101B AC voltage sensor module |
| 0.96" SSD1306 I2C OLED (4-pin) | 1 | ₹220 | Buy 0.96" SSD1306 I2C OLED (4-pin) |
| Inline fuse holder + 250 mA fuse (sensor’s mains lead) | 1 | ₹40 | Buy Inline fuse holder + 250 mA fuse (sensor’s mains lead) |
| 5 V USB adapter + cable | 1 | ₹120 | Buy 5 V USB adapter + cable |
| Closed plastic enclosure with a mains terminal block | 1 | ₹200 | Buy Closed plastic enclosure with a mains terminal block |
| Total | ₹1,140 |
Affiliate disclosure: “Buy” links may earn FlashReady a small commission at no extra cost to you. Prices are indicative.
Wiring
| ESP32 DevKit V1 | Part · pin | Wire | Notes |
|---|---|---|---|
| D34 | ZMPT101B AC voltage sensor · OUT | green | ADC1, input-only pin |
| 3V3 | ZMPT101B AC voltage sensor · VCC | red | Powered at 3.3 V so OUT stays inside the ADC range |
| GND | ZMPT101B AC voltage sensor · GND | black | |
| 230 V AC socket via 250 mA fuse · L | ZMPT101B AC voltage sensor · L | brown | Live through the 250 mA fuse — electrician only |
| 230 V AC socket via 250 mA fuse · N | ZMPT101B AC voltage sensor · N | blue | Neutral. The module’s transformer isolates this side from the ESP32 |
| D22 | SSD1306 OLED 0.96″ (128×64, I2C) · SCL | yellow | |
| D21 | SSD1306 OLED 0.96″ (128×64, I2C) · SDA | blue | |
| 3V3 | SSD1306 OLED 0.96″ (128×64, I2C) · VCC | red | |
| GND | SSD1306 OLED 0.96″ (128×64, I2C) · GND | black |
Board quirks: The ZMPT101B’s L/N screw terminals carry 230 V. The module isolates it with a small transformer, but the terminals and solder joints are live — enclose them and never touch the board while it is plugged in. Calibrate against a true-RMS multimeter on the same socket: change voltsPerCount until both agree. Every module’s gain trimmer is set differently. Readings near 0–20 V are shown as "NO SUPPLY", so a power cut isn’t mistaken for very low voltage.
Code
// Mains voltage monitor — ESP32 DevKit V1 + ZMPT101B + SSD1306 OLED
// ZMPT101B OUT on D34 (ADC1, input-only). Module powered from 3V3 so its output can never
// exceed the ESP32's 3.3 V ADC limit. The 230 V side is wired by an electrician only.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
constexpr uint8_t SENSE_PIN = 34;
// Calibrate: compare with a true-RMS multimeter and adjust until the readings match.
float voltsPerCount = 0.33f;
constexpr float LOW_V = 190.0f; // below this, many appliances misbehave
constexpr float HIGH_V = 250.0f; // above this, protect your appliances
Adafruit_SSD1306 oled(128, 64, &Wire, -1);
// RMS over 10 mains cycles (200 ms at 50 Hz).
float readVrms() {
const unsigned long window = 200;
unsigned long t0 = millis();
double sum = 0, sumSq = 0;
unsigned long n = 0;
while (millis() - t0 < window) {
int v = analogRead(SENSE_PIN);
sum += v;
sumSq += (double)v * v;
n++;
}
double mean = sum / n;
double variance = sumSq / n - mean * mean; // removes the module's DC offset
return variance > 0 ? sqrt(variance) * voltsPerCount : 0;
}
void setup() {
Serial.begin(115200);
analogSetPinAttenuation(SENSE_PIN, ADC_11db); // full 0–3.3 V range
Wire.begin(21, 22);
if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) Serial.println("OLED not found at 0x3C");
}
void loop() {
float v = readVrms();
const char* state = v < 20 ? "NO SUPPLY" : v < LOW_V ? "LOW" : v > HIGH_V ? "HIGH" : "NORMAL";
Serial.printf("V=%.1f %s\n", v, state);
oled.clearDisplay();
oled.setTextColor(SSD1306_WHITE);
oled.setTextSize(3);
oled.setCursor(0, 8);
oled.printf("%3.0fV", v);
oled.setTextSize(2);
oled.setCursor(0, 44);
oled.print(state);
oled.display();
delay(800);
}// Mains voltage monitor — ESP32 DevKit V1 + ZMPT101B + SSD1306 OLED
// ZMPT101B OUT on D34 (ADC1, input-only). Module powered from 3V3 so its output can never
// exceed the ESP32's 3.3 V ADC limit. The 230 V side is wired by an electrician only.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
constexpr uint8_t SENSE_PIN = 34;
// Calibrate: compare with a true-RMS multimeter and adjust until the readings match.
float voltsPerCount = 0.33f;
constexpr float LOW_V = 190.0f; // below this, many appliances misbehave
constexpr float HIGH_V = 250.0f; // above this, protect your appliances
Adafruit_SSD1306 oled(128, 64, &Wire, -1);
// RMS over 10 mains cycles (200 ms at 50 Hz).
float readVrms() {
const unsigned long window = 200;
unsigned long t0 = millis();
double sum = 0, sumSq = 0;
unsigned long n = 0;
while (millis() - t0 < window) {
int v = analogRead(SENSE_PIN);
sum += v;
sumSq += (double)v * v;
n++;
}
double mean = sum / n;
double variance = sumSq / n - mean * mean; // removes the module's DC offset
return variance > 0 ? sqrt(variance) * voltsPerCount : 0;
}
void setup() {
Serial.begin(115200);
analogSetPinAttenuation(SENSE_PIN, ADC_11db); // full 0–3.3 V range
Wire.begin(21, 22);
if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) Serial.println("OLED not found at 0x3C");
}
void loop() {
float v = readVrms();
const char* state = v < 20 ? "NO SUPPLY" : v < LOW_V ? "LOW" : v > HIGH_V ? "HIGH" : "NORMAL";
Serial.printf("V=%.1f %s\n", v, state);
oled.clearDisplay();
oled.setTextColor(SSD1306_WHITE);
oled.setTextSize(3);
oled.setCursor(0, 8);
oled.printf("%3.0fV", v);
oled.setTextSize(2);
oled.setCursor(0, 44);
oled.print(state);
oled.display();
delay(800);
}Compiled for esp32 on esp32 3.3.12 with Adafruit SSD1306 2.5.17 · 3 Nov 2026
How it works
Voltage in many Indian homes swings well outside 230 V, and fridges, ACs and pumps don’t like either extreme. This monitor shows the live supply voltage on an OLED and flags LOW below 190 V and HIGH above 250 V, so you know when to switch heavy appliances off or call the stabiliser repairman.
The ZMPT101B module has a small precision transformer and an op-amp. Its output on D34 is a scaled copy of the mains waveform, centred halfway up its supply. Powering the module from 3.3 V keeps that waveform inside the ESP32’s ADC range.
To measure AC properly the sketch takes as many samples as it can in 200 ms (ten 50 Hz cycles). It removes the DC centre by computing the variance, and takes the square root to get the RMS. That is what a true-RMS meter shows, and what appliance ratings refer to. One calibration number, voltsPerCount, turns raw counts into volts.
This project connects to the 230 V supply, so the full safety checklist below applies. The fused mains lead to the module and the enclosure are an electrician’s job.
Troubleshooting
The reading jumps around by tens of volts
Turn the module’s trimmer until the waveform isn’t clipped (watch the Serial Plotter), keep the ESP32 away from the transformer, and average over more cycles.
Always reads 0 V
Check OUT goes to D34 and the module’s VCC is 3V3. If the L/N side isn’t connected or the fuse has blown, the output stays flat.
Reads the right voltage at 230 V but is wrong at 190 V
The ESP32’s ADC isn’t perfectly linear. Calibrate at your usual voltage, or use a two-point correction (one reading at low voltage, one at normal).
Upgrades
- Push a phone alert through ntfy.sh when the voltage stays out of range for a minute.
- Log the voltage every minute to see your feeder’s daily pattern.
- Add a second sensor on the inverter output to compare grid and backup.



