Edvin Jakobsson

MSc student in Electronics Design Engineering, Linköping University.

Wi-Fi Human Activity Recognition

2025-12-14

Source: CDIO

Summary

The general goal of this project was to create a human activity detection (HAD) system which utilized Wi-Fi channel state information (CSI) for detection. This was a CDIO project course (TNE085) at Linköping University, done with a small team of Electronics Design engineering students.

Wi-Fi channel state information describes how a radio signal is affected as it travels from a transmitter to a receiver — amplitude and phase for every OFDM subcarrier. Since the room itself acts as the channel, objects and people moving through it perturb the CSI in measurable ways. We used this to train machine learning models that could tell whether someone was present in a room, and classify basic activities (standing, sitting, walking) without the person carrying any sensor or being on camera. The final models reached about 95% accuracy for presence detection and 80% accuracy for activity classification on validation data.

Overall system

The system had three main parts:

  • Two ESP32-based nodes acting as Wi-Fi transmitter/receiver, one configured as an access point (AP) and the other as a station (STA). The STA continuously sent Wi-Fi frames to the AP, which extracted CSI from every received frame via a callback in the Wi-Fi driver.
  • A central computer system (written in Python) that received the streamed CSI data, turned it into amplitude/phase heatmaps, and fed short time-windows of these images into a trained CNN for classification.
  • A GUI that visualized the live CSI stream and classification result, updating at at least 1 Hz, with the nodes' RGB LEDs also reflecting the detected activity as a simple physical indicator.

Nodes were mounted around 2 meters off the ground to get a more stable multipath profile and reduce interference from ground-level reflections, with the transmitter and receiver about 7 meters apart in the final test setup.

Nodes

Both nodes ran on the ESP32 microcontroller using Espressif's ESP-IDF SDK, on top of FreeRTOS. Software was written in C, split across a few custom-built components (below) and a handful of FreeRTOS tasks:

  • echo_task — handled commands sent over UART, letting a node's classification output be inspected over a wired connection.
  • battery_task — periodically read battery voltage and reported low-battery state through the RGB LED.
  • tcp_server_task / tcp_client_task — a small TCP server/client pair used to move CSI data reliably between a node and the computer system. TCP was chosen over UDP for its built-in retransmission, at the cost of some throughput.

On startup a node initializes NVS, brings up the ESP-IDF networking stack, configures itself as AP or STA with the right SSID/password, forces 40 MHz bandwidth and 2.4 GHz-only operation, then starts Wi-Fi. Once running, the AP's CSI callback (wifi_csi_rx_cb) copies out CSI data behind a mutex whenever a frame arrives from the expected MAC address, avoiding torn reads if a consumer reads the buffer mid-update.

Components

Three reusable ESP-IDF components were built for the project (each also usable standalone):

rgb_led

A small component for driving a common RGB LED, used here as a low-effort visual indicator of the currently-detected activity. Colors (white, yellow, purple, red, cyan, green, blue, black) are set with a single call, e.g. rgb_set_color(&MY_LED, rgb_purple), after initializing the LED with its three GPIO pins via rgb_init_LED.

EZADC

A thin wrapper around the ESP32 ADC for quickly configuring and reading a channel — originally written to monitor the battery voltage on an Adafruit Feather HUZZAH32 (GPIO 35 / ADC channel 7) via battery_task, exposed as an ADC struct with init_adc, config_adc, ez_read and teardown_adc.

EZWIFI

The core networking component: configures a node as AP or STA, brings up Wi-Fi with the settings described above, and independently turns CSI capture on or off regardless of the node's role. It registers the CSI callback that extracts and stores the amplitude/phase data for each subcarrier from incoming frames, guarded by a mutex so the computer system always reads a consistent snapshot.

Antennas

Antenna choice turned out to matter more than almost anything else in the system. The transmitter used a directional biquad antenna (two square loop antennas forming a total antenna length of two wavelengths, with a reflector spaced λ/8 behind the loops for directivity) — simple to build and with good bandwidth for Wi-Fi frequencies. The receiver used the MIFA (meander inverted-F) antenna integrated into the receiver hardware. Of the antenna types tested during the project, the biquad transmitter gave the most stable, least noisy signal, which translated directly into cleaner CSI and better classification results.

Machine learning

CSI naturally lends itself to being treated as an image: subcarrier index on one axis, time on the other, amplitude as pixel intensity. That let us reuse standard image classification techniques — specifically two separate CNNs (same architecture, trained separately), one for presence detection and one for activity classification, each with three convolution + max-pooling blocks operating on grayscale, fixed-resolution CSI frames.

Data augmentation (random flips, contrast jitter) was used during training to reduce overfitting given a fairly small dataset. Models were trained with the Adam optimizer using a train/validation split and early stopping on validation performance. On held-out test data (304 images for presence, 137 for activity) the trained models reached 94.08% accuracy for presence and 97.08% for activity classification.

Results

The finished prototype met its goals: presence detection around 95% accuracy and activity classification (standing / sitting / walking) around 80% accuracy on validation data, with CSI updating at 10 Hz and the GUI updating at least once per second. Testing at the final ~7 m transmitter–receiver distance showed good, repeatable classification performance under consistent conditions.

The results made it clear that the system's performance was really a product of how well all the subsystems worked together rather than any single part — antenna design and placement in particular were critical, since a more stable, directional signal produced much clearer separation between activity classes in the CSI. Classification also held up better for large, distinct movements than for subtle ones, since small or slow motion produces weaker CSI variation that is easier to confuse with ambient noise. Performance also degraded somewhat with more complex movement patterns or at greater distances, which — along with the small size of the training dataset — would be the natural next things to address in a follow-up iteration (more environments, more subjects, more activity classes, and a more systematic antenna/placement optimization).

Overall, the project was a solid demonstration that Wi-Fi CSI can be used for device-free activity recognition, and a good practical exercise in taking a system from RF hardware, through embedded firmware and networking, to a trained ML model and a live GUI.