The NEC code for switch height mandates that the center of the switch grip must not exceed 48 inches above the finished floor, nor be lower than 15 inches, per NEC Article 404.8(A) and ADA accessibility guidelines. If you are retrofitting a custom ESP32 smart switch into an existing wall box, or framing a new smart-home build, your physical enclosure must sit strictly within this 15-to-48-inch envelope.
This guide covers the exact code requirements for switch placement, followed by a complete ESP32-WROOM-32 smart relay build. We will cover the physical wiring, provide compilable firmware with WiFi error handling, and debug the most common power-supply crashes you will hit when driving a 5V mechanical relay from a microcontroller.
NEC Code for Switch Height & ADA Reach Requirements
When roughing in electrical boxes for lighting control, the National Electrical Code (NEC) and the Americans with Disabilities Act (ADA) align on specific reach ranges to ensure switches are accessible to all users, including those in wheelchairs. According to NFPA 70 (NEC) Article 404.8(A), snap switches must be installed so the center of the grip is not more than 48 inches above the floor.
| Measurement Point | NEC / ADA Requirement | Practical Jobsite Target |
|---|---|---|
| Maximum Height (Center of Grip) | 48 inches (1219 mm) | 44 to 46 inches (allows for flooring thickness) |
| Minimum Height (Center of Grip) | 15 inches (381 mm) | 18 to 20 inches (keeps it above baseboards) |
| Over Countertops (Unobstructed) | 48 inches max from floor | 44 inches (standard 36" counter + 8" backsplash) |
| Over Countertops (Obstructed by cabinet) | 44 inches max from floor | 40 to 42 inches |
Note: Local AHJs (Authority Having Jurisdiction) may adopt stricter state-specific accessibility codes. Always verify with your local inspector before closing up drywall.
Parts List & Smart Switch Specs
To build a smart switch that fits inside a standard US single-gang wall box while respecting the NEC height limits, we need compact components. A standard ESP32 DevKit and a 5V relay module will fit, but it requires careful wire management.
- Microcontroller: ESP32-WROOM-32 DevKit v1 (30-pin variant, 28.5mm wide)
- Switching Module: HiLetgo 5V 1-Channel Relay Module with optocoupler isolation (crucial for protecting the ESP32 from mains inductive kickback)
- Power Supply: 5V 2A USB-C or Micro-USB wall adapter (do not use a 500mA PC USB port)
- Enclosure: Standard 1-gang deep electrical box (minimum 22 cubic inches to accommodate the USB cable and 14 AWG pigtails)
- Wiring: 14 AWG THHN copper pigtails (Black for Line, Red for Load, Bare/Green for Ground)
Pin Mapping & Mains Wiring Procedure
The ESP32 operates at 3.3V logic, but the HiLetgo relay module requires a 5V trigger and 5V VCC. We will power the relay VCC from the ESP32's VIN pin (which outputs 5V when powered via USB), and use a standard GPIO for the trigger.
| ESP32 DevKit v1 Pin | Relay Module Pin | Function / Notes |
|---|---|---|
| VIN (5V) | VCC | Powers the relay coil and optocoupler LED |
| GND | GND | Common ground reference |
| GPIO 5 | IN (Signal) | 3.3V logic trigger (Active LOW on this module) |
Mains Wiring Steps:
- Connect the 14 AWG Black (Line) pigtail to the COM (Common) terminal on the relay's screw block.
- Connect the 14 AWG Red (Load) pigtail to the NO (Normally Open) terminal.
- Wire nut the bare copper ground pigtails together and to the metal gang box (if metal). The ESP32 and relay module do not connect to mains ground.
- Route the USB power cable out through the bottom knockout of the gang box so it can reach an outlet or a hidden 5V USB receptacle installed below the switch.
Complete ESP32 Smart Switch Firmware
This code targets the ESP32 DevKit v1 board in the Arduino IDE. It includes WiFi reconnection logic and ensures the relay defaults to a safe state (OFF) if the network drops.
#include <WiFi.h>
// --- PIN DEFINITIONS ---
#define RELAY_PIN 5 // GPIO 5 connected to Relay IN
#define STATUS_LED 2 // Built-in LED on most DevKit v1 boards
// --- NETWORK CREDENTIALS ---
const char* ssid = "YourNetworkSSID";
const char* password = "YourNetworkPassword";
// --- STATE VARIABLES ---
bool relayState = false;
unsigned long lastReconnectAttempt = 0;
const unsigned long reconnectInterval = 10000; // 10 seconds
void setup() {
Serial.begin(115200);
// Initialize Pins
pinMode(RELAY_PIN, OUTPUT);
pinMode(STATUS_LED, OUTPUT);
// Default to safe state (Relay OFF)
// Note: Many 5V relay modules are Active LOW
digitalWrite(RELAY_PIN, HIGH);
digitalWrite(STATUS_LED, LOW);
Serial.println("Booting ESP32 Smart Switch...");
connectToWiFi();
}
void loop() {
// Maintain WiFi Connection
if (WiFi.status() != WL_CONNECTED) {
digitalWrite(STATUS_LED, LOW); // LED off when disconnected
unsigned long currentMillis = millis();
if (currentMillis - lastReconnectAttempt >= reconnectInterval) {
lastReconnectAttempt = currentMillis;
Serial.println("WiFi lost. Attempting reconnect...");
connectToWiFi();
}
} else {
digitalWrite(STATUS_LED, HIGH); // LED on when connected
}
// Simple Serial Command Interface for Testing
if (Serial.available() > 0) {
char cmd = Serial.read();
if (cmd == '1') toggleRelay(true);
if (cmd == '0') toggleRelay(false);
}
delay(50); // Prevent watchdog timeouts
}
void connectToWiFi() {
WiFi.mode(WIFI_STA);
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("\nConnected! IP: " + WiFi.localIP().toString());
} else {
Serial.println("\nConnection failed. Will retry in loop.");
}
}
void toggleRelay(bool state) {
relayState = state;
// Active LOW logic for standard optocoupler relay modules
digitalWrite(RELAY_PIN, relayState ? LOW : HIGH);
Serial.print("Relay state changed to: ");
Serial.println(relayState ? "ON" : "OFF");
}
Debugging: "Brownout detector was triggered"
When you first plug your ESP32 into the wall to test the relay, you might see the board boot, click the relay once, and immediately reboot while throwing this exact error string in the Serial Monitor:
Brownout detector was triggered
This is the most common failure mode when pairing ESP32s with mechanical relays. The ESP32's internal brownout detector trips when the supply voltage drops below ~2.4V, which happens when the relay coil draws a sudden 70mA inrush current combined with the ESP32's WiFi RF transmission spike.
The First Three Things to Check:
- Power Supply Amperage: Are you using a 500mA USB brick from an old phone? The ESP32 + 5V Relay requires a minimum 5V 2A power supply. Swap to a modern USB-C PD or 2A+ adapter.
- USB Cable Gauge: Thin, cheap USB cables suffer from severe voltage drop over 6 feet. If you measure the voltage at the ESP32's VIN pin with a multimeter while the relay clicks, and it drops below 4.5V, replace the cable with a thick, 20 AWG data/power cable.
- Relay Flyback Protection: The HiLetgo module includes a flyback diode across the coil. If you are using a raw relay or a clone module missing this diode, the inductive kickback will collapse the voltage rail. Ensure your module has the diode soldered across the blue relay cube.
Extending or Simplifying the Build
How to Simplify: If writing C++ and managing WiFi credentials feels like overkill, abandon the raw ESP32 and buy a pre-assembled Sonoff Basic R4 (approx. $9). It is a sealed, UL-listed smart relay that flashes easily with Tasmota or ESPHome, handles the mains-to-low-voltage isolation safely, and requires zero custom wiring inside the gang box.
How to Extend: To make this a true smart-home node, integrate the PubSubClient library to connect the ESP32 to an MQTT broker (like Mosquitto on a Raspberry Pi). This allows Home Assistant to publish state changes to the `home/livingroom/light/set` topic. You can also wire a physical Decora toggle switch to GPIO 4 with a 10k pull-up resistor to allow manual wall control if the WiFi network goes down.
Frequently Asked Questions
What is the NEC code for switch height over a countertop?
For switches mounted above a countertop, the NEC and ADA guidelines dictate that the center of the grip must not exceed 48 inches from the finished floor if the reach is unobstructed. If a cabinet or deep counter (more than 24 inches deep) obstructs the reach, the maximum height drops to 44 inches from the floor to ensure a wheelchair user can reach over the counter.
Does the NEC code for switch height apply to smart switches and dimmers?
Yes. NEC Article 404.8(A) applies to all snap switches, including smart switches (like Lutron Caseta or ESP32-based relays) and dimmers, provided they are used for the manual control of lighting. Even if the switch is primarily controlled via an app, the physical wall interface must remain within the 15-to-48-inch accessibility envelope.
What is the minimum NEC code for switch height in a garage or basement?
The minimum height for a switch grip is 15 inches above the floor. In garages or unfinished basements, electricians often mount switches higher (around 48 inches) to avoid them being blocked by parked cars, workbenches, or stored boxes, but they must never exceed the 48-inch maximum ADA reach limit.
Are there exceptions to the NEC code for switch height for accessibility?
Yes, there are specific exceptions. Switches located above countertops in kitchens or bathrooms are exempt from the strict 48-inch rule if they are part of an appliance or fixture. Additionally, switches intended solely for use by maintenance personnel (like disconnect switches for HVAC units on a roof or high up on a wall) are not bound by the ADA reach ranges, as they are not considered general-use lighting controls.






