When searching for electrical engineering design project ideas that actually test your grasp of both circuit theory and embedded firmware, skip the blinking LEDs and basic weather stations. Build a True RMS AC Power Analyzer. This project forces you to confront AC phase shifts, ADC non-linearity, discrete sampling math, and mains isolation. It is the ultimate bridge between textbook AC theory and bench-level embedded debugging.
In this guide, we will build a 120V/240V True RMS voltage, current, and power factor meter. We will cover the exact hardware variants, the math behind discrete RMS sampling, and the specific ESP32 ADC quirks that will break your code if you ignore them.
Choosing Your Sensor: Decision Path for AC Measurement
Before ordering parts, you must match your sensor topology to your measurement goals. Average-responding sensors will lie to you when measuring non-linear loads (like LED drivers or PC power supplies). Use this decision table to select the right hardware.
| Measurement Goal | Load Type | Required Topology | Concrete Part Pick |
|---|---|---|---|
| DC Power / Efficiency | DC Motors, Batteries | High-side shunt + I2C ADC | INA219 Breakout |
| AC Apparent Power (VA) | Resistive Heaters | Current Transformer (CT) | SCT-013-000 (100A) |
| AC True RMS & Power Factor | Switch-mode supplies, Motors | Active Voltage Module + Hall Effect | ZMPT101B + ACS712-30A |
Hardware BOM & Spec Sheet
Do not substitute the ESP32 variant. The code below relies on the specific ADC1 channels available on the standard 30-pin DevKit. Pricing reflects typical 2026 market rates for authentic modules.
| Component | Exact Variant / Model | Est. Price | Critical Spec / Note |
|---|---|---|---|
| Microcontroller | ESP32-DevKitC V4 (ESP32-WROOM-32E) | $6.50 | Must have ADC1 exposed (GPIO 32-39) |
| Voltage Sensor | ZMPT101B Active AC Voltage Module | $4.00 | Includes onboard op-amp and trimpot |
| Current Sensor | ACS712-30A Hall Effect Module | $3.50 | 66mV/A sensitivity, 5V VCC required |
| Calibration | 10kΩ Multi-turn Trimpot (Bourns 3296W) | $1.50 | For fine-tuning ZMPT101B offset if needed |
Pin Mapping & AC Theory Integration
The fundamental theory here is discrete sampling. True RMS is not an average; it is the square root of the mean of the squares of the instantaneous values. Mathematically: \( V_{RMS} = \sqrt{\frac{1}{T} \int_0^T v(t)^2 dt} \). In firmware, we approximate this integral by sampling the ADC thousands of times per AC cycle.
CRITICAL ESP32 ADC RULE: You must use ADC1 (GPIO 32, 33, 34, 35, 36, 39). Never use ADC2 (GPIO 0, 2, 4, 12-15, 25-27) for analog reads while WiFi is active. The WiFi driver hijacks ADC2, causing silent read failures. For a deep dive into ESP32 ADC architecture, refer to the official Espressif ADC Oneshot documentation.
| Sensor Pin | ESP32-DevKitC V4 Pin | Function / Theory Note |
|---|---|---|
| ZMPT101B OUT | GPIO 36 (ADC1_CH0) | Input only. No internal pull-up. |
| ACS712 OUT | GPIO 39 (ADC1_CH3) | Input only. Measures bidirectional current. |
| Both VCC | 5V (VIN pin on DevKit) | Op-amps and Hall sensors need 5V, not 3.3V. |
| Both GND | GND | Establish common reference plane. |
Complete ESP32 Firmware (Arduino IDE)
This firmware targets the ESP32-DevKitC V4 (ESP32-WROOM-32E). It uses raw ADC sampling to calculate True RMS and Apparent Power without heavy external libraries, giving you full control over the sampling window. It includes a strict timeout handler to prevent the MCU from hanging if the AC waveform disappears.
// Target Board: ESP32-DevKitC V4 (ESP32-WROOM-32E)
// IDE: Arduino IDE 2.x with 'esp32' board package v2.0.14+
#include <Arduino.h>
#include <math.h>
// --- PIN DEFINITIONS ---
#define PIN_V_SENSOR 36 // ADC1_CH0 (ZMPT101B)
#define PIN_I_SENSOR 39 // ADC1_CH3 (ACS712-30A)
// --- CALIBRATION CONSTANTS ---
// Adjust these based on your multimeter readings during calibration
#define V_CAL_FACTOR 0.0985 // Maps ADC raw to Volts
#define I_CAL_FACTOR 0.0441 // Maps ADC raw to Amps (ACS712 30A = 66mV/A)
#define V_OFFSET 2048 // Theoretical mid-point for 12-bit ADC (3.3V ref)
#define I_OFFSET 2048
// --- SAMPLING PARAMETERS ---
#define SAMPLES_PER_CYCLE 1000
#define TIMEOUT_MS 1000 // Max time to wait for a zero-crossing
void setup() {
Serial.begin(115200);
delay(1000);
// Configure ADC1 for 12-bit resolution
analogReadResolution(12);
analogSetAttenuation(ADC_11db); // Full 3.3V scale
Serial.println("ESP32 True RMS Power Analyzer Initialized.");
}
void loop() {
unsigned long startTime = millis();
// 1. Wait for Voltage Zero-Crossing (Rising edge past midpoint)
int vRaw = analogRead(PIN_V_SENSOR);
while (vRaw >= V_OFFSET) {
vRaw = analogRead(PIN_V_SENSOR);
if (millis() - startTime > TIMEOUT_MS) {
Serial.println("E: ADC Read Timeout - Zero Crossing Not Detected");
return; // Abort this loop iteration
}
}
while (vRaw < V_OFFSET) {
vRaw = analogRead(PIN_V_SENSOR);
if (millis() - startTime > TIMEOUT_MS) {
Serial.println("E: ADC Read Timeout - Zero Crossing Not Detected");
return;
}
}
// 2. Sample one full cycle
unsigned long sampleStart = micros();
double sumV_sq = 0;
double sumI_sq = 0;
double sumP = 0;
for (int i = 0; i < SAMPLES_PER_CYCLE; i++) {
int rawV = analogRead(PIN_V_SENSOR);
int rawI = analogRead(PIN_I_SENSOR);
double vInst = (rawV - V_OFFSET) * V_CAL_FACTOR;
double iInst = (rawI - I_OFFSET) * I_CAL_FACTOR;
sumV_sq += vInst * vInst;
sumI_sq += iInst * iInst;
sumP += vInst * iInst;
// Small delay to space samples across the ~16.6ms (60Hz) cycle
// 16666us / 1000 = ~16us per sample. analogRead takes ~10us.
delayMicroseconds(4);
}
unsigned long sampleEnd = micros();
// 3. Calculate RMS and Power
double Vrms = sqrt(sumV_sq / SAMPLES_PER_CYCLE);
double Irms = sqrt(sumI_sq / SAMPLES_PER_CYCLE);
double P_real = sumP / SAMPLES_PER_CYCLE;
double P_apparent = Vrms * Irms;
double powerFactor = (P_apparent > 0.1) ? (P_real / P_apparent) : 0.0;
// Calculate actual frequency based on sampling time
double cycleTimeSec = (sampleEnd - sampleStart) / 1000000.0;
double freq = 1.0 / cycleTimeSec;
// 4. Output Data
Serial.printf("Vrms: %5.1f V | Irms: %4.2f A | P: %5.1f W | PF: %.2f | Freq: %.1f Hz\n",
Vrms, Irms, P_real, powerFactor, freq);
delay(500); // Update rate limit
}
Debugging: Exact Error Strings & First 3 Checks
When working with mains-referenced analog signals, the ESP32 will inevitably hang or output garbage if the hardware isn't biased correctly. If your serial monitor stalls or prints errors, follow this decision path.
The First Three Things to Check
- VCC Voltage: Measure the 5V pin on the ESP32 with a multimeter. The ZMPT101B and ACS712 op-amps require a stable 5.0V. If your USB port is sagging to 4.6V, the sensor output will clip, destroying the zero-crossing detection.
- DC Offset Bias: Disconnect the AC mains from the ZMPT101B. Read the DC voltage between the ZMPT101B
OUTpin andGND. It must read exactly 2.5V (or half of your VCC). If it reads 0V or 5V, the onboard trimpot is misadjusted or the op-amp is dead. - ADC Pin Selection: Verify you are not using GPIO 25, 26, or 27. Those are ADC2 pins. If WiFi initializes (even in the background), ADC2 reads will return 0 or 4095, breaking the math.
Error: "E: ADC Read Timeout - Zero Crossing Not Detected"
This exact string triggers when the firmware waits longer than 1000ms for the AC voltage waveform to cross the 1.65V midpoint. The ESP32's watchdog hasn't tripped, but the math loop is aborted to prevent division-by-zero or infinite hangs.
Ranked Causes:
- Cause 1 (80%): AC Source is Dead or Disconnected. The ZMPT101B primary side isn't receiving mains voltage. Verify the outlet with a standard digital multimeter. For more on True RMS measurement theory, see this All About Circuits primer on True RMS.
- Cause 2 (15%): ZMPT101B Trimpot is Zeroed. If the blue trimpot on the ZMPT101B is turned all the way down, the secondary waveform amplitude is 0V, meaning it never crosses the digital midpoint threshold. Turn it counter-clockwise 5 full turns.
- Cause 3 (5%): V_OFFSET Constant Mismatch. If your ESP32's internal Vref is unusually low (e.g., 3.1V), the theoretical midpoint of 2048 is wrong. Measure the 3.3V pin, calculate the exact midpoint (e.g., 3.1V / 2 = 1.55V -> 1922 ADC raw), and update
V_OFFSETin the code.
Error: "PF: 0.00" or Negative Power Factor on Resistive Loads
If you plug in a toaster (purely resistive, PF should be 1.0) and the serial monitor shows a PF of 0.4 or negative, your voltage and current waveforms are out of phase in software.
The Fix: The ACS712 and ZMPT101B have different internal low-pass filter delays. You must add a software phase-shift compensation. In the for loop, sample the current sensor before the voltage sensor, or introduce a 2-sample delay to align the peaks physically.
Extending and Simplifying the Build
Depending on your final project requirements, you may need to scale this design up or down.
How to Simplify (For Quick Prototyping)
If you only need to monitor a purely resistive load (like a water heater or incandescent lighting), drop the ZMPT101B entirely. Assume a fixed nominal voltage (e.g., 120V). Use only the ACS712 to measure True RMS current, and multiply by 120 in firmware. This eliminates the dangerous mains-voltage wiring and the zero-crossing detection logic, reducing the code complexity by half.
How to Extend (For Production / High Accuracy)
The ESP32's internal 12-bit SAR ADC is notoriously non-linear at the extremes (near 0 and 4095) and suffers from noise. To elevate this from a bench toy to a reliable engineering tool:
- Upgrade the ADC: Bypass the internal ADC and use an external ADS1115 (16-bit I2C ADC). It provides differential inputs and a stable internal voltage reference, eliminating VCC sag errors.
- Add MQTT Telemetry: Integrate the
PubSubClientlibrary to pushVrms,Irms, andPFto a local Mosquitto broker every 5 seconds. This allows you to graph power consumption over time using Grafana and InfluxDB. - Isolate the I2C Bus: If using an external ADC, place an ISO1540 I2C isolator between the ESP32 and the ADC to maintain strict galvanic isolation between your low-voltage logic and the mains-referenced sensors.
Building a True RMS analyzer forces you to respect the physics of alternating current. By mastering the discrete sampling math and debugging the ESP32's specific ADC quirks, you move beyond copying library code and actually engineer a solution from the silicon up.






