Skip to content

Instantly share code, notes, and snippets.

@JayGoldberg
Last active December 24, 2024 00:58
Show Gist options
  • Save JayGoldberg/c2541578ddbc36e3d9905d424a40aac9 to your computer and use it in GitHub Desktop.
Save JayGoldberg/c2541578ddbc36e3d9905d424a40aac9 to your computer and use it in GitHub Desktop.
Circuit finder using ESP devices (ESP8266 or ESP32)

ESPhome circuit finder

What do it do??

If you've ever tried to locate circuits in your house by flipping the breakers and have someone yell "it's off!", this project is for you.

Traditionally, identifying which circuit a particular outlet or appliance is on often involves tedious trial-and-error and usually requires another person to manually turn off breakers in the breaker box while you check if the target device is still powered.

This project aims to simplify the process of finding specific circuits within a home's electrical system.

This system utilizes inexpensive ESP WiFi microcontrollers (ESP8266 or ESP32 such as the Wemos D1 Mini, costing around $2 each) to monitor the status of different circuits.

Hardware

You can use any existing IoT devices already installed and connected to the same WiFi network, as long as they have a web interface (HTTP).

If you don't have existing IoT devices, you can buy & flash your own by following step #1.

If you don't have an existing WiFi network to use, you can use your phone's mobile hotspot, but all of the circuits you wish to monitor will have to be within range of your mobile hotspot!

Software

ESPHome is used, since it has an web flasher that makes it easy to get the firmware running and to connect the devices to your Wi-Fi.

How to use it

  1. Flash ESPs:

    • Flash each ESP with the generic ESPHome image using the ESP Web Flasher @ https://web.esphome.io/
    • Configure each ESP to your home's Wi-Fi network using the ESP Web Flasher. Record the hostname of each of the ESPs by using the Web Flasher's 3-dot menu (select ).
  2. Connect ESPs to circuits:

    • Plug each ESP into the outlet you want to monitor, or use alligator clips between live/neutral and your USB power bricks. The polarity should not matter for most USB chargers, but check the charger blades for different sizes. In the United States the large blade is neutral, and the narrower blade is live.
  3. Open circuitFinder.html in a browser:

    • Open circuitFinder.html in a web browser connected to the same Wi-Fi network as the ESPHome devices.
    • Enter the hostname or IP of each ESP into the input box, each on a newline.
    • Click the "Scan" button to begin monitoring the status of each ESP.
  4. Toggle Breakers:

    • Toggle the breakers in your electrical panel one by one, waiting for about 5 seconds between each toggle.
    • Observe the status of the devices on the web page. A device connected to a circuit that was just turned off will become unresponsive and turn RED, indicating which circuit it was on.

Benefits & drawbacks

Benefits:

  • No extra power Devices are powered directly on the suspect circuits through USB chargers (110v or 220v AC mains)
  • Efficient circuit identification: Streamlines the circuit finding process, saving time and effort.
  • Cost-effective: Utilizes inexpensive and readily available ESP microcontrollers.
  • Easy to Set Up: Simple to flash and configure ESP devices.
  • User-Friendly: Provides a convenient web interface for monitoring and identifying circuits.

Drawbacks:

  • Not compatible with 240v (US circuits), since there aren't(?) 240v USB bricks
  • Obviously can't be used to test the circuit that your Wi-Fi access points are running from
  • Requires live circuits Can't be used on non-live circuits since the ESPs are powered from the circuit you are trying to find.

Safety note

  • Always exercise caution when working with mains power / electricity. The ESP devices never have mains power on them directly, they are operated off of 5v USB power.
  • Ensure you have the necessary knowledge and experience before attempting any electrical work.
<!DOCTYPE html>
<html>
<head>
<title>ESPHome Circuit finder</title>
<style>
.status-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
grid-gap: 10px;
}
.status-box {
width: 100%;
height: 20px;
border: 1px solid black;
text-align: center;
line-height: 20px;
}
.green {
background-color: green;
}
.red {
background-color: red;
}
</style>
</head>
<body>
<h1>ESPHome Circuit Finder</h1>
<a
href="https://gist.github.com/JayGoldberg/c2541578ddbc36e3d9905d424a40aac9"
>GitHub</a
>
<br />
<textarea
id="mdns-list"
rows="10"
cols="50"
placeholder="Enter MDNS names (one per line)"
></textarea
><br />
<button onclick="startPolling()">Start Polling</button>
<button onclick="stopPolling()">Stop Polling</button>
<div id="status-container"></div>
<script>
const pollingInterval = 3000; // Poll every 3 seconds
let pollIntervalId;
let statusBoxes = {};
let lastPollTimes = {}; // Track the last poll time for each device
let deviceLocations = {};
function loadFromLocalStorage() {
let storedMdnsList = localStorage.getItem("mdnsList");
if (storedMdnsList) {
document.getElementById("mdns-list").value = storedMdnsList;
}
let storedLocations = localStorage.getItem("deviceLocations");
if (storedLocations) {
deviceLocations = JSON.parse(storedLocations);
}
}
function saveToLocalStorage() {
localStorage.setItem(
"mdnsList",
document.getElementById("mdns-list").value
);
localStorage.setItem(
"deviceLocations",
JSON.stringify(deviceLocations)
);
}
document.getElementById("mdns-list").addEventListener("input", () => {
saveToLocalStorage();
});
function startPolling() {
const mdnsNames = document
.getElementById("mdns-list")
.value.trim()
.split("\n");
const statusContainer = document.getElementById("status-container");
statusContainer.innerHTML = "";
loadFromLocalStorage();
mdnsNames.forEach((mdnsName) => {
const statusBox = document.createElement("div");
statusBox.classList.add("status-box");
statusBox.textContent = mdnsName;
const locationInput = document.createElement("input");
locationInput.type = "text";
locationInput.placeholder = "Location";
locationInput.value = deviceLocations[mdnsName] || "";
locationInput.addEventListener("change", () => {
deviceLocations[mdnsName] = locationInput.value;
saveToLocalStorage();
});
statusBox.appendChild(locationInput);
statusContainer.appendChild(statusBox);
statusBoxes[mdnsName] = statusBox;
lastPollTimes[mdnsName] = 0; // Initialize last poll time to 0
});
pollIntervalId = setInterval(pollDevices, pollingInterval);
}
function pollDevices() {
for (const mdnsName in statusBoxes) {
const now = Date.now();
if (now - lastPollTimes[mdnsName] >= pollingInterval) {
lastPollTimes[mdnsName] = now;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 2000); // 2-second timeout
fetch(`http://${mdnsName}`, { signal: controller.signal })
.then((response) => {
if (response.ok) {
statusBoxes[mdnsName].classList.remove("red");
statusBoxes[mdnsName].classList.add("green");
} else {
statusBoxes[mdnsName].classList.remove("green");
statusBoxes[mdnsName].classList.add("red");
// emit_alert_sound();
}
})
.catch((error) => {
// if (error.name === "AbortError") {
statusBoxes[mdnsName].classList.remove("green");
statusBoxes[mdnsName].classList.add("red");
// emit_alert_sound();
// }
});
}
}
}
function stopPolling() {
clearInterval(pollIntervalId);
}
function emit_alert_sound() {
const audioCtx = new (window.AudioContext ||
window.webkitAudioContext)();
const oscillator = audioCtx.createOscillator();
const gainNode = audioCtx.createGain();
oscillator.frequency.value = 800; // Set frequency to 800Hz
oscillator.type = "square"; // Generate a square wave
gainNode.gain.value = 0.25; // Adjust volume as needed
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.start();
setTimeout(() => {
oscillator.stop();
audioCtx.close();
}, 250); // length of alert
}
// Load saved data on page load
loadFromLocalStorage();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment