Community project
Smart Energy Monitoring System
K R SATYAJIT
Published August 11, 2026 · Updated August 11, 2026
Generated with AIThis smart energy monitoring system tracks real-time electrical consumption using a PZEM-004T power meter connected to an ESP32 microcontroller. The system displays voltage, current, power draw, and cumulative energy usage on a small OLED screen, with status indicators via LED and buzzer for system feedback.
The guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions that keep the mains-voltage side safely isolated from the low-voltage control electronics. Builders will receive the full firmware code, component pinout details, and safety guidelines for integrating the meter into their electrical monitoring setup.
Wiring diagram
Interactive · read-only
Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.
Parts list
Bill of materials| Component | Qty | Notes |
|---|---|---|
| PZEM-004TPZEM-004T v3.0 + CT clamp | 1 | Multi-function single-phase AC power monitor module measuring voltage (80-260V AC), current (0-100A via CT clamp), active power (0-22kW), active energy (0-9999.99kWh), frequency (45-65Hz), and power factor (0.00-1.00). Communicates via TTL UART using a Modbus-RTU style protocol at 9600 baud. Uses a non-invasive split-core current transformer (CT) for safe current measurement. Logic powered at 5V DC; the AC mains supply powers the internal metering circuitry. Features 247 programmable slave addresses for multi-device Modbus buses. |
| SSD1306 OLED0.96 in, 128x64 | 1 | 0.96 inch 128x64 OLED display with I2C interface |
| 2-Channel 3.3V-to-5V UART Logic-Level Converter2-channel bidirectional | 1 | A small converter that keeps the 5V PZEM serial signals from reaching the ESP32's 3.3V-only GPIO pins. |
| Status LEDGreen | 1 | A small light that turns on when the meter reading succeeds and blinks when it fails. |
| 220 ohm Resistor220 Ω | 1 | A small resistor that limits the status LED current so the LED and ESP32 pin are protected. |
| 3.3V Active Buzzer3.3V active | 1 | A small sounder that beeps to call attention to a meter communication failure. |
| 5V DC Power Adapter5V DC, 2A | 1 | An isolated wall adapter that supplies regulated 5V to the ESP32 and energy meter low-voltage side. |
Assembly
6 stepsKeep the mains side separate
Put the ESP32, OLED, level converter, LED, buzzer, and the low-voltage pins of the PZEM in one insulated low-voltage area. Put the PZEM AC terminals and the appliance wires in a separate closed mains enclosure, with a physical gap between the two areas.
- Tip: Use a labelled enclosure or divider marked LOW VOLTAGE CONTROL and HIGH VOLTAGE AC.
- ⚠ AC mains wiring must be done by qualified electrician. Touching an exposed Live wire can cause fatal electric shock or fire.
Power the low-voltage section
Connect the adapter +5V lead to the ESP32 5V/VIN pin and PZEM VCC pin. Connect the adapter GND lead to ESP32 GND and PZEM GND; all low-voltage ground wires must meet. Do not connect the adapter directly to household AC wiring.
- Tip: +5V → ESP32 VIN/5V and PZEM VCC (power); GND → ESP32 GND and PZEM GND (ground).
- ⚠ Make sure the adapter positive and negative leads are not swapped — reversed power can damage the ESP32 or meter.
Wire the protected meter data connection
Connect PZEM TX to the level converter HV1 and level converter LV1 to ESP32 GPIO16. Connect ESP32 GPIO17 to level converter LV2 and level converter HV2 to PZEM RX. Power the converter with HV → 5V, LV → ESP32 3.3V, and GND → the shared ground.
- Tip: PZEM TX → converter HV1 → GPIO16 (meter data); GPIO17 → converter LV2/HV2 → PZEM RX (meter command); HV → 5V, LV → 3V3, GND → GND (power and ground).
- ⚠ Do not connect a 5V PZEM TX pin straight to GPIO16 — 5V can damage an ESP32 GPIO pin.
Add the small screen
Connect OLED VCC to ESP32 3.3V, OLED GND to ESP32 GND, OLED SDA to GPIO21, and OLED SCL to GPIO22.
- Tip: VCC → 3V3 (power); GND → GND (ground); SDA → GPIO21 (data); SCL → GPIO22 (clock).
- ⚠ Make sure VCC and GND are not swapped — swapped power can damage the screen.
Add the status light and buzzer
Connect GPIO4 to one end of the 220 ohm resistor. Connect the resistor's other end to the LED long leg, then connect the LED short leg to GND. Connect buzzer + to GPIO25 and buzzer - to GND.
- Tip: GPIO4 → 220 ohm resistor → LED long leg (status signal); LED short leg → GND (ground); buzzer + → GPIO25 (alert signal); buzzer - → GND (ground).
- ⚠ The resistor must stay in series with the LED — without it, the LED or board pin can be damaged.
Have the AC wiring completed safely
A qualified electrician should connect protected AC Live and Neutral to the PZEM voltage input terminals, then route Live and Neutral onward to the labelled load: Freezer / AC / Exhaust Fan. Clip the CT clamp around only the single Live wire that goes from the meter area to that load; do not put Neutral or the whole cable inside the clamp.
- Tip: AC Live → PZEM AC-L → load Live (mains measurement); AC Neutral → PZEM AC-N → load Neutral (mains return); CT clamp around only the outgoing Live conductor (current measurement).
- ⚠ Do not work on energized wires. Putting both Live and Neutral through the CT clamp makes the current reading incorrect, and exposed mains terminals can cause fatal shock or fire.
Pin assignments
Board wiring reference| Pin | Connection | Type |
|---|---|---|
| 5V | adapter_5v_1 +5V | power |
| GND | adapter_5v_1 GND | ground |
| 5V | pzem_004t_1 VCC | power |
| GND | pzem_004t_1 GND | ground |
| EXT | pzem_004t_1 TX → 2-Channel 3.3V-to-5V UART Logic-Level Converter HV1 | digital |
| EXT | pzem_004t_1 RX → 2-Channel 3.3V-to-5V UART Logic-Level Converter HV2 | digital |
| EXT | pzem_004t_1 AC-L → HIGH VOLTAGE AC: protected AC Live input terminal | data |
| EXT | pzem_004t_1 AC-N → HIGH VOLTAGE AC: protected AC Neutral input terminal | data |
| EXT | pzem_004t_1 CT → CT clamp around ONLY the Live conductor feeding Freezer / AC / Exhaust Fan | data |
| 5V | level_shifter_1 HV | power |
| 3V3 | level_shifter_1 LV | power |
| GND | level_shifter_1 GND | ground |
| GPIO 16 | level_shifter_1 LV1 | digital |
| GPIO 17 | level_shifter_1 LV2 | digital |
| 3V3 | oled_1 VCC | power |
| GND | oled_1 GND | ground |
| GPIO 21 | oled_1 SDA | i2c |
| GPIO 22 | oled_1 SCL | i2c |
| GPIO 4 | resistor_1 End A | digital |
| EXT | resistor_1 End B → Status LED Anode | digital |
| GND | led_1 Cathode | ground |
| GPIO 25 | buzzer_1 + | digital |
| GND | buzzer_1 - | ground |
Firmware
ESP32#include <Arduino.h>
#include <Wire.h>
#include <PZEM004Tv30.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// Forward declarations
void showReadings();
void readMeter();
constexpr int PZEM_RX_PIN = 16;
constexpr int PZEM_TX_PIN = 17;
constexpr int OLED_SDA_PIN = 21;
constexpr int OLED_SCL_PIN = 22;
constexpr int STATUS_LED_PIN = 4;
constexpr int BUZZER_PIN = 25;
constexpr uint8_t OLED_ADDRESS = 0x3C;
constexpr unsigned long READ_INTERVAL_MS = 2000;
PZEM004Tv30 pzem(Serial2, PZEM_RX_PIN, PZEM_TX_PIN);
Adafruit_SSD1306 display(128, 64, &Wire, -1);
float lastVoltage = NAN;
float lastCurrent = NAN;
float lastPower = NAN;
float lastEnergy = NAN;
bool meterOk = false;
bool oledReady = false;
unsigned long lastReadMs = 0;
void showReadings() {
if (!oledReady) return;
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.println("Lynqraj Energy Lab");
display.drawFastHLine(0, 10, 128, SSD1306_WHITE);
if (!meterOk) {
display.setCursor(0, 20);
display.println("PZEM not responding");
display.println("Check meter power");
display.println("and UART wiring.");
} else {
display.setCursor(0, 15);
display.printf("Voltage: %.1f V\n", lastVoltage);
display.printf("Current: %.2f A\n", lastCurrent);
display.printf("Power: %.0f W\n", lastPower);
display.printf("Energy: %.3f kWh", lastEnergy);
}
display.display();
}
void readMeter() {
lastVoltage = pzem.voltage();
lastCurrent = pzem.current();
lastPower = pzem.power();
lastEnergy = pzem.energy();
meterOk = !isnan(lastVoltage) && !isnan(lastCurrent) && !isnan(lastPower) && !isnan(lastEnergy);
digitalWrite(STATUS_LED_PIN, meterOk ? HIGH : LOW);
digitalWrite(BUZZER_PIN, meterOk ? LOW : HIGH);
if (meterOk) {
Serial.printf("Voltage %.1f V | Current %.2f A | Power %.0f W | Energy %.3f kWh\n", lastVoltage, lastCurrent, lastPower, lastEnergy);
} else {
Serial.println("PZEM read failed. Check 5V power, common GND, level converter, and AC input.");
}
showReadings();
}
void setup() {
pinMode(STATUS_LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(STATUS_LED_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
Serial.begin(115200);
Wire.begin(OLED_SDA_PIN, OLED_SCL_PIN);
oledReady = display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS);
if (!oledReady) Serial.println("OLED not found; monitoring continues over serial.");
readMeter();
}
void loop() {
if (millis() - lastReadMs >= READ_INTERVAL_MS) {
lastReadMs = millis();
readMeter();
}
}“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.
Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.