When you bundle 12 AWG THHN wire in a hot attic or pull multiple circuits through a single conduit, the 20-amp breaker protecting the circuit won't trip if the wire overheats. Why? Because the breaker only sees current, not temperature. According to NEC Article 310, ambient heat and conductor bundling severely reduce a wire's safe ampacity. If your derated ampacity drops below your actual load, the insulation degrades long before the breaker trips.
This project solves that blind spot. We are building an ESP32-based IoT monitor that reads real-time current and ambient temperature, applies NEC 310.15(B) derating math on the fly, and triggers an alert if your electrical code wire is pushed beyond its legally derated limits. For this bench-safe build, we use a 12V DC test loop with 12 AWG THHN, but the math and logic apply directly to 120V/240V AC branch circuits.
Project Overview & Difficulty Rating
- Target Board: ESP32-WROOM-32 DevKit V1 (30-pin variant)
- Difficulty: Intermediate (Requires basic I2C/OneWire wiring and C++ logic)
- Time to Build: 90 minutes (hardware) + 30 minutes (code/calibration)
- Core Concept: Calculating real-time derated ampacity based on ambient temperature correction factors and conductor bundling adjustment factors.
Parts List & Spec Sheet
Using exact variants ensures the pinouts and I2C addresses in the code block below work without modification.
| Component | Exact Variant / Model | Key Specification |
|---|---|---|
| Microcontroller | ESP32-WROOM-32 DevKit V1 (30-pin) | Dual-core 240MHz, built-in WiFi/BLE, 3.3V logic |
| Current/Power Sensor | INA226 Breakout Board (Generic or Adafruit 5296) | I2C Address 0x40, 0.1Ω shunt, 1.25mV bus LSB |
| Temperature Sensor | DS18B20 Waterproof (Stainless Steel Probe) | OneWire protocol, -55°C to +125°C range |
| Pull-up Resistor | 4.7kΩ 1/4W Carbon Film | Required for OneWire data line stability |
| Test Wire | 12 AWG THHN Copper (Stranded) | 90°C dry rating, 30A base ampacity (NEC Table 310.16) |
| Power Supply | 12V 5A DC Switching Power Supply | Provides safe bench-level current for heating tests |
Wiring & Pin Mapping
The ESP32-WROOM-32 DevKit V1 uses specific pins for hardware I2C and our chosen OneWire data line. Do not use GPIO 34-39 for outputs; they are input-only.
| Sensor / Module | Sensor Pin | ESP32 DevKit V1 Pin | Notes |
|---|---|---|---|
| INA226 | VCC | 3V3 | Do NOT connect to 5V (VIN) |
| INA226 | GND | GND | Common ground required |
| INA226 | SDA | GPIO 21 | Default ESP32 I2C SDA |
| INA226 | SCL | GPIO 22 | Default ESP32 I2C SCL |
| DS18B20 | VDD (Red) | 3V3 | External power mode (recommended) |
| DS18B20 | GND (Black) | GND | Common ground |
| DS18B20 | Data (Yellow) | GPIO 4 | Connect 4.7kΩ resistor between Data and 3V3 |
The Code: Real-Time NEC Derating Calculator
This complete, compilable C++ sketch reads the INA226 via raw I2C (avoiding external library dependencies for the current sensor) and the DS18B20 via the standard DallasTemperature library. It calculates the derated ampacity of 12 AWG THHN based on NEC Table 310.16 temperature correction factors.
Prerequisites: Install the OneWire and DallasTemperature libraries via the Arduino IDE Library Manager. Select "ESP32 Dev Module" as your board.
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// --- PIN DEFINITIONS ---
#define ONE_WIRE_BUS 4
#define I2C_SDA 21
#define I2C_SCL 22
// --- CONSTANTS & CALIBRATION ---
#define INA226_ADDRESS 0x40
#define SHUNT_RESISTOR_OHMS 0.1
#define BASE_AMPACITY_12AWG_90C 30.0 // NEC Table 310.16, 90C column for 12 AWG Copper
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// --- FUNCTION PROTOTYPES ---
float readINA226Current();
float readDS18B20Temp();
float calculateTempDeratingFactor(float tempC);
float calculateDeratedAmpacity(float ambientTempC, int numConductors);
void setup() {
Serial.begin(115200);
while(!Serial) { delay(10); }
Serial.println("\n--- ESP32 Electrical Code Wire Monitor ---");
// Initialize I2C
Wire.begin(I2C_SDA, I2C_SCL, 400000);
// Verify INA226 presence
Wire.beginTransmission(INA226_ADDRESS);
if (Wire.endTransmission() != 0) {
Serial.println("FATAL: INA226 not found at I2C address 0x40. Check wiring.");
while(1) { delay(1000); } // Halt
}
// Initialize DS18B20
sensors.begin();
if (sensors.getDeviceCount() == 0) {
Serial.println("Error: No DS18B20 sensor found on GPIO 4. Check parasitic power wiring.");
// Non-fatal, will retry in loop, but flag it
}
Serial.println("Sensors initialized. Starting monitor...");
}
void loop() {
float currentA = readINA226Current();
float tempC = readDS18B20Temp();
// Assume 4 current-carrying conductors in the conduit for this test scenario
int bundledConductors = 4;
float deratedAmpacity = calculateDeratedAmpacity(tempC, bundledConductors);
Serial.printf("Load: %.2f A | Temp: %.1f C | Derated Limit: %.1f A\n",
currentA, tempC, deratedAmpacity);
if (currentA > deratedAmpacity) {
Serial.println("*** ALERT: CURRENT EXCEEDS NEC DERATED AMPACITY! WIRE OVERHEATING RISK ***");
}
delay(2000);
}
float readINA226Current() {
Wire.beginTransmission(INA226_ADDRESS);
Wire.write(0x01); // Shunt Voltage Register
Wire.endTransmission();
Wire.requestFrom(INA226_ADDRESS, 2);
if (Wire.available() == 2) {
uint16_t raw = (Wire.read() << 8) | Wire.read();
// INA226 Shunt LSB is 2.5uV
float shuntVoltageV = (int16_t)raw * 0.0000025;
return shuntVoltageV / SHUNT_RESISTOR_OHMS;
}
return 0.0;
}
float readDS18B20Temp() {
sensors.requestTemperatures();
float temp = sensors.getTempCByIndex(0);
if (temp == DEVICE_DISCONNECTED_C) return 25.0; // Fallback to 25C on error
return temp;
}
// NEC Table 310.16 Temperature Correction Factors (90C Insulation)
float calculateTempDeratingFactor(float tempC) {
if (tempC <= 30.0) return 1.00;
if (tempC <= 40.0) return 0.91;
if (tempC <= 50.0) return 0.82;
if (tempC <= 60.0) return 0.71;
if (tempC <= 70.0) return 0.58;
return 0.41; // > 70C
}
// NEC Table 310.15(C)(1) Adjustment Factors for Bundling
float calculateDeratedAmpacity(float ambientTempC, int numConductors) {
float tempFactor = calculateTempDeratingFactor(ambientTempC);
float bundleFactor = 1.0;
if (numConductors >= 4 && numConductors <= 6) bundleFactor = 0.80;
else if (numConductors >= 7 && numConductors <= 9) bundleFactor = 0.70;
else if (numConductors >= 10 && numConductors <= 20) bundleFactor = 0.50;
return BASE_AMPACITY_12AWG_90C * tempFactor * bundleFactor;
}
Debugging: When the Build Fails
Embedded sensor integration often fails on the first boot. If your serial monitor hangs or throws errors, follow this decision path.
The Exact Error String
If your OneWire bus is misconfigured, the DallasTemperature library won't halt the ESP32, but our custom error handler will print this exact string to the Serial Monitor:
Error: No DS18B20 sensor found on GPIO 4. Check parasitic power wiring.
Ranked Causes & Fixes
- Missing 4.7kΩ Pull-up Resistor (Most Likely): OneWire requires a pull-up resistor between the Data line (GPIO 4) and VCC (3V3). Without it, the signal floats and the ESP32 reads garbage. Fix: Solder a 4.7kΩ resistor between the yellow and red wires of the probe.
- Parasitic Power Mode Failure: If you tied the DS18B20 VDD pin to GND to use "parasitic power" mode, the ESP32's 3V3 pin often cannot source the required 1.5mA pulse during temperature conversion. Fix: Wire the red VDD wire directly to the ESP32 3V3 pin for external power mode.
- Bad Crimp on Breadboard Jumper: Waterproof DS18B20 probes use stranded wire that often pushes breadboard contacts apart. Fix: Solder the probe wires to solid-core jumper wires or use a screw terminal breakout.
The First Three Things to Check When It Fails
Before rewriting code or swapping parts, verify these three physical layer basics:
- I2C Address Scan: Run a basic I2C scanner sketch. If the INA226 doesn't show up at
0x40, your SDA/SCL wires are swapped or the breakout board's 3.3V regulator is dead. - Pull-up Resistor Presence: Measure resistance between GPIO 4 and 3V3 with the power off. It must read ~4.7kΩ.
- Serial Baud Rate Mismatch: Ensure your Serial Monitor is set to 115200 baud. The ESP32 boot logs will print as gibberish if your monitor is set to 9600.
Extending and Simplifying the Build
Not every project needs full bidirectional monitoring. Here is how to scale this build to your specific needs.
How to Simplify the Build
If you only care about ambient temperature derating (e.g., monitoring a hot attic where wires are routed, but you already know the static load from a clamp meter), drop the INA226 entirely. Remove the I2C initialization and current-reading functions from the code. Hardcode the currentA variable to your known static load (e.g., float currentA = 16.5;) and let the ESP32 solely act as a temperature-triggered alarm for NEC derating violations.
How to Extend the Build
To integrate this into a smart home dashboard, add the PubSubClient library to publish the deratedAmpacity and currentA variables to an MQTT broker (like Mosquitto). Home Assistant can then ingest these MQTT topics via the ESP-MQTT framework, allowing you to trigger automated HVAC cooling or smart breaker trips via a Shelly EM if the wire approaches its thermal limit.
FAQ: Electrical Code Wire Questions
What size electrical code wire do I need for a 20-amp breaker?
For a standard 20-amp residential branch circuit, the minimum electrical code wire size is 12 AWG copper. While 12 AWG THHN has a base ampacity of 30A in the 90°C column of NEC Table 310.16, NEC 240.4(D) specifically limits small conductors: 12 AWG copper is strictly capped at 20A for overcurrent protection, regardless of the insulation's higher thermal rating. If your run exceeds 100 feet, you must calculate voltage drop and may need to upsize to 10 AWG to maintain a 3% drop limit, even though 10 AWG is still protected by the 20A breaker.
How does ambient temperature affect electrical code wire ampacity?
Wire ampacity is rated at a baseline ambient temperature of 30°C (86°F). As ambient heat rises, the wire cannot dissipate its own resistive heating (I²R losses) effectively, risking insulation meltdown. According to NEC Table 310.16, if you run 12 AWG THHN (90°C rated) through an attic that reaches 50°C (122°F), you must apply a correction factor of 0.82. This drops the wire's base ampacity from 30A down to 24.6A. If bundling factors also apply, the safe limit drops even further.
Does the electrical code wire color matter for DC vs AC branch circuits?
Yes, color coding is strictly governed by the NEC for AC, and by industry convention (often adopted by local AHJs) for DC. For AC branch circuits, NEC 200.6 mandates that the grounded neutral must be white or gray, and NEC 250.119 requires the equipment grounding conductor to be bare, green, or green with yellow stripes. The ungrounded "hot" conductors can be any color except white, gray, or green (black, red, and blue are standard for 120/208V 3-phase). For DC systems (like solar or battery banks), the NEC is less prescriptive on colors, but IEEE and industry standards dictate Red for positive (ungrounded) and Black or White for negative (grounded), to prevent catastrophic cross-wiring when AC and DC panels are mounted in the same enclosure.






