Every electrician has walked into a basement or garage to find a stack of holiday decorations, a lawnmower, or a workbench shoved directly against a load center. This isn't just a nuisance; it's a direct violation of the clearance electrical panel code mandated by the National Electrical Code (NEC). Specifically, NEC 110.26 dictates strict working space requirements to ensure safe operation and emergency egress.
As a maker and an electrician, I decided to solve this jobsite and home-compliance problem with hardware. Below is a complete guide to building an ESP32-based IoT ultrasonic monitor that continuously measures the 36-inch depth clearance in front of your panel and triggers an MQTT alert the moment someone stores a box in the restricted zone.
The NEC 110.26 Clearance Electrical Panel Code Explained
Before we wire the sensor, you need to know exactly what the National Fire Protection Association (NFPA) requires for working space. According to NEC 110.26(A), the minimum clear working space in front of an electrical panel must be:
- Depth: 36 inches (91.4 cm) measured from the live exposed parts (or the front of the panel enclosure if all parts are enclosed).
- Width: 30 inches (76.2 cm) or the width of the equipment, whichever is greater.
- Height: 6.5 feet (2.0 m) from the floor to the ceiling, or the height of the equipment if taller.
This space must be kept clear at all times. Our IoT monitor will be mounted at the top of the panel, pointing downward at a slight angle, or mounted on the ceiling directly above the 36-inch boundary line, pinging the floor to detect intrusions into the 3D clearance volume.
Hardware Spec Sheet & Parts List
Selecting the right ultrasonic sensor is critical here. Standard 5V HC-SR04 sensors will eventually fry the 3.3V GPIO pins on an ESP32 due to the 5V echo return signal. We use the HC-SR04P, which is natively 3.3V logic tolerant.
| Component | Exact Variant / Model | Notes |
|---|---|---|
| Microcontroller | ESP32-WROOM-32 (DevKit v1) | 38-pin variant, dual-core, built-in WiFi |
| Sensor | HC-SR04P (3.3V Logic) | Do NOT use standard HC-SR04 (5V) |
| Power Supply | 5V 2A USB Micro-B | Standard phone charger is sufficient |
| Enclosure | 3D Printed PLA/PETG | Angled mount for ceiling/panel-top |
| Protocol | MQTT over WiFi | Requires local broker (e.g., Mosquitto) |
If you only have the standard 5V HC-SR04, you must use a voltage divider (e.g., 1kΩ and 2kΩ resistors) on the ECHO pin before it reaches the ESP32 GPIO. Feeding 5V into an ESP32 GPIO will degrade the silicon and cause phantom triggers or permanent pin failure. Always check your sensor's silkscreen for the 'P' designation.
Wiring the Sensor to the ESP32
We are using GPIO 5 for the trigger and GPIO 18 for the echo. These are safe, general-purpose pins that do not conflict with the ESP32's internal flash SPI bus or strapping pins during boot.
| ESP32 DevKit v1 Pin | HC-SR04P Pin | Wire Color (Recommended) |
|---|---|---|
| 3V3 | VCC | Red |
| GND | GND | Black |
| GPIO 5 | TRIG | Yellow |
| GPIO 18 | ECHO | Blue |
Complete ESP32 Clearance Monitor Code
This code targets the ESP32 DevKit v1 (ESP32-WROOM-32) board variant. It uses the NewPing library instead of the blocking pulseIn() function. Blocking functions cause the ESP32's Task Watchdog Timer (WDT) to panic and reset the board if the sensor fails to return an echo.
#include <WiFi.h>
#include <PubSubClient.h>
#include <NewPing.h>
// --- Pin Definitions ---
#define TRIGGER_PIN 5
#define ECHO_PIN 18
#define MAX_DISTANCE 150 // 150cm (~59 inches). NEC requires 36" (91cm) clear.
// --- Network & MQTT Definitions ---
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* mqtt_server = "192.168.1.100"; // Local Mosquitto broker IP
const int mqtt_port = 1883;
const char* mqtt_topic = "home/electrical/panel1/clearance";
// --- Thresholds ---
const int CLEARANCE_LIMIT_CM = 91; // 36 inches in cm
const unsigned long CHECK_INTERVAL = 5000; // Ping every 5 seconds
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastCheck = 0;
bool isViolated = false;
void setup_wifi() {
delay(10);
Serial.print("Connecting to WiFi: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connected. IP: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("\nWiFi connection failed. Rebooting...");
ESP.restart();
}
}
void reconnect_mqtt() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
String clientId = "ESP32-PanelMonitor-" + String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println("connected");
client.publish(mqtt_topic, "online");
} else {
Serial.print("MQTT connect failed, rc=");
Serial.print(client.state());
Serial.println(" retrying in 5 seconds");
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
}
void loop() {
if (!client.connected()) {
reconnect_mqtt();
}
client.loop();
if (millis() - lastCheck >= CHECK_INTERVAL) {
lastCheck = millis();
// Get median of 5 pings to filter out acoustic noise
delay(29); // Wait 29ms between pings
int distance_cm = sonar.ping_median(5);
if (distance_cm == 0) {
Serial.println("Out of range or clear.");
if (isViolated) {
client.publish(mqtt_topic, "clear");
isViolated = false;
}
} else {
Serial.print("Distance: ");
Serial.print(distance_cm);
Serial.println(" cm");
if (distance_cm < CLEARANCE_LIMIT_CM && !isViolated) {
String payload = "VIOLATION: Object detected at " + String(distance_cm) + "cm";
client.publish(mqtt_topic, payload.c_str());
isViolated = true;
} else if (distance_cm >= CLEARANCE_LIMIT_CM && isViolated) {
client.publish(mqtt_topic, "clear");
isViolated = false;
}
}
}
}
Debugging: First Three Things to Check When It Fails
When deploying sensors in dusty, echoing environments like a garage or basement, ultrasonic pings can behave erratically. If your serial monitor is throwing errors or failing to connect, check these three items first:
- Verify the 3.3V Logic Level: Put your multimeter in DC voltage mode. Probe the ECHO pin on the sensor side while triggering a measurement. If it reads 4.8V to 5.1V, you have a 5V sensor connected to a 3.3V ESP32 pin. You will need a logic level converter or voltage divider immediately to prevent GPIO damage.
- Check the RSSI and Network Isolation: If the ESP32 connects to WiFi but fails to reach the MQTT broker, check your router's VLAN settings. IoT devices are often placed on isolated guest networks that block local LAN mDNS and direct IP routing to your main network where the Mosquitto broker lives.
- Inspect the Sensor Face for Dust/Condensation: Ultrasonic transducers rely on clean diaphragms. In a basement, condensation or drywall dust on the mesh grille will scatter the 40kHz acoustic wave, resulting in a
0(timeout) return even when an object is present. Wipe it with a dry microfiber cloth.
Handling the Exact Error String: MQTT connect failed, rc=-2
If your serial monitor outputs MQTT connect failed, rc=-2, the PubSubClient library is telling you that the network connection to the broker was refused or timed out. Here are the ranked causes:
- Cause 1 (Most Likely): The Mosquitto broker service is not running on the target IP, or the IP address in the code is incorrect. SSH into your broker machine and run
systemctl status mosquitto. - Cause 2: Mosquitto 2.0+ defaults to blocking external connections without explicit listeners. You must add
listener 1883andallow_anonymous true(or configure passwords) in yourmosquitto.conffile. - Cause 3: A local firewall (like UFW on Ubuntu or Windows Defender) is blocking inbound TCP traffic on port 1883.
How to Extend or Simplify the Build
Not everyone runs a local MQTT broker. Here is how you can adapt this project to your specific infrastructure:
If you don't have a home automation server, strip out the
WiFi.h and PubSubClient.h libraries. Add a 5V active buzzer to GPIO 23. In the loop(), simply write digitalWrite(BUZZER_PIN, HIGH) when distance_cm < CLEARANCE_LIMIT_CM. This creates a standalone, code-compliance alarm that requires zero network infrastructure.
Ultrasonic sensors struggle with angled surfaces (like a sloped pile of boxes) because the sound wave reflects away from the receiver. For industrial or high-reliability monitoring, swap the HC-SR04P for a Benewake TF-Luna LiDAR sensor. It uses infrared time-of-flight (ToF) and communicates via I2C or UART. You can also port this entire logic tree into ESPHome YAML for native, zero-code Home Assistant integration.
FAQ: Clearance Electrical Panel Code Questions
What is the exact clearance electrical panel code for residential homes?
For residential homes, the rule is governed by NEC 110.26(A). The minimum clear working space must be at least 36 inches deep, 30 inches wide, and 6.5 feet high. This applies to all panels containing live parts operating at 0 to 150 volts to ground, which covers standard 120V/240V residential split-phase systems.
Does the clearance electrical panel code apply to outdoor subpanels?
Yes. According to NEC 110.26, the working space requirements apply to any electrical equipment likely to require examination, adjustment, servicing, or maintenance while energized. An outdoor subpanel mounted on a detached garage or a pole requires the same 36-inch depth and 30-inch width clearance. You cannot plant shrubs or build a fence within that 30x36 inch footprint.
Can I install shelving above the panel if it meets the height requirement?
No. NEC 110.26(B) specifically addresses clear spaces. The working space required by the code cannot be used for storage. Furthermore, the 6.5-foot height requirement means the space must be completely unobstructed from the floor up to 6.5 feet. Installing a shelf at the 5-foot mark directly above the panel violates the code, even if the shelf doesn't physically touch the panel door, because it intrudes into the mandated vertical working envelope.
How do inspectors measure the 36-inch depth for panel clearance?
Inspectors (the Authority Having Jurisdiction, or AHJ) measure the 36 inches from the live exposed parts. If the panel deadfront (the metal cover with the breaker slots) is removed and the hot bus bars are exposed, the 36 inches starts from those bus bars. If the panel is fully enclosed and you are just measuring for general storage clearance, they measure 36 inches from the front face of the panel enclosure door. For more on inspection standards, refer to the OSHA 1910.303 working space guidelines, which closely mirror and enforce NEC standards in commercial environments.






