Search for "project ideas electrical engineering" and you will get a hundred variations of IoT weather stations and Bluetooth-controlled LED strips. While fine for learning basic GPIO, they completely bypass the core of electrical engineering: AC/DC theory, impedance, phase shift, and power factor. If you want to bridge the gap between embedded firmware and real-world power systems, you need to measure actual AC waveforms.
This guide cuts through the generic lists with a decision matrix to pick your next build, then provides a complete, debug-forward blueprint for a flagship project: a Non-Invasive True RMS AC Power and Power Factor Meter. We will cover the exact hardware, the math behind the sampling, and how to debug the inevitable watchdog crashes.
The Decision Matrix: Picking Your Next EE Build
Not all projects teach the same skills. Use this decision path to select the right build for your current knowledge gap. The path terminates in a single default recommendation for maximum electrical theory exposure.
| Project Idea | Primary Theory Learned | Hardware Complexity | Firmware Complexity | Verdict |
|---|---|---|---|---|
| IoT Weather Station | I2C/SPI protocols, MQTT | Low | Medium | Skip if you already know I2C. |
| DC Motor PID Controller | Control theory, PWM, H-bridges | Medium | High | Great for robotics, misses AC theory. |
| AC True RMS Power Meter | RMS math, phase shift, ADC sampling | High | High | DEFAULT PICK: Best for EE fundamentals. |
Flagship Build: Parts List and Pin Mapping
This build targets the ESP32 DevKit V1 (38-pin variant with the ESP32-WROOM-32E module). We use the ESP32 over the Arduino Uno because its 12-bit ADC and dual-core 240MHz processor are mandatory for capturing 50/60Hz AC waveforms with enough resolution to calculate True RMS accurately.
Exact Bill of Materials
- MCU: ESP32 DevKit V1 (38-pin, ESP32-WROOM-32E) — ~$6.00
- Voltage Sensor: ZMPT101B Active Single-Phase AC Voltage Module — ~$4.00
- Current Sensor: SCT-013-030 Current Transformer (30A max, 1V analog output) — ~$8.00
- Passives: 10kΩ and 100kΩ resistors, 10µF electrolytic capacitors for bias filtering.
- Prototyping: 830-point breadboard, 22 AWG solid copper jumper wires.
Pin Mapping Table
The ESP32 ADC2 pins conflict with the WiFi radio. You must use ADC1 pins for analog sampling if you plan to transmit data over WiFi later.
| Component | Module Pin | ESP32 Pin | Notes |
|---|---|---|---|
| ZMPT101B | OUT | GPIO 34 (ADC1_CH6) | Input only, no internal pullup. |
| ZMPT101B | VCC | 5V (VIN) | Requires 5V for internal op-amp. |
| SCT-013-030 | Audio Jack Tip | GPIO 35 (ADC1_CH7) | Input only, requires 1.65V bias. |
| SCT-013-030 | Audio Jack Sleeve | GND | Shared analog ground. |
| Bias Network | Voltage Divider | 3.3V & GND | Two 10kΩ resistors + 10µF cap to create 1.65V virtual ground for SCT-013. |
Complete ESP32 Firmware: ADC Sampling and RMS Math
True RMS is not simply the peak voltage divided by the square root of 2. That only works for pure sine waves. True RMS requires squaring each instantaneous sample, finding the mean of those squares over one full AC cycle, and taking the square root of that mean. The code below implements this math while explicitly feeding the ESP32's watchdog timer to prevent crashes.
// Target Board: ESP32 DevKit V1 (38-pin, ESP32-WROOM-32E)
// IDE Setting: Board "ESP32 Dev Module", Flash Frequency "80MHz"
#include
#include
// Pin Definitions
const int VOLTAGE_PIN = 34; // ZMPT101B Analog Out
const int CURRENT_PIN = 35; // SCT-013-030 Analog Out
// Calibration Constants (Adjust based on your multimeter readings)
const float V_CALIBRATION = 0.152; // Volts per ADC step
const float I_CALIBRATION = 0.075; // Amps per ADC step
const float V_BIAS_OFFSET = 1.65; // ZMPT101B center bias voltage
const float I_BIAS_OFFSET = 1.65; // SCT-013 virtual ground bias
// Sampling Parameters (60Hz AC = 16.67ms per cycle)
const unsigned long SAMPLE_WINDOW_MS = 20; // Slightly over 1 cycle
const int SAMPLE_RATE_US = 100; // 10kHz sampling rate
void setup() {
Serial.begin(115200);
analogReadResolution(12); // Set ESP32 ADC to 12-bit (0-4095)
analogSetAttenuation(ADC_11db); // Full scale ~3.3V
Serial.println("AC True RMS Power Meter Initialized.");
}
void loop() {
unsigned long start_time = micros();
unsigned long window_us = SAMPLE_WINDOW_MS * 1000;
float sum_V_sq = 0;
float sum_I_sq = 0;
float sum_P_inst = 0; // For Real Power (Watts)
int sample_count = 0;
while ((micros() - start_time) < window_us) {
// Read raw 12-bit ADC values
int raw_V = analogRead(VOLTAGE_PIN);
int raw_I = analogRead(CURRENT_PIN);
// Convert to instantaneous voltage/current relative to bias
float inst_V = (raw_V * (3.3 / 4095.0) - V_BIAS_OFFSET) * V_CALIBRATION;
float inst_I = (raw_I * (3.3 / 4095.0) - I_BIAS_OFFSET) * I_CALIBRATION;
// Sum of squares for RMS calculation
sum_V_sq += (inst_V * inst_V);
sum_I_sq += (inst_I * inst_I);
// Instantaneous power for Real Power calculation
sum_P_inst += (inst_V * inst_I);
sample_count++;
// CRITICAL: Yield to the FreeRTOS scheduler to feed the Watchdog Timer
yield();
delayMicroseconds(SAMPLE_RATE_US);
}
// Calculate True RMS
float V_rms = sqrt(sum_V_sq / sample_count);
float I_rms = sqrt(sum_I_sq / sample_count);
float P_real = sum_P_inst / sample_count; // Real Power in Watts
// Calculate Apparent Power and Power Factor
float P_apparent = V_rms * I_rms;
float power_factor = (P_apparent > 0) ? (P_real / P_apparent) : 0;
// Output Data
Serial.printf("V_RMS: %.2f V | I_RMS: %.3f A | Real: %.2f W | PF: %.3f\n",
V_rms, I_rms, P_real, power_factor);
delay(500); // Update display twice a second
}
Debugging the "Interrupt WDT Timeout" Crash
When you first attempt high-frequency ADC sampling on the ESP32, you will almost certainly hit this exact error string in your serial monitor:
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
This happens because the ESP32 runs a FreeRTOS background task for WiFi and system management. If your while() sampling loop hogs the CPU for more than a few milliseconds without yielding, the hardware watchdog assumes the system has locked up and resets the chip.
Ranked Causes and Fixes
- Missing
yield()in the loop: The most common cause. You must includeyield()orvTaskDelay(1)inside your tight sampling loop (as done in the code above) to feed the watchdog. - Using Hardware Timers incorrectly: If you trigger ADC reads via an interrupt service routine (ISR) that takes too long to execute, it starves the main loop. Keep ISRs under 5µs, or use the ESP-IDF
adc_continuousAPI via DMA instead of manual polling. - WiFi Stack Starvation: If you add WiFi transmission inside the sampling loop, the radio stack will crash. Always sample first, store in a buffer, and transmit in a separate FreeRTOS task on Core 0.
First 3 Things to Check When Readings Fail
If the code compiles but your serial monitor shows 0.00V or wildly fluctuating numbers, run this diagnostic path:
- Verify the 1.65V Bias Voltage: The SCT-013 outputs an AC signal that swings positive and negative. The ESP32 ADC can only read 0V to 3.3V. If you forgot the voltage divider bias network (two 10kΩ resistors and a 10µF cap), the negative half of the AC wave gets clipped at 0V, destroying the RMS math. Measure the SCT-013 sleeve-to-tip voltage with no load; it should sit exactly at 1.65V DC.
- Check for ADC Non-Linearity at the Edges: The ESP32 ADC is notoriously non-linear below 0.1V and above 3.1V. If your ZMPT101B is calibrated too "hot" and pushing 3.2V peaks, your readings will compress. Adjust the ZMPT101B's onboard multi-turn potentiometer until the peak-to-peak swing is roughly 1.0V to 2.5V.
- Calibrate Phase Shift: Transformers (both ZMPT101B and SCT-013) introduce a slight phase delay. If your Real Power (Watts) reads negative or near-zero on a known resistive load (like an incandescent bulb), you have a phase shift error. You must introduce a software time-delay offset in the current sampling array to align the V and I waveforms.
Extending or Simplifying the Build
Depending on your bench time and goals, you can scale this project up or down.
How to Simplify (The DC Alternative)
If AC waveform sampling is overwhelming, pivot to DC power monitoring. Swap the ZMPT101B and SCT-013 for an INA219 I2C High-Side DC Current Sensor (Adafruit product ID 904). The INA219 handles all the shunt voltage amplification and ADC conversion internally. You simply read the I2C registers via the Adafruit_INA219 library. It teaches I2C and DC power theory without the Nyquist sampling headaches.
How to Extend (The Smart Subpanel Monitor)
To turn this into a permanent home energy monitor: 1. Replace the breadboard with a custom PCB using KiCad, routing the AC mains traces with proper clearance (minimum 2mm for 240V, ideally with a milled slot under the optocoupler/transformer). 2. Add a second SCT-013 to measure 240V split-phase loads (like dryers) by clamping it around both hot legs and summing the power. 3. Implement MQTT using the PubSubClient library to push JSON payloads to a local Home Assistant instance, creating long-term power factor degradation alerts for your HVAC motors.
By building a True RMS meter, you move past abstract textbook formulas and start seeing the actual physics of your home's electrical system. You will immediately notice how switching power supplies destroy your power factor, and why utility companies charge industrial clients for reactive power. That is the real value of engineering project ideas that push you into the deep end.






