When building sensor-based systems with ESP32 microcontrollers, electromagnetic interference from Wi-Fi transmission can create ghost readings that appear as legitimate sensor triggers; the solution involves using a latch-and-reset pattern where the system arms a latch when the sensor goes low, kills it on any rising edge, and only counts the signal if the latch survives an uninterrupted low state for a specific duration (such as 20 milliseconds), distinguishing true continuous signals from the oscillating bursts caused by Wi-Fi noise.
ESP32 Ghost Laps: Solving WiFi EMI Interference on a Slot Car Track
Added:I bought this slot car track with my adult money because that's what the most responsible adults do. But the moment you start racing these things, you realize there is no way to tell who actually won. There is no lap counter, there is no timer, nothing. So obviously I had to build one. An ESP32, some sensors, some displays, a live web dashboard, and it's full thing. Now, how hard could it be? Turns out it's pretty hard. Here's what I wanted to build. Two infrared sensors on the track, one per lane. detect when a car passes and ESP32 counts the lap. It tracks times and shows the count on these seven segment displays. And because this is 2026, it also hosts a live dashboard over Wi-Fi so you can watch the race from your phone. There's nothing exotic here and USB 32 to E18 D8NK infrared proximity sensors, two TM1637 displays, and a couple of pull-up resistors. I wired it all up, wrote the code, and it worked.
Laps counting, dashboard updating in real time, raise fining cleanly. Done, right? No, not even close. The first problem, I had added some signal validation. When the sensor triggers, it waits 50 mconds. It checks three more times to confirm if it's real. Sounds smarts, right? Except these cars pass the sensors in about 10 to 30 milliseconds. By the time my validation code checks again, the car is gone. The sensor reads high and there is no car detected and the lab is actually rejected. The fix was actually simple. I ripped out the validation entirely. I trusted the sensor. I just used a 3second or around 300 millisecond debounce. So it doesn't double count.
Done. Maybe for about 5 minutes. Now it was counting every other lap. One skip, three skip, five skip. It was a perfect alternating pattern. The debounce was set to around 1500 milliseconds. But the car completes a full lap in under a second on this tiny track. So the lab 2 arrives while the debounce from lap 1 is still blocking. It dropped it to about 500 milliseconds. It's fixed. Then I turned the Wi-Fi back on. With the Wi-Fi and the web server running, the lab started getting missed. Not every lap, not in a pattern, just randomly. The ESP32's web servers has this function called handle client. Every time someone loads this dashboard, it blocks the entire loop for about 50 to 200 milliseconds. During that time, the code literally cannot check the sensor. The car passes, the sensor goes low and backs to high. And by the next check, it is gone and it is invisible. So I again stripped everything, removed the Wi-Fi, removed the web server, removed the lap time tracking, just the sensors, displays, and serial commands. And it worked perfectly every single lap and every single time. So the web server was the culprit. But the web dashboard was the whole point of this build. And I needed both to work together. This is where things got weird. I switched from polling to hardware interrupts. They fire instantly no matter what the main loop is doing. I put the Wi-Fi back and the lap counter started going up on its own. No cars on the track, nobody touching anything, and there were several ghost laps. Here's what is happening. The ESP32's Wi-Fi radio transmits at around 2.4 GHz in bursts.
That electromagnetic energy couples into nearby GPIO pins. The GPIO 32 and 33. My sensor pins are actually an ADC pins and the Wi-Fi hardware internally shares this ADC one for calibration.
These pins are basically antennas that are used for Wi-Fi noise. Now the thing is I can filter it by pulse width, right? I only count signals longer than 5 millconds and it still ghosts. And I added maximum. I reject anything over 75 millisecond and it still ghosts.
I then bumped the minimum to 25, 50, 75, and 100 milliseconds and nothing worked.
Every fix made logical sense. The ghost signals should have been caught, right, by at least one of these filters. But they just kept slipping through. I was stuck and I stayed stuck until I figured it out what the noise actually looked like. That breakthrough came when I finally understood what noise actually looks like. I was imagining the noise as a single spike, one loop pulse, either too short or too long to be real.
If that were the case, then my filters would have easily caught it. But Wi-Fi EMI on GPIO33 doesn't create a single pulse. It creates oscillating bursts, rapid alternating high, low, high, low, each individual low segments is maybe about 10 to 40 mconds, which is exactly the same duration as the real car passing. My pulse width filter checks each low to high filter in isolation.
The first oscillation in the bust is about 20 mconds long. It passes the minimum and it passes the maximum and it gets counted as a lap. The debounce blocks the rest of the bust, but the damage is already done. Every individual oscillation looks legitimate. The filter was working perfectly. It just couldn't tell the difference between one clean dip and the first dip in the noisy burst.
So, here's the key difference. When a real car blocks the sensor beam, the signal goes low and stays low. And one unbroken dip, no unbouncing. When Wi-Fi noise hits, the signal bounces from low, high, low, high multiple times in quick successions. The fix is what I am calling a latch and reset pattern. When the sensor goes low, I arm a latch. If any rising edge happens, even for a microcond, the latch is killed. It's dead. After 20 milliseconds, I check if it's the latch is still alive. If yes, the pin stayed low continuously for 20 mconds straight. That's a real car.
Noise bursts bounce. The latch gets killed over and over. A real car holds the signal down. The latch survives around 20 milliseconds of uninterrupted low. That's all it takes. The code is surprisingly short. The interrupt handles the latch aiming and killing.
The main loop just checks whether 20 mcond has passed. And that's the whole thing. And here it is. Two players, lap counting, lap times, and it is probably the best lap tracking using seven segment displays and a live dashboard.
All running over Wi-Fi. And you can start and reset all from the browser or your serial console.
You can actually set the laps in either ID or directly on the web UI. The Wi-Fi is running, the web server is running, and there are almost zero ghost laps.
Honestly, the working version took probably a few hours because of Claude uh making it work really with the Wi-Fi took almost days. That's the real lesson here. Making hardware work is one thing.
Making it reliable is completely different game. I'll link the full devlog with all the pin mappings and every dead end in the description so you can build your own if you want. And if you are getting into IoT projects, I've got an ebook with hands-on exercise with a beginner course that walks you through the basics and there's a link in the description. If you have ever had ghost readings on ESP22, drop a comment. I know I'm not the only one who has been haunted by Wi-Fi noise. And if you want to see what I build next, subscribe. See you in the next one.
Up Next

Designing an Arduino Uno Enclosure in Fusion 360
@adskFusion
677.3K views•2015-02-23

Decarbonizing Shipping: New Marine Technologies Explained
@business
138.8K views•2024-11-08

Polymer Environmental Degradation: Mechanisms & Stabilization
@iit
1.8K views•2012-07-10

The Advanced Engineering Behind ASML's EUV Lithography Machines
@veritasium
18.2M views•2025-12-31
Related Study Plans & Knowledge Roadmaps
Structured learning paths in Engineering







































