A Radar for Under 400 Rubles: Based on ESP32 and Wi-Fi
A hands-on guide to building a motion-detection radar using ESP32 microcontrollers and Wi-Fi RSSI signals, capable of detecting even hand movements in a room, with email notifications and full firmware code included.
Introduction
Wi-Fi technology contains capabilities that most people never use. In this article, we'll explore how to turn a cheap ESP32 microcontroller into a motion detection radar using nothing but built-in wireless signals — no external sensors required.
Key Technology: CSI and RSSI
Channel State Information (CSI)
Channel State Information is radio channel metadata that allows you to assess changes in the surrounding environment. CSI parameters include:
- Signal amplitude
- Phase shift
- Subcarrier data
While CSI offers the most detailed data, its implementation proved problematic in practice, so I pivoted to a simpler approach.
Received Signal Strength Indicator (RSSI)
Since CSI implementation failed, the solution uses RSSI — measurements of received signal strength in decibel-milliwatts (dBm). Despite being a simpler metric, the results were surprisingly impressive.
System Capabilities
During my tests, in any point of the room (approximately 3.5 x 5.5 meters), it was enough not just to take a step, but even to change hand position for the system to detect it. The sensitivity exceeded all expectations.
The system also supports email notifications via Mail.ru when movement is detected.
Hardware Considerations
Antenna Orientation
- ESP32 antenna polarization is horizontal
- The device must be oriented vertically for optimal detection
- Horizontal placement on a table significantly reduces responsiveness
Detection Directionality
The antenna exhibits a figure-eight directional response, with optimal sensitivity within approximately +/-45 degrees relative to the board's vertical plane. This means the sensor has clear "blind spots" and "hot zones" that you need to account for during placement.
Signal Reflection
Movement detection works through multipath propagation: the receiver captures not only the signal that directly "shines" on it, but also all other signals reflected from objects and walls. This is what makes the system sensitive enough to detect subtle movements.
Mail Configuration for Notifications
To receive email alerts, users must:
- Enable external program access in Mail.ru settings
- Generate an application-specific password
- Configure SMTP credentials in the code
Complete Firmware Code
/*Created with the help of AI DeepSeek.
Code is provided without any guarantees of functionality.*/
#include <WiFi.h>
#include <ESP_Mail_Client.h>
#include <time.h>
// WiFi access point settings
const char* SSID = "";
const char* PASSWORD = "";
// SMTP settings (Mail.ru)
#define SMTP_HOST "smtp.mail.ru"
#define SMTP_PORT 465
#define EMAIL_SENDER "sender email address"
#define EMAIL_PASSWORD "application password from mail.ru"
#define EMAIL_RECIPIENT "recipient email address"
#define NTP_SERVER "pool.ntp.org"
#define GMT_OFFSET_SEC 3 * 3600 // +3 for Moscow
// Detector parameters
const float BASE_THRESHOLD = 1.2f;
const int SAMPLE_INTERVAL = 200;
const unsigned long EMAIL_COOLDOWN = 30000;
// Global variables
SMTPSession mailSession;
unsigned long lastEmailTime = 0;
float baselineRSSI = -60.0f;
bool isCalibrated = false;
void setup() {
Serial.begin(115200);
Serial.println("\n=== Wi-Fi Motion Detector ===");
connectToWiFi();
configTime(GMT_OFFSET_SEC, 0, NTP_SERVER);
calibrateSensor();
}
void loop() {
float currentRSSI = readFilteredRSSI();
float deviation = abs(currentRSSI - baselineRSSI);
float threshold = max(BASE_THRESHOLD, abs(baselineRSSI * 0.03f));
Serial.print("RSSI: ");
Serial.print(currentRSSI, 1);
Serial.print(" dB | Dev: ");
Serial.print(deviation, 1);
Serial.print("/");
Serial.print(threshold, 1);
Serial.println(" dB");
if (deviation > threshold && isCalibrated) {
handleMotionDetected(deviation, currentRSSI);
baselineRSSI = baselineRSSI * 0.7f + currentRSSI * 0.3f;
} else {
baselineRSSI = baselineRSSI * 0.98f + currentRSSI * 0.02f;
}
if (WiFi.status() != WL_CONNECTED) connectToWiFi();
delay(SAMPLE_INTERVAL);
}
void connectToWiFi() {
if (WiFi.status() == WL_CONNECTED) return;
Serial.print("Connecting to ");
Serial.print(SSID);
WiFi.begin(SSID, PASSWORD);
byte attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nSuccess! RSSI: " + String(WiFi.RSSI()) + " dB");
} else {
Serial.println("\nConnection error!");
}
}
void calibrateSensor() {
Serial.println("Calibrating... Don't move for 5 sec");
float sum = 0;
for (byte i = 0; i < 20; i++) {
sum += WiFi.RSSI();
delay(250);
}
baselineRSSI = sum / 20.0f;
isCalibrated = true;
Serial.print("Baseline level: ");
Serial.println(baselineRSSI, 1);
}
void handleMotionDetected(float deviation, float rssi) {
Serial.println(">>> MOTION DETECTED! <<<");
if (millis() - lastEmailTime > EMAIL_COOLDOWN) {
if (sendAlert(deviation, rssi)) {
lastEmailTime = millis();
}
}
}
bool sendAlert(float deviation, float rssi) {
ESP_Mail_Session config;
config.server.host_name = SMTP_HOST;
config.server.port = SMTP_PORT;
config.login.email = EMAIL_SENDER;
config.login.password = EMAIL_PASSWORD;
SMTP_Message message;
message.sender.name = "ESP32 Motion Alert";
message.sender.email = EMAIL_SENDER;
message.subject = "Motion detected";
message.addRecipient("User", EMAIL_RECIPIENT);
struct tm timeinfo;
String timeStr = getLocalTime(&timeinfo) ?
String(timeinfo.tm_hour) + ":" + String(timeinfo.tm_min) + ":" + String(timeinfo.tm_sec) : "N/A";
message.text.content = ("Details:\n"
"• RSSI: " + String(rssi, 1) + " dB\n" +
"• Deviation: " + String(deviation, 1) + " dB\n" +
"• Time: " + timeStr).c_str();
if (!mailSession.connect(&config) || !MailClient.sendMail(&mailSession, &message)) {
Serial.println("Send error!");
return false;
}
Serial.println("Email sent at " + timeStr);
return true;
}
float readFilteredRSSI() {
static float filtered = WiFi.RSSI();
filtered = filtered * 0.6f + WiFi.RSSI() * 0.4f;
return filtered;
}Future: Full Radar with Four ESP32 Units
The author proposes using four ESP32 units (two transmitter-receiver pairs) to create a complete radar system capable of:
- Distance measurement
- X-Y coordinate tracking
- Approximate 10-30 cm accuracy
- Full short-range radar for under 1,600 rubles
Practical example: you could radar-track a cat that wants to jump on the kitchen table at night and spray it with water from an automated sprayer.
Conclusion
This solution demonstrates practical Wi-Fi-based motion detection without specialized hardware. It's immune to lighting and temperature variations, and its applications extend beyond simple motion sensors to directional tracking systems. All you need is an ESP32 board costing under 400 rubles.
FAQ
What is this article about in one sentence?
This article explains the core idea in practical terms and focuses on what you can apply in real work.
Who is this article for?
It is written for engineers, technical leaders, and curious readers who want a clear, implementation-focused explanation.
What should I read next?
Use the related articles below to continue with closely connected topics and concrete examples.