If you want to bridge the gap between abstract AC circuit theory and real-world embedded firmware, the single best build you can tackle is a True RMS AC Power Analyzer. Unlike basic multimeters that assume a perfect sine wave and just scale the peak voltage, a True RMS (Root Mean Square) meter samples the waveform thousands of times per second, squares the values, averages them, and takes the square root. This is the only way to accurately measure power in modern circuits with non-linear loads like LED drivers or switching power supplies.

For projects for electrical engineering students, this build forces you to confront the messy reality of hardware: ADC non-linearity, phase shift errors in transformers, and discrete integration math. Below is the complete blueprint, from the exact bill of materials to the compilable C++ firmware with built-in error handling.

Hardware Spec Sheet and Pin Mapping

To calculate Real Power (Watts), Apparent Power (VA), and Power Factor (PF), we need simultaneous voltage and current sampling. We are targeting the ESP32-WROOM-32E DevKit V1 because its dual-core processor allows us to handle high-speed ADC sampling on one core while the other manages serial output and math.

Component Exact Model / Variant Est. Cost (2026) Role in Circuit
Microcontroller ESP32-WROOM-32E (30-pin DevKit V1) $6.50 Dual-core 240MHz MCU, dual 12-bit ADCs
Voltage Sensor ZMPT101B Module (Active, with op-amp) $3.00 Steps down mains AC, biases to 1.65V DC
Current Sensor SCT-013-030 (30A max, 1V output) $9.00 Split-core CT with internal burden resistor
Biasing Resistors 2x 470kΩ, 1x 10µF Capacitor $0.50 Creates 1.65V virtual ground for CT

Pin Mapping Table

ESP32 Pin Target Component Notes
GPIO 34 (ADC1_CH6) ZMPT101B Analog Out Input only pin, no internal pull-up
GPIO 35 (ADC1_CH7) SCT-013 Signal (via bias network) Input only pin, requires external bias
3V3 ZMPT101B VCC / Bias Network Use the regulated 3.3V out from the ESP32
GND All Module GNDs Ensure common ground across all modules

Step-by-Step Wiring and Mains Safety

⚠️ HIGH VOLTAGE WARNING: The ZMPT101B primary side connects directly to 120V/230V AC mains. Lethal current is present. De-energize the circuit at the breaker, verify dead with a CAT III/IV multimeter, and use proper insulation. If you are a student, test this build using a low-voltage AC source (like a 12V AC doorbell transformer) before ever touching mains voltage. NEC-style guidance requires proper enclosure for any permanent mains connection; your local AHJ has final authority.
  1. Prepare the Current Transformer (CT) Bias: The SCT-013-030 outputs an AC voltage centered at 0V. The ESP32 ADC can only read 0V to 3.3V. Build a voltage divider using two 470kΩ resistors between 3.3V and GND to create a 1.65V bias point. Add a 10µF capacitor in parallel with the bottom resistor to stabilize the DC offset.
  2. Connect the SCT-013: Plug the CT's 3.5mm jack into a breakout adapter. Wire the signal pin to the 1.65V bias network, and then to GPIO 35. Wire the CT GND to the ESP32 GND.
  3. Wire the ZMPT101B: Connect the module's VCC to 5V (if it has an onboard regulator) or 3.3V (check your specific board's silk screen). Connect GND to GND, and the Analog Out to GPIO 34.
  4. Calibrate the Voltage Offset: Before connecting mains, power the ESP32 via USB. Use a multimeter to measure the analog out of the ZMPT101B. Adjust the blue trimpot on the module until the output sits exactly at 1.65V (half of the ESP32's 3.3V logic level).

The AC Theory: Sampling, RMS, and Power Factor

To get accurate readings, we cannot just take one reading per AC cycle. According to the Nyquist-Shannon sampling theorem, we need to sample at least twice the highest frequency component. For a 60Hz fundamental with harmonics, we target 1,500 samples per second (25 samples per cycle).

The True RMS voltage is calculated using the discrete form of the integral:

V_rms = √( (1/N) * Σ(v_i²) )

Where v_i is the instantaneous voltage reading minus the DC bias (1.65V). Real Power (Watts) is the average of the instantaneous voltage multiplied by the instantaneous current. If the load is inductive (like a motor), the current wave lags the voltage wave. The ratio of Real Power to Apparent Power (V_rms × I_rms) gives you the Power Factor. A purely resistive load has a PF of 1.0; a switching power supply might sit at 0.65.

Complete ESP32 Firmware with ADC Error Handling

This code targets the ESP32-WROOM-32E DevKit V1 using the Arduino IDE (ESP32 Core v3.x). It includes hardware-level error handling to detect disconnected sensors or ADC saturation, which are the most common failures in student builds.

#include <Arduino.h>

// --- PIN DEFINITIONS ---
#define VOLTAGE_PIN 34
#define CURRENT_PIN 35

// --- CALIBRATION CONSTANTS ---
// Adjust these based on your specific sensor trim and multimeter verification
#define V_CAL 0.000805  // Volts per ADC step (approx 3.3V / 4095 * transformer ratio)
#define I_CAL 0.007326  // Amps per ADC step (30A CT / 4095 steps)
#define DC_BIAS 1.65    // Theoretical center voltage

// --- SAMPLING PARAMETERS ---
#define SAMPLES_PER_CYCLE 64
#define FREQUENCY 60.0
#define TOTAL_SAMPLES (SAMPLES_PER_CYCLE * 4) // Sample 4 full cycles for stability

void setup() {
  Serial.begin(115200);
  while(!Serial) { delay(10); }
  
  // Configure ESP32 ADC for maximum range (0-3.3V) and 12-bit resolution
  analogReadResolution(12);
  analogSetAttenuation(ADC_11db);
  
  Serial.println("ESP32 True RMS Power Analyzer Initialized.");
  Serial.println("Ensure sensors are biased to 1.65V before applying AC load.");
}

void loop() {
  float sumV_sq = 0, sumI_sq = 0, sumP = 0;
  int maxV_adc = 0, minV_adc = 4095;
  
  unsigned long start_time = micros();
  
  for (int i = 0; i < TOTAL_SAMPLES; i++) {
    int rawV = analogRead(VOLTAGE_PIN);
    int rawI = analogRead(CURRENT_PIN);
    
    // Track ADC bounds for error handling
    if (rawV > maxV_adc) maxV_adc = rawV;
    if (rawV < minV_adc) minV_adc = rawV;
    
    // Convert to instantaneous voltage/current relative to DC bias
    float instV = (rawV * (3.3 / 4095.0)) - DC_BIAS;
    float instI = (rawI * (3.3 / 4095.0)) - DC_BIAS;
    
    sumV_sq += instV * instV;
    sumI_sq += instI * instI;
    sumP += instV * instI;
    
    // Delay to hit target sampling rate (approx 1500Hz)
    // 1,000,000us / 1500Hz = 666us per sample minus execution time
    delayMicroseconds(500); 
  }
  
  // --- ERROR HANDLING & DIAGNOSTICS ---
  if (maxV_adc >= 4090) {
    Serial.println("ERR_ADC_SATURATION: Reading pinned at 4095. Lower ZMPT101B gain trimpot immediately.");
    delay(2000);
    return;
  }
  
  float varianceV = (maxV_adc - minV_adc);
  if (varianceV < 10 && maxV_adc > 2000 && maxV_adc < 2100) {
    Serial.println("ERR_SENSOR_OPEN: Variance below threshold. Check ZMPT101B wiring or AC source.");
    delay(2000);
    return;
  }

  // --- MATH & OUTPUT ---
  float V_rms = sqrt(sumV_sq / TOTAL_SAMPLES) * V_CAL * 1000; // Scaled for transformer ratio
  float I_rms = sqrt(sumI_sq / TOTAL_SAMPLES) * I_CAL * 100;
  float Real_Power = (sumP / TOTAL_SAMPLES) * V_CAL * I_CAL * 100000;
  float Apparent_Power = V_rms * I_rms;
  float Power_Factor = (Apparent_Power > 0.1) ? Real_Power / Apparent_Power : 0.0;

  Serial.printf("V: %6.2f V | I: %5.2f A | W: %6.2f | VA: %6.2f | PF: %4.2f\n", 
                V_rms, I_rms, Real_Power, Apparent_Power, Power_Factor);
  
  delay(500);
}

Debugging: First Three Things to Check When It Fails

When your serial monitor spits out garbage or flatlines, don't rewrite the code. Hardware integration issues cause 95% of failures in these builds. Check these three things first:

  1. ADC Saturation (The Trimpot Trap): If you see the exact error string ERR_ADC_SATURATION: Reading pinned at 4095, your ZMPT101B gain is too high. The op-amp is clipping against the 3.3V rail. Disconnect mains, power via USB, and turn the blue trimpot counter-clockwise until the analog out reads exactly 1.65V DC.
  2. Phase Shift Distortion (Power Factor > 1.0 or Negative): If your Real Power is negative or your PF calculates to 1.2, the current and voltage waveforms are temporally misaligned. Transformers and CTs introduce phase delay. Fix: The SCT-013 and ZMPT101B have different phase shifts. You must either add a software phase-calibration offset in the code (shifting the array index of the current samples) or physically reverse the CT clamp orientation on the wire.
  3. ESP32 ADC Non-Linearity: The ESP32's internal ADC is notoriously noisy near 0V and 3.3V, and its actual reference voltage can be off by ±5%. If your RMS voltage reads 114V when your multimeter says 120V, do not change the math in the code. Change the V_CAL constant. Map the ESP32 readings against a trusted Fluke or Brymen multimeter and derive a custom calibration scalar.

FAQ: Embedded Projects for Electrical Engineering Students

What are the best embedded projects for electrical engineering students involving AC power?

Beyond the True RMS analyzer shown here, the next logical step is building a MPPT (Maximum Power Point Tracking) Solar Charge Controller. While the power analyzer measures AC, an MPPT controller forces you to deal with DC-DC buck converter topology, PWM generation, and PID control loops. Both projects require you to write firmware that directly manipulates physical power states, which is the core competency of power electronics engineering.

How do I simplify projects for electrical engineering students if I lack mains access?

If you are in a dorm or lack safe access to 120V/230V AC, swap the ZMPT101B for a simple 10kΩ / 10kΩ voltage divider and measure low-voltage AC from a 12V AC doorbell transformer or a function generator. The math, the sampling logic, and the RMS calculations remain exactly the same. You can also use a 5V AC signal from a bench transformer to test the firmware safely before scaling up to mains voltage.

Why do university projects for electrical engineering students fail during ADC sampling?

The most common failure mode is aliasing due to blocking code. If your loop() contains a delay(100) or uses a blocking Wi-Fi library like WiFi.begin() without a state machine, your sampling interval becomes erratic. The discrete integration math assumes a fixed time step ($\Delta t$). If the time between samples jitters because the CPU was busy handling a network stack, your RMS and Power Factor calculations will drift wildly. Always use hardware timers (like ESP32TimerInterrupt) or tight, non-blocking loops for the sampling window.