Difficulty: Advanced
Time Required: 3-4 hours
Cost: $20-30
ESP Board: ESP32
What You’ll Need
| Component | Cost | Where to Buy |
|---|---|---|
| ESP32 Dev Board | $5-8 | Amazon |
| PZEM-004T V3.0 | $8-12 | Amazon |
| CT Sensor (Optional) | $5-8 | Amazon |
| OLED Display | $3-5 | Amazon |
Circuit Diagram
| ESP32 Pin | PZEM-004T |
|---|---|
| GND | GND |
| GPIO 16 | TX |
| GPIO 17 | RX |
Step 1: Install Libraries
- PZEM004T Library – GitHub
- SoftwareSerial – Built-in
The Complete Code
/*********************************************************************
* ESP32 Energy Monitor with PZEM-004T
*
* Measures: Voltage, Current, Power, Energy, Frequency
* Hardware: ESP32, PZEM-004T V3.0, OLED Display
*
* Libraries Required:
* - PZEM004T (install from Library Manager)
* - Adafruit GFX
* - Adafruit SSD1306
*********************************************************************/
#include
#include
#include
#include
#include
// ============== PZEM CONFIGURATION ==============
#define PZEM_RX 16
#define PZEM_TX 17
PZEM004T pzem(&Serial2);
IPAddress ip(192, 168, 1, 1);
// ============== DISPLAY CONFIGURATION ==============
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ============== WIFI ==============
const char* wifi_ssid = "YOUR_WIFI";
const char* wifi_password = "YOUR_PASSWORD";
// ============== THINGSPEAK ==============
String apiKey = "YOUR_THINGSPEAK_KEY";
String server = "http://api.thingspeak.com/update";
// ============== DATA STRUCTURE ==============
struct EnergyData {
float voltage;
float current;
float power;
float energy;
float frequency;
float pf;
};
EnergyData data;
unsigned long lastUpdate = 0;
const unsigned long UPDATE_INTERVAL = 5000;
void setup() {
Serial.begin(115200);
Serial.println(F("\n======================================"));
Serial.println(F("ESP32 Energy Monitor Starting..."));
Serial.println(F("======================================"));
// Initialize PZEM
pzem.setAddress(ip);
// Initialize Display
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("Display allocation failed"));
}
display.display();
delay(1000);
// Connect WiFi
connectToWiFi();
Serial.println(F("Setup complete!"));
}
void loop() {
if (millis() - lastUpdate > UPDATE_INTERVAL) {
lastUpdate = millis();
readPZEM();
updateDisplay();
sendToThingSpeak();
}
}
void readPZEM() {
// Read all values from PZEM
data.voltage = pzem.voltage(ip);
data.current = pzem.current(ip);
data.power = pzem.power(ip);
data.energy = pzem.energy(ip);
data.frequency = pzem.frequency(ip);
data.pf = pzem.pf(ip);
// Print to Serial
Serial.println(F("========== ENERGY DATA =========="));
Serial.print(F("Voltage: ")); Serial.print(data.voltage); Serial.println(F(" V"));
Serial.print(F("Current: ")); Serial.print(data.current); Serial.println(F(" A"));
Serial.print(F("Power: ")); Serial.print(data.power); Serial.println(F(" W"));
Serial.print(F("Energy: ")); Serial.print(data.energy); Serial.println(F(" kWh"));
Serial.print(F("Frequency: ")); Serial.print(data.frequency); Serial.println(F(" Hz"));
Serial.print(F("Power Factor: ")); Serial.println(data.pf);
Serial.println(F("================================\n"));
}
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(F("ENERGY MONITOR"));
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setCursor(0, 16);
display.print(F("V: ")); display.print(data.voltage, 1); display.println(F(" V"));
display.setCursor(0, 28);
display.print(F("I: ")); display.print(data.current, 2); display.println(F(" A"));
display.setCursor(0, 40);
display.print(F("P: ")); display.print(data.power, 0); display.println(F(" W"));
display.setCursor(0, 52);
display.print(F("E: ")); display.print(data.energy, 3); display.println(F(" kWh"));
display.display();
}
void sendToThingSpeak() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = server + "?api_key=" + apiKey;
url += "&field1=" + String(data.voltage);
url += "&field2=" + String(data.current);
url += "&field3=" + String(data.power);
url += "&field4=" + String(data.energy);
http.begin(url);
http.GET();
http.end();
}
}
void connectToWiFi() {
WiFi.begin(wifi_ssid, wifi_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
Serial.println(F(" Connected!"));
}
Step 2: Calibration
The PZEM-004T requires calibration for accurate readings. See the official documentation for calibration procedures.
Safety Warning
⚠️ DANGER: This project involves mains voltage. Only proceed if you are experienced with electrical work. Always disconnect power before making connections. Use proper insulation and enclosures.
Official Documentation
- PZEM004T Library – Official GitHub
- ThingSpeak – IoT platform
Frequently Asked Questions
Q: Can I measure three-phase power?
A: You would need 3 PZEM modules and additional code to sum readings.
Q: How accurate is PZEM-004T?
A: Typically ±1% for voltage and power under normal conditions.

