Building a smart energy monitor that interfaces directly with your home's breaker box requires mastering two very different types of code: the C++ firmware running on your microcontroller, and the National Electrical Code (NEC) regulations governing physical panel modifications. The direct answer for a safe, compliant build is to use an ESP32-WROOM-32 paired with SCT-013-000 voltage-output CT clamps, while strictly adhering to NEC Article 725 for low-voltage wire separation inside the enclosure.
In this guide, we will walk through the exact hardware spec sheet, the NEC electrical panel code requirements you must follow to pass an inspection (and avoid ADC noise), the complete compilable C++ code, and how to debug the most common ESP32 watchdog panics associated with AC sampling.
Hardware Spec Sheet and Pin Mapping
Before writing a single line of code, you need the right bench components. The SCT-013-000 outputs a 0-1V AC signal at 100A, which means we must build a DC bias circuit to shift the waveform into the ESP32's 0-3.3V ADC range.
| Component | Exact Variant / Value | Est. Cost (2026) | Purpose |
|---|---|---|---|
| Microcontroller | ESP32 DevKit V1 (30-pin, WROOM-32) | $6.50 | Main processing and WiFi/MQTT telemetry |
| Current Sensor | SCT-013-000 (100A, Voltage Output) | $12.00 ea | Non-invasive AC current measurement |
| Voltage Sensor | ZMPT101B AC Voltage Module | $5.50 | Isolated mains voltage step-down for Real Power calc |
| DC Bias Resistors | 100kΩ 1/4W Metal Film (x2) | $0.10 | Creates 1.65V virtual ground for AC waveform |
| Bias Capacitor | 10µF Electrolytic (16V+) | $0.15 | Stabilizes the 1.65V DC bias midpoint |
ESP32 Pin Mapping Table
This mapping targets the standard 30-pin DevKit V1. We exclusively use ADC1 pins because ADC2 conflicts with the ESP32's WiFi radio during transmission.
| ESP32 Pin | GPIO Number | Connected To | Notes |
|---|---|---|---|
| VP (GPIO 36) | 36 | ZMPT101B Analog Out | AC Voltage sensing (ADC1_CH0) |
| VN (GPIO 39) | 39 | SCT-013-000 Signal | AC Current sensing (ADC1_CH3) |
| 3V3 | N/A | 100kΩ Resistor 1 | Top of voltage divider for DC bias |
| GND | N/A | 100kΩ Resistor 2 | Bottom of voltage divider for DC bias |
Navigating NEC Electrical Panel Code for Sensor Installations
When you open a 200A residential panel, you are entering a strictly regulated environment. Inspectors and the National Fire Protection Association (NFPA) enforce NEC 70 (National Electrical Code) to prevent fires and arc flashes. Here is how electrical panel code applies to embedded sensor installs:
1. Working Space (NEC Article 110.26)
You must maintain a 30-inch wide, 36-inch deep clearance in front of the panel. Mounting your ESP32 enclosure on the wall directly adjacent to the panel is fine, but you cannot mount the microcontroller inside the panel if it obstructs the working space or requires you to reach past exposed bus bars to service the USB port.
2. Separation of Class 2 Circuits (NEC Article 725.136)
This is where most DIYers fail. Your ESP32 sensor wires (Class 2, low voltage) cannot share the same raceway or panel cavity as 120V/240V mains conductors (Class 1/Power) without a physical barrier. The Fix: Route your SCT-013 and ZMPT101B wires through a separate conduit or use a listed external junction box mounted to the outside of the panel knockout. Pass only the insulated, listed sensor leads through the knockout, keeping them physically separated from the THHN mains wires to prevent both code violations and severe 60Hz EMI noise on your ADC readings.
3. Wire Fill and Derating (NEC Article 312.8)
Panelboards are limited to 40% wire fill in the wiring gutters. Adding a bundle of 8 sensor wires might seem harmless, but if the gutter is already packed with 12 AWG and 10 AWG branch circuits, you violate the fill capacity. Keep sensor wires routed tightly along the exterior edges of the gutter.
Complete ESP32 C++ Code with Error Handling
The following firmware targets the ESP32 DevKit V1 (ESP32-WROOM-32). It uses the EmonLib library for RMS calculations and PubSubClient for MQTT telemetry. Crucially, it includes explicit pin definitions, WiFi reconnection logic, and Watchdog Timer (WDT) resets to prevent the ESP32 from panicking during long AC sampling loops.
#include <WiFi.h>
#include <PubSubClient.h>
#include <EmonLib.h>
#include <esp_task_wdt.h>
// --- BOARD & PIN DEFINITIONS (ESP32 DevKit V1 / WROOM-32) ---
#define PIN_CURRENT_ADC 39 // GPIO 39 (VN) - ADC1_CH3
#define PIN_VOLTAGE_ADC 36 // GPIO 36 (VP) - ADC1_CH0
// --- CALIBRATION CONSTANTS ---
// CT Ratio: 100A / 1V. Burden is internal. Adjust based on multimeter readings.
#define CT_CALIBRATION 111.1
#define VOLTAGE_CALIBRATION 300.0
// --- NETWORK CREDENTIALS ---
const char* ssid = "YourNetworkSSID";
const char* password = "YourNetworkPassword";
const char* mqtt_server = "192.168.1.50";
const int mqtt_port = 1883;
EnergyMonitor emon1;
WiFiClient espClient;
PubSubClient mqttClient(espClient);
unsigned long lastMqttPublish = 0;
const long publishInterval = 5000; // 5 seconds
void setup_wifi() {
delay(10);
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
attempts++;
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("[ERROR] WiFi Connection Failed. Check SSID/Pass.");
} else {
Serial.print("[OK] Connected. IP: ");
Serial.println(WiFi.localIP());
}
}
void reconnect_mqtt() {
if (!mqttClient.connected()) {
String clientId = "ESP32-PanelMonitor-" + String(random(0xffff), HEX);
if (mqttClient.connect(clientId.c_str())) {
Serial.println("[OK] MQTT Connected");
} else {
Serial.print("[ERROR] MQTT failed, rc=");
Serial.println(mqttClient.state());
}
}
}
void setup() {
Serial.begin(115200);
// Initialize Watchdog Timer to 5 seconds to catch infinite loops
esp_task_wdt_init(5, true);
esp_task_wdt_add(NULL);
setup_wifi();
mqttClient.setServer(mqtt_server, mqtt_port);
// Initialize EmonLib (Current Pin, Voltage Pin, Calibrations)
emon1.current(PIN_CURRENT_ADC, CT_CALIBRATION);
emon1.voltage(PIN_VOLTAGE_ADC, VOLTAGE_CALIBRATION, 1.7);
// ADC Attenuation must be set to 11dB for 3.3V range on ESP32
analogSetPinAttenuation(PIN_CURRENT_ADC, ADC_11db);
analogSetPinAttenuation(PIN_VOLTAGE_ADC, ADC_11db);
}
void loop() {
// Reset Watchdog Timer to prevent panic during long sampling
esp_task_wdt_reset();
if (!mqttClient.connected()) {
reconnect_mqtt();
}
mqttClient.loop();
unsigned long now = millis();
if (now - lastMqttPublish > publishInterval) {
lastMqttPublish = now;
// Calculate Real Power (samples 1480 crossings, ~2000ms timeout)
emon1.calcVI(20, 2000);
float realPower = emon1.realPower;
float apparentPower = emon1.apparentPower;
float powerFactor = emon1.powerFactor;
float rmsCurrent = emon1.Irms;
float rmsVoltage = emon1.Vrms;
// Format and publish via MQTT
char payload[128];
snprintf(payload, sizeof(payload),
"{\"W\":%.1f,\"VA\":%.1f,\"PF\":%.2f,\"A\":%.2f,\"V\":%.1f}",
realPower, apparentPower, powerFactor, rmsCurrent, rmsVoltage);
mqttClient.publish("home/panel/main", payload);
Serial.println(payload);
}
}
Debugging: Exact Error Strings and the First Three Checks
When interfacing embedded code with high-voltage AC environments, failures usually manifest as ESP32 kernel panics or locked ADC values. If your serial monitor throws an error, follow this decision path.
Error: Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Why it happens: The emon1.calcVI(20, 2000) function blocks the CPU while waiting for AC zero-crossings. If the ZMPT101B voltage sensor is unplugged, floating, or reading pure noise, the zero-crossing detector never triggers. The loop hangs, the hardware Watchdog Timer (WDT) isn't reset, and the ESP32 reboots.
The First Three Things to Check:
- Verify ZMPT101B Power: Use a multimeter to check that the ZMPT101B module is receiving exactly 5V (or 3.3V, depending on your module variant) at its VCC pin. A brownout here causes a flatline output.
- Check the DC Bias Midpoint: Disconnect the CT clamp and measure the voltage at GPIO 39 with a DC multimeter. It must read between 1.60V and 1.70V. If it reads 0V or 3.3V, your 100kΩ bias resistors are miswired or the 10µF capacitor is shorted.
- Inspect the Potentiometer: The ZMPT101B has a physical trimpot. If it is turned fully to one extreme, the op-amp saturates, clipping the AC waveform and preventing the software from detecting a zero-crossing. Adjust it while monitoring the serial plotter until the sine wave is centered.
FAQ: Electrical Panel Code and Embedded Monitor Questions
Does NEC electrical panel code allow low-voltage sensor wires inside a breaker box?
Yes, but with strict caveats under NEC Article 725.136. Low-voltage Class 2 sensor wires (like the 22 AWG leads from your SCT-013) can enter the panel enclosure, but they must not share the same wiring gutter or raceway as the 120V/240V mains conductors unless separated by a physical, listed barrier. In practice, the cleanest code-compliant method is to mount an external NEMA 1 or 3R junction box to the side of the panel, route the sensor leads into that box via a dedicated knockout, and keep the ESP32 entirely outside the high-voltage environment.
How do I simplify the build if I only need to monitor one 240V circuit?
If you are only monitoring a single 240V load (like a water heater or EV charger) and do not need whole-home Real Power (Watts) calculations, you can drop the ZMPT101B voltage sensor entirely. Pass the red and black hot wires through the SCT-013-000 CT clamp in opposite directions (or just pass one hot wire). In the C++ code, remove the emon1.voltage() initialization and use emon1.calcIrms(1480) instead of calcVI(). Multiply the resulting RMS current by a hardcoded 240V to get apparent power (VA). This eliminates the zero-crossing timeout errors and removes mains voltage from your embedded workbench entirely.
What is the required working clearance according to electrical panel code?
Under NEC Article 110.26, residential electrical panels require a dedicated working space that is 30 inches wide (or the width of the equipment, whichever is greater), 36 inches deep, and 6.5 feet high. You cannot mount your ESP32 smart home hub, network switch, or sensor enclosure on the wall directly in front of or immediately beside the panel if it infringes on this 30x36 inch footprint. Always mount your embedded enclosures on the opposite side of the stud bay or at least 30 inches laterally away from the panel edge.






