Spokes.wiki Search About
Created
Updated
also read
https://hackaday.com/2026/08/15/scanning-for-lifesigns-with-esp32-and-raspberry-pi-now-open-source/

WiFi Sense (wifisense-pi)

The-Masked-Bear's Apache-2.0 project that detects motion, an unmoving person, and breathing rate using an ordinary 2.4 GHz Wi-Fi link as the instrument. An ESP32-S3 reports the radio channel 100 times a second, a Raspberry Pi 4 does the signal processing, and a browser shows the result. No camera, no PIR, nothing worn.

Arrived here as a Hackaday post (2026-08-15, "Scanning For Lifesigns With ESP32 And Raspberry Pi"); everything below is read from the repository's own README, which is far more specific than the write-up. Repo state at ingest: created 2026-08-14, last push 2026-08-15, 15★ / 5 forks / 58 commits, 0 open issues — days old, so the star count says nothing yet.

Graded T1 at ingest, superseded 2026-08-20 → T3 as first-party project documentation, with the standing caveat this spoke applies to every such source: the numbers below are the author's, measured by the author.

The mechanism

A Wi-Fi signal reaches a receiver by several paths at once — direct, plus reflections off walls, furniture and people. Those copies add with different delays, so across the band some frequencies reinforce and others cancel. That per-sub-carrier pattern is Channel State Information, and most Wi-Fi chips can report it. Move something and the path lengths change, so the pattern changes. A body is mostly water and reflects 2.4 GHz strongly. One wavelength is 12.5 cm, so a chest travelling 6 mm shifts a path by a usable fraction of it. wifi-sensing holds the technique.

The Pi runs the DSP chain: 64 sub-carriers in, drop the DC null and guard band to leave 52, strip outliers, resample to a fixed 20 Hz, then split into a motion band (1.5–9 Hz) and a breathing band (0.13–0.7 Hz), with a harmonic-sum peak pick on the second. Both bands are Doppler frequencies rather than body-movement rates — walking at 1 m/s writes energy near 8 Hz, not at walking pace — and the README is explicit that the separation between the two bands is the only reason breathing can be pulled out at all. Python 3.13, NumPy and SciPy, about 20% of one core. The dashboard has no build step and no JavaScript dependencies.

You have to make the traffic yourself

The finding the README labels "the surprise", and the one most worth carrying out of this source: CSI exists only when a packet is received. A quiet channel yields nothing however good the hardware is.

approach measured CSI rate
listen passively to ambient Wi-Fi ~1 Hz, irregular
ESP32 transmits, capture CSI from the ACKs ~1 Hz (does not work)
Pi sends UDP to the ESP32 1:1, up to 200 Hz

So the node joins your network and the Pi floods it with 100 packets a second purely to have something to measure. The sample rate is chosen rather than discovered, and evenly spaced, which is what the breathing analysis needs. A passive listener is not a slower version of this instrument; it is a different and much worse one.

Second design note in the same spirit: everything reads in dB above the receiver's own noise floor, so an empty room measures 0 dB at any distance and any transmit power. There is no calibration step and no learned baseline that can drift. The floor comes out of the sub-carrier covariance — receiver noise is independent per sub-carrier because it is made inside the radio, while anything happening in the room moves all sub-carriers together.

Measured performance (author's own bench)

CSI sample rate 100 Hz, 1:1 with the stimulus (200 Hz also works)
Wire rate 149 bytes/frame, 119 kbit/s (~16% of the serial link)
Pi CPU ~20% of one core
Empty room +1.1 dB motion, +0.0 dB vitals
Walking +27.8 dB motion
Still person breathing +33.6 dB vitals
Breathing accuracy 14.1 bpm against 14.0 truth; 22.0 against 22.0
Encrypted nRF24 link 108.1 Hz delivered of 113.6 Hz captured (95.1% end to end)
DSP validation 81/81 checks pass

tools/validate_dsp.py drives the whole detector chain with synthetic CSI of exactly known breathing rate — 81 checks, run by CI on every push, and described by the author as "the only place the pipeline is falsifiable." That is a claim this corpus has not seen before (see synthesis).

The section that makes it a good source

Section 2 is titled "What it cannot do, and why", and it exists because "most WiFi-sensing claims online are describing a different instrument."

claim why not, here
see an outline or skeleton impossible with Wi-Fi. 20 MHz of bandwidth gives range resolution c/2B = 7.5 m, bigger than most rooms. The MIT demos (RF-Pose, RF-Capture, WiTrack) use purpose-built FMCW radar with GHz of bandwidth and an antenna array
locate someone in the room one transmitter and one receiver measure one path. You learn that something changed, not where. Needs 3+ separated nodes
count people two people disturb the channel more than one, but not countably
detect someone still and holding their breath nothing to measure. Physics, not a missing feature
identify who no
report sleep stages REM/deep/light come from brain and eye activity, absent from this signal. The project reports stillness and leaves the interpretation to the reader

It also states its own confounders instead of hiding them: fans, curtains and pets register, and "they are not false positives — something really is moving." Hackaday's summary makes the same point from the other side, that a 50 kg dog and a 50 kg human look alike here. Not a medical device, and the README says so.

Build and limits

Minimum bill of materials is an ESP32-S3-WROOM-1 N16R8, a Raspberry Pi 4 (2 GB is enough), a USB data cable, and a 2.4 GHz router you already own. 5 GHz alone will not do — the ESP32-S3 cannot see it. Optional extras: a pair of nRF24L01+ PA/LNA modules for a wireless uplink, or CC1101 at 433 MHz (out of band, so it cannot disturb the measurement; software complete, build blocked on antenna matching), plus BMP280 / DHT22 / MQ135 environment sensors.

Placement is the part the README says matters more than anything else: node and router on opposite sides of the space, 2–8 m apart, subject between them, RSSI −40 to −60 dBm. A stronger signal is worse — very close together the direct path dominates and the reflection is buried in it.

Two hardware traps recorded in the same register as the physics:

  • N16R8 octal PSRAM consumes GPIO 33–37, and internal flash owns 26–32. Wire a peripheral to one and the board fails to boot with no useful error. The README calls this the most common way to brick the module. Compare the pin-scarcity trade in input-multiplexing; here the pins are lost to the package rather than spent by the designer.
  • ADC2 (GPIO11–20) is unusable while Wi-Fi is running — the radio owns that converter — so the MQ135 has to land on ADC1. Since Wi-Fi is the entire point, ADC2 is permanently off the table.

Through-wall behaviour is stated by material: plasterboard, wood and glass pass 2.4 GHz; brick and concrete attenuate heavily; foil-backed insulation blocks it.

Radio uplinks carry AES-128 in counter mode, fresh session ID per boot, replay rejection — because "CSI reveals when a room is occupied, when people move and when they sleep. In the clear, anyone in range with a matching radio on the same channel reads all of it." The README then declines to oversell it: this is confidentiality and not authentication, and the CRC-16 inside the encrypted payload means a random forgery survives with probability 1/65536, "proportionate for a home sensor but not a MAC." Published binaries ship an all-zero placeholder key, since a prebuilt image cannot carry a private one.

The privacy reading points at two other spokes and stays here: an instrument that sees through a wall, needs no camera, and works with hardware already in the room is surveillance-capable by construction (../osint-wiki), and the reason to encrypt a home telemetry link is ../defensive-security-wiki's subject. This page is the device.

wifi-sensing · esp32 · single-board-computer · the-masked-bear · open-hardware · embedded-systems · microcontroller · iot-protocols · synthesis

Re-tiered 2026-08-20 → T3. Under the corpus rule adopted this day (../QUALITY.md → tier by the claim, not the URL), this page rests on a table the page itself labels "the author's own bench" — sample rate, wire rate, CPU load and the dB separations that make the whole result. The artifact facts on this page remain first-party and checkable; the grade follows the claim the page is here for.