|
Millimeter Wave Radar Sensor RCWL-0516: Human Presence Detection in Practice

Millimeter Wave Radar Sensor RCWL-0516: Human Presence Detection in Practice

Have you ever experienced this: you’re sitting at your desk completely still, and the sensor decides “nobody’s here” and turns off the lights?

That’s the classic problem with traditional PIR (Passive Infrared) sensors — they detect changes in body heat. If you don’t move, they go “blind.”

The RCWL-0516 millimeter wave radar sensor we’re discussing today solves exactly this pain point. It uses Doppler radar principles — as long as you’re there, even if you’re just dozing off or breathing lightly, it can detect you.

In this article, we’ll go from theory to practice, building a complete human presence detection system with Arduino and ESP32.

What is the RCWL-0516?

The RCWL-0516 is a motion detection module based on 3.15 GHz microwave Doppler radar. It works by transmitting microwave signals and receiving reflected signals, comparing the frequency difference (Doppler shift) between the two to determine if an object is moving.

Compared to PIR infrared sensors, millimeter wave radar has several clear advantages:

FeatureRCWL-0516 RadarPIR Infrared
Detection PrincipleDoppler microwaveInfrared thermal radiation change
PenetrationCan penetrate plastic, wood, thin wallsCannot penetrate, requires line of sight
Static DetectionCan detect micro-movements like breathingQuickly determines “no one” after stillness
Detection Range5-7 meters adjustableTypically 3-5 meters
Temperature ImpactUnaffected by ambient temperatureSensitivity decreases in high temperature
Concealed InstallationCan hide behind ceiling/wallsNeeds to be exposed

Hardware Checklist

ComponentQuantityDescription
RCWL-0516 sensor module1Core detection module
ESP32 development board1Main controller (Arduino Uno also works)
Breadboard + jumper wiresSeveralFor wiring
LED module1Status indicator (or use onboard LED)
100kΩ resistor1Optional, for adjusting detection range
USB data cable1For power and programming

The RCWL-0516 module has 5 pins:

+---+
|   |
| 3V3  ← 3.3V output (can power external devices)
| VIN  ← 4-28V power input
| GND  ← Ground
| OUT  ← Digital output: target detected = HIGH(3.3V), no target = LOW(0V)
| CDS  ← Photoresistor input: add resistor or photosensitive element to disable during daytime
+---+

How It Works: The Doppler Effect

The core principle of the RCWL-0516 isn’t complicated — in one sentence: transmit → reflect → detect frequency shift.

  1. The microwave oscillator inside the module generates a continuous wave at 3.15 GHz
  2. The electromagnetic waves radiate outward through the PCB antenna
  3. When hitting a moving object, the reflected wave frequency changes (Doppler shift)
  4. Internal circuitry compares the frequency difference between transmitted and reflected waves
  5. If the frequency difference exceeds a threshold, it determines “motion detected” and the OUT pin outputs HIGH

Doppler shift formula:

Δf = 2 × v × f₀ / c

Where v is target velocity, f₀ is transmission frequency, and c is the speed of light. Even when your chest rises and falls only a few millimeters during breathing, this tiny velocity change is amplified by the 3.15 GHz electromagnetic waves, producing a detectable frequency difference.

This is why the RCWL-0516 can detect people who are “almost motionless.”

Wiring: RCWL-0516 + ESP32

Wiring is very simple — just three wires:

RCWL-0516          ESP32
  VIN      ────▶   3.3V (or 5V)
  GND      ────▶   GND
  OUT      ────▶   GPIO4 (any digital pin)
  CDS      ────▶   Floating or connect photoresistor

Note: The RCWL-0516’s VIN supports 4-28V wide voltage input, but when using ESP32, connecting directly to 3.3V also works (the module has internal voltage regulation). If connected to 5V, detection sensitivity will be slightly higher.

Practice 1: Basic Detection (Arduino)

The most basic usage: detect presence → print to serial + light up LED.

// RCWL-0516 Basic Human Presence Detection
// Hardware: RCWL-0516 + Arduino Uno
const int SENSOR_PIN = 2;  // RCWL-0516 OUT to Arduino D2
const int LED_PIN = 13;    // Onboard LED

void setup() {
  Serial.begin(115200);
  pinMode(SENSOR_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);
  
  Serial.println("RCWL-0516 Human Presence Detection Starting...");
  Serial.println("Waiting for sensor initialization...");
  delay(3000);  // Give sensor 3 seconds to initialize
}

void loop() {
  int sensorValue = digitalRead(SENSOR_PIN);
  
  if (sensorValue == HIGH) {
    digitalWrite(LED_PIN, HIGH);
    Serial.println("[PRESENT] Human activity detected!");
  } else {
    digitalWrite(LED_PIN, LOW);
    Serial.println("[ABSENT] No one detected");
  }
  
  delay(500);  // 500ms sampling interval
}

Once this code is running, if you wave your hand in front of the sensor, walk around, or even just sit and move a finger, the serial port will print [PRESENT].

Practice 2: ESP32 + Anti-False-Trigger Filtering

The RCWL-0516 has a common issue: the output signal sometimes jitters, switching HIGH-LOW repeatedly in short periods. We solve this with simple time window filtering.

// RCWL-0516 ESP32 Version: With Anti-False-Trigger Filtering
const int SENSOR_PIN = 4;     // OUT to GPIO4
const int LED_PIN = 2;        // Onboard LED
const int LED_BUILTIN = 13;

// Filtering parameters
const unsigned long PRESENCE_CONFIRM_MS = 2000;   // Confirm "someone present" requires 2 seconds continuous
const unsigned long ABSENCE_CONFIRM_MS = 10000;   // Confirm "no one" requires 10 seconds continuous

bool currentState = false;        // Current determined presence state
unsigned long lastChangeTime = 0; // Last state change time

void setup() {
  Serial.begin(115200);
  pinMode(SENSOR_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  
  Serial.println("=== RCWL-0516 ESP32 Human Presence Detection ===");
  Serial.println("Anti-false-trigger filtering enabled");
  delay(3000);
}

void loop() {
  bool rawSignal = (digitalRead(SENSOR_PIN) == HIGH);
  unsigned long now = millis();
  
  if (rawSignal && !currentState) {
    // Detected "someone" signal from "no one" state
    if (now - lastChangeTime >= PRESENCE_CONFIRM_MS) {
      currentState = true;
      Serial.println("[✅ CONFIRMED] Human presence detected!");
      digitalWrite(LED_PIN, HIGH);
      digitalWrite(LED_BUILTIN, HIGH);
      lastChangeTime = now;
    }
  }
  else if (!rawSignal && currentState) {
    // Detected "no one" signal from "someone" state
    if (now - lastChangeTime >= ABSENCE_CONFIRM_MS) {
      currentState = false;
      Serial.println("[❌ CONFIRMED] Human has left");
      digitalWrite(LED_PIN, LOW);
      digitalWrite(LED_BUILTIN, LOW);
      lastChangeTime = now;
    }
  }
  
  // Reset timer when signal matches current state
  if (rawSignal == currentState) {
    lastChangeTime = now;
  }
  
  delay(100);
}

The logic of this code:

  • Must detect HIGH continuously for 2 seconds to determine “someone present” (prevents false triggers)
  • Must detect LOW continuously for 10 seconds to determine “no one has left” (prevents breathing gaps from being misjudged as departure)
  • Timer resets each time the signal matches the current state

You can adjust these two time thresholds based on your actual environment.

Practice 3: ESP32 + MQTT Reporting (Smart Home Integration)

This is where it gets truly useful — sending detection results to an MQTT Broker for Home Assistant or other smart home platforms to handle.

// RCWL-0516 + ESP32 + MQTT → Smart Home Human Presence Sensor
#include <WiFi.h>
#include <PubSubClient.h>

// WiFi configuration
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

// MQTT configuration
const char* mqtt_server = "192.168.1.100";  // Your MQTT Broker address
const int mqtt_port = 1883;
const char* mqtt_user = "mqtt_user";
const char* mqtt_password = "mqtt_password";

const int SENSOR_PIN = 4;
const char* mqtt_topic_presence = "home/sensor/presence/office";

WiFiClient espClient;
PubSubClient mqttClient(espClient);

// Filtering
bool presenceState = false;
unsigned long stateChangeTime = 0;
const unsigned long ON_DELAY = 2000;
const unsigned long OFF_DELAY = 10000;

void setup() {
  Serial.begin(115200);
  pinMode(SENSOR_PIN, INPUT);
  
  // Connect WiFi
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected!");
  
  // Configure MQTT
  mqttClient.setServer(mqtt_server, mqtt_port);
  mqttClient.setKeepAlive(30);
  
  connectMQTT();
  delay(3000);  // Sensor initialization
}

void connectMQTT() {
  while (!mqttClient.connected()) {
    String clientId = "ESP32-Presence-" + String(random(0xffff), HEX);
    if (mqttClient.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
      Serial.println("MQTT connected");
      // Publish offline status
      mqttClient.publish(mqtt_topic_presence, "OFFLINE", true);  // retain
    } else {
      Serial.print("MQTT connection failed, rc=");
      Serial.println(mqttClient.state());
      delay(3000);
    }
  }
}

void loop() {
  if (!mqttClient.connected()) {
    connectMQTT();
  }
  mqttClient.loop();
  
  bool raw = (digitalRead(SENSOR_PIN) == HIGH);
  unsigned long now = millis();
  
  if (raw && !presenceState) {
    if (now - stateChangeTime >= ON_DELAY) {
      presenceState = true;
      mqttClient.publish(mqtt_topic_presence, "ONLINE", true);
      Serial.println("[MQTT] Published: ONLINE (someone present)");
      stateChangeTime = now;
    }
  }
  else if (!raw && presenceState) {
    if (now - stateChangeTime >= OFF_DELAY) {
      presenceState = false;
      mqttClient.publish(mqtt_topic_presence, "OFFLINE", true);
      Serial.println("[MQTT] Published: OFFLINE (no one)");
      stateChangeTime = now;
    }
  }
  
  if (raw == presenceState) {
    stateChangeTime = now;
  }
  
  delay(100);
}

In Home Assistant, you can use MQTT Discovery to automatically discover this sensor:

# Home Assistant configuration.yaml
binary_sensor:
  - platform: mqtt
    name: "Office Human Presence"
    state_topic: "home/sensor/presence/office"
    payload_on: "ONLINE"
    payload_off: "OFFLINE"
    device_class: occupancy
    qos: 1

Once set up, your Home Assistant will have a new “Office Human Presence” sensor — much more sensitive than PIR, and won’t keep deciding “no one’s here.”

Advanced: Adjusting Detection Range

The RCWL-0516’s default detection range is about 5-7 meters. If you want to use it in smaller spaces (like a bathroom or closet), you can adjust the detection range.

Method 1: Add a Shield

The simplest and most brute-force approach — make a cover from metal foil (aluminum foil) to cover the sensor’s antenna side, leaving only one direction open. This can significantly reduce the detection range.

Method 2: Modify the Capacitor

The C-TM capacitor on the module determines detection sensitivity. The original is typically 1nF:

  • Reduce capacitance → Lower sensitivity → Shorter detection range
  • Increase capacitance → Higher sensitivity → Longer detection range

Reference values:

C-TM CapacitorApproximate Detection Range
0.5nF~2-3 meters
1nF (default)~5-7 meters
2nF~8-10 meters

Method 3: Add a Potentiometer to VIN

Lowering the supply voltage reduces transmission power, thereby shortening detection range. Add an adjustable potentiometer in the VIN circuit to fine-tune from 4V to 9V.

CDS Pin: Automatic Daytime Disable

The RCWL-0516’s CDS pin is for connecting a photoresistor. When ambient light is bright enough, the sensor automatically stops outputting HIGH, avoiding wasted resources during the day.

Wiring:

CDS pin ──▶ Photoresistor ──▶ 5V

                10kΩ resistor

                GND

When it’s dark, the photoresistor’s resistance increases → CDS pin voltage is low → sensor works normally When it’s bright, the photoresistor’s resistance decreases → CDS pin voltage is high → sensor is disabled

If you don’t need this feature, just leave the CDS pin floating (it defaults to working normally).

Common Troubleshooting

Q1: Sensor always outputs HIGH, never goes LOW?

Possible causes:

  • Continuous motion objects in detection range: Fan rotation, curtain movement, pet movement will all trigger it. Check the surrounding environment, or shorten the detection range.
  • Sensor malfunction: Measure the OUT pin voltage with a multimeter. Normally with no one present, it should be close to 0V.
  • Insufficient power: VIN voltage below 4V may cause abnormal operation. Ensure power is within the 4-28V range.

Q2: Sensor sensitivity too low, can’t detect people?

  • Check power supply: Sensitivity is lower at 3.3V, try 5V
  • Check installation direction: Radar waves mainly emit from the front of the module (component side), sensitivity is very low on the back
  • Increase C-TM capacitance (see “Adjusting Detection Range” section above)

Q3: Output signal jitters frequently, state keeps switching?

This is a known characteristic of the RCWL-0516. The solution is the time window filtering from Practice 2 above. Adjust the ON_DELAY and OFF_DELAY values based on your actual environment.

Q4: Can it detect people in the next room through walls?

Theoretically yes, but actual results depend on:

  • Wall material (drywall/wood is easy to penetrate, reinforced concrete almost impossible)
  • Distance (effective range greatly reduced after penetrating walls)
  • Interference (if there are fans, air conditioners, etc. next door, they’ll also be detected)

If your scenario is “detecting whether someone is in the next room,” I recommend using WiFi probes or Bluetooth beacon solutions instead for better reliability.

Q5: RCWL-0516 vs LD2410, which to choose?

If you’re starting a new project, I strongly recommend looking at the LD2410 (24GHz millimeter wave radar). Comparison:

FeatureRCWL-0516LD2410 (24GHz)
Frequency3.15 GHz24 GHz
Detection AccuracyCan only determine presence/absenceCan distinguish presence/absence + distance info
Micro-motion DetectionAverageExcellent (can detect breathing, heartbeat)
Price~¥5~¥20
InterfaceDigital OUTUART serial
Recommended ScenariosSimple presence detectionPrecise human presence detection

The LD2410 is a newer-generation solution, suitable for scenarios requiring higher precision. But the RCWL-0516 wins on price and simplicity — it’s more than enough for learning and entry-level projects.

Summary

The RCWL-0516 millimeter wave radar sensor is one of the best choices for getting started with human presence detection:

  • Pros: Cheap, simple wiring, can penetrate obstacles, unaffected by temperature, can detect micro-movements
  • Cons: Signal can jitter, cannot provide distance information, cannot distinguish humans from pets

With the three practice projects in this article, you can now build:

  1. Basic human detection (local indication)
  2. Stable detection with anti-false-trigger filtering
  3. Smart home integration with MQTT reporting

Next, you could deploy multiple sensors in different rooms and use Home Assistant for whole-house human presence monitoring; or combine with light sensors to implement “turn on lights only when someone is present AND it’s dark” smart lighting logic.

Got questions or want to share your project? Feel free to discuss in the comments!