ESP32 WiFi CSI Presence Detection: Build a Camera-Free Motion Sensor for Under $10
If you've ever mounted a PIR sensor in a room and had it completely ignore someone sitting still at a desk, you already know the biggest weakness of "cheap" presence detection. PIR sensors only see movement. The moment you stop moving, as far as the sensor is concerned, you've left the room.
I ran into this exact problem while automating my home office lighting. The lights would shut off mid-video-call because I hadn't twitched in ninety seconds. I didn't want a $60 mmWave radar module, and I definitely didn't want a camera pointed at my desk all day. So I went looking for a third option — and found one already sitting inside the ESP32 I had lying around: WiFi Channel State Information, or CSI.
This guide walks through what CSI actually is, why it can detect a stationary human body using nothing but ordinary WiFi packets, and how to get a working presence sensor running on an ESP32 or ESP32-S3 this weekend.
What Is WiFi CSI, and Why Does It See What PIR Sensors Miss
Every WiFi packet that travels between your router and a device doesn't arrive in perfect condition. It bounces off walls, furniture, and — critically — people. Channel State Information is the raw data describing exactly how that signal was distorted on its way from transmitter to receiver: amplitude changes, phase shifts, and multipath reflections across dozens of subcarriers.
A PIR sensor gives you one bit of information: did infrared change, yes or no. CSI gives you a rich, high-dimensional fingerprint of the entire room's radio environment, updated with every packet — often dozens of times per second. When a person sits down, breathes, or simply exists in the signal path, they perturb that fingerprint in a measurable way, even without moving a muscle.
Espressif built native CSI extraction into the ESP32 WiFi stack, which means you don't need any special antenna array or expensive radar chip. Any standard ESP32, ESP32-S3, or ESP32-C6 can pull this data straight out of packets it's already receiving from your router.
What You'll Need
- One ESP32 or ESP32-S3 dev board (the S3 has more RAM, which helps if you plan to add on-device inference later)
- A USB cable and your existing WiFi router — no new hardware on the network side
- ESP-IDF installed, or Arduino IDE with the ESP32 board package if you prefer a lighter setup
- Optional: a second ESP32 acting as a dedicated packet-sending "beacon" for more consistent CSI sampling
You are not buying a sensor module here. The sensor is the WiFi radio you already own.
Step 1: Enable CSI Capture on the ESP32
Espressif maintains an open-source component specifically for this, which saves you from parsing raw CSI byte arrays by hand. Clone it and set up the wifi_csi_recv example as your starting point rather than writing the capture layer from scratch — it already handles registering the CSI callback and gives you clean amplitude and phase arrays per packet.
The core of your firmware boils down to three things:
- Connect the ESP32 to your existing WiFi network in station mode.
- Register a CSI callback using
esp_wifi_set_csi_config()andesp_wifi_set_csi_rx_cb(). - Keep a rolling buffer of recent CSI amplitude readings so you have something to compare each new packet against.
wifi_csi_config_t csi_config = {
.lltf_en = true,
.htltf_en = true,
.stbc_htltf2_en = true,
.ltf_merge_en = true,
.channel_filter_en = true,
.manu_scale = false,
};
esp_wifi_set_csi_config(&csi_config);
esp_wifi_set_csi_rx_cb(&wifi_csi_rx_cb, NULL);
esp_wifi_set_csi(true);Every time a packet arrives, wifi_csi_rx_cb fires with a buffer of raw CSI values. That's your live radio fingerprint of the room.
Step 2: Turn Raw CSI Into a "Presence" Signal
Raw CSI is noisy. You don't need machine learning to get started — a simple variance-based approach gets you surprisingly far for a first version:
- Record a 30-second baseline with the room empty and calculate the standard deviation of CSI amplitude across subcarriers.
- Continuously compare new readings against that baseline.
- When short-term variance exceeds your empty-room baseline by a threshold you tune experimentally, flag the room as occupied.
This is essentially the same idea used in published ESP32 WiFi-sensing research, where variance in received signal strength over time reliably separated an empty room from an occupied one, especially on the 5 GHz band where sensitivity to environmental change is higher than on 2.4 GHz.
Once your threshold logic is stable, this is also where a lightweight TinyML classifier can replace the simple variance check if you want to distinguish between "someone walked through" versus "someone is sitting still" — a natural next step if you've already worked through edge AI on ESP32 before.
Step 3: Expose It as a Sensor in Home Assistant
A presence sensor is only useful if it talks to the rest of your smart home. The cleanest path is MQTT:
mqtt_publish("home/office/presence", occupied ? "ON" : "OFF");From there, add a standard MQTT binary sensor to your Home Assistant configuration and treat it exactly like any other occupancy sensor in your automations — no custom integration required. If you've already set up MQTT for another ESP32 project, this part takes about five minutes.
Honest Limitations Before You Commit
WiFi CSI sensing is genuinely promising, but it isn't a drop-in replacement for radar yet, and being upfront about that will save you a frustrating weekend:
- It's sensitive to environment changes. Moving furniture or even a spinning ceiling fan can throw off your baseline and require recalibration.
- Router firmware matters. Not every router sends packets at a consistent enough rate for smooth CSI sampling; a dedicated "beacon" ESP32 sending ping packets on a schedule gives far more reliable results than relying on organic router traffic.
- It's still an emerging field. Current open-source projects in this space explicitly describe themselves as research-grade rather than finished consumer products, and multi-person detection accuracy still has real limits.
Treat your first build as a proof of concept, not a security system. Tune it in your actual room, over a few days, before trusting it to control anything critical.
Why This Matters Beyond One DIY Project
Camera-based presence detection raises real privacy concerns, and dedicated radar sensors add real cost. WiFi CSI sensing sits in an interesting middle ground: it uses hardware you likely already own, respects privacy by never capturing anything resembling an image, and is cheap enough to put in every room of a house. It won't replace PIR or radar overnight, but as the open-source tooling around it matures, it's one of the more genuinely useful directions edge AI on microcontrollers is heading in 2026.
If you build one, start small — one ESP32, one room, one threshold to tune. Once you trust the results, expanding to a full-house presence mesh is just a matter of repeating the same setup.
Got your own ESP32 WiFi CSI results, or ran into router compatibility issues? Drop your setup in the comments — I'm compiling a list of routers the community has tested this on for a future update.
