Embedded Development PIR Human Detection Optimization Tips: False Trigger Solutions
Why Does Your PIR Sensor Always False Trigger?
Friends who have used PIR (Passive Infrared) human body sensors have probably encountered this trouble: the sensor mysteriously triggers when no one is passing by; or it fails to detect when someone is standing there motionless.
It’s not that the sensor is broken, it’s determined by PIR’s working principle. Today we’ll discuss how to make PIR sensors “smarter” and minimize false trigger rates.
PIR Sensor Working Principle Quick Overview
PIR sensors don’t detect “people”, they detect changes in infrared radiation. Human body temperature is about 37°C, radiating infrared rays of specific wavelengths. When people move, the infrared radiation intensity received by the sensor changes, triggering the signal.
Here’s the key: PIR detects change, not presence. This is why sensors “lose target” when people stand still.
Hardware List
| Model | Description | Price | Notes |
|---|---|---|---|
| HC-SR501 | Classic PIR module, adjustable sensitivity | ¥8-12 | Most commonly used, recommended for beginners |
| HC-SR505 | Small PIR module | ¥6-10 | Compact size |
| AM312 | Micro PIR sensor | ¥5-8 | Suitable for portable projects |
| Arduino Nano | Development board | ¥15-20 | Or ESP32/STM32 |
| Potentiometer | 10kΩ | ¥1 | For sensitivity adjustment |
| LED | 5mm | ¥0.5 | Status indication |
Hardware Connection (HC-SR501 Example)
HC-SR501 has 3 pins:
-
VCC: 5V power
-
OUT: Signal output (high level trigger)
-
GND: Ground
# Arduino connection
HC-SR501 VCC → Arduino 5V
HC-SR501 OUT → Arduino D2
HC-SR501 GND → Arduino GND
Module has two adjustable potentiometers:
-
Time adjustment: Controls output high level duration after trigger (5-300 seconds)
-
Sensitivity adjustment: Controls detection distance (3-7 meters)
Basic Code Example
Start with the simplest:
const int pirPin = 2;
const int ledPin = 13;
void setup() {
pinMode(pirPin, INPUT);
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
Serial.println("PIR sensor initialized");
}
void loop() {
int pirState = digitalRead(pirPin);
if (pirState == HIGH) {
digitalWrite(ledPin, HIGH);
Serial.println("Human movement detected!");
delay(1000);
} else {
digitalWrite(ledPin, LOW);
}
}
This code works, but has two problems:
-
Frequent false triggers shortly after power-on, because sensor internal circuits haven’t stabilized yet
-
Heat source changes in environment (like heating, sunlight) also cause false judgments, need software filtering to eliminate interference
Optimization Solution 1: Preheating
HC-SR501 needs preheating time after power-on to stabilize internal circuits. We can add delay in setup:
void setup() {
pinMode(pirPin, INPUT);
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
Serial.println("PIR sensor preheating...");
delay(60000); // Preheat for 60 seconds
Serial.println("Preheating complete, start detection");
}
Optimization Solution 2: Software Filtering Algorithm
Hardware adjustment has limitations, software filtering can control more precisely. Here’s a practical filtering algorithm:
const int pirPin = 2;
const int ledPin = 13;
// Filtering parameters
const int SAMPLE_COUNT = 5; // Sampling count
const int TRIGGER_THRESHOLD = 3; // Trigger threshold (3 out of 5 detections)
const int COOLDOWN_MS = 5000; // Cooldown time, avoid repeated triggering
unsigned long lastTriggerTime = 0;
bool isTriggered = false;
void setup() {
pinMode(pirPin, INPUT);
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
Serial.println("PIR sensor preheating...");
delay(60000);
Serial.println("System ready");
}
bool readPIRWithFilter() {
int triggerCount = 0;
for (int i = 0; i < SAMPLE_COUNT; i++) {
if (digitalRead(pirPin) == HIGH) {
triggerCount++;
}
delay(50); // Sample every 50ms
}
return triggerCount >= TRIGGER_THRESHOLD;
}
void loop() {
unsigned long currentTime = millis();
// Don't process during cooldown
if (currentTime - lastTriggerTime < COOLDOWN_MS) {
return;
}
if (readPIRWithFilter()) {
digitalWrite(ledPin, HIGH);
isTriggered = true;
lastTriggerTime = currentTime;
Serial.println("Confirmed human detection!");
} else {
digitalWrite(ledPin, LOW);
isTriggered = false;
}
}
Algorithm explanation:
-
Sample 5 times in 250ms
-
Need at least 3 detections to confirm trigger (majority voting)
-
5 second cooldown prevents same event from triggering repeatedly
This method can reduce false trigger rate by over 80%.
Optimization Solution 3: Installation Position Optimization
PIR sensor’s installation position is crucial for reducing false triggers:
Correct Installation
-
Install height: 2-2.5 meters, slightly above human head height
-
Detection direction: Perpendicular to human movement direction (not parallel)
-
Avoid heat sources: Stay away from air conditioner vents, heaters, direct sunlight
-
Avoid airflow: Don’t install where strong air currents pass (fans, ventilation ducts)
Wrong Installation
-
Pointing at windows (sunlight changes trigger false alarms)
-
Near heating equipment (temperature fluctuations cause interference)
-
Too low (pets trigger easily)
-
Parallel to walking direction (detection range becomes very small)
Advanced Application: Smart Home Lighting System
Combine PIR sensor with light sensor to implement automatic lighting control:
const int pirPin = 2;
const int lightSensorPin = A0;
const int lightPin = 9; // PWM control LED brightness
bool isLightOn = false;
unsigned long lastTriggerTime = 0;
const unsigned long COOLDOWN_MS = 30000; // 30 second cooldown
void setup() {
pinMode(pirPin, INPUT);
pinMode(lightPin, OUTPUT);
Serial.begin(9600);
Serial.println("Smart lighting system starting...");
delay(60000); // PIR preheating
Serial.println("System ready");
}
bool isEnvironmentDark() {
int lightValue = analogRead(lightSensorPin);
return lightValue < 300; // Adjust threshold based on actual environment
}
void loop() {
unsigned long currentTime = millis();
// Auto turn off after timeout
if (isLightOn && (currentTime - lastTriggerTime > COOLDOWN_MS)) {
analogWrite(lightPin, 0);
isLightOn = false;
Serial.println("Auto light off");
return;
}
// Human detection
if (readPIRWithFilter() && isEnvironmentDark() && !isLightOn) {
analogWrite(lightPin, 200); // 80% brightness
isLightOn = true;
lastTriggerTime = currentTime;
Serial.println("Human detected, turn on light");
}
}
Cost Analysis
Building a complete PIR human detection system costs very little:
| Component | Unit Price | Quantity | Subtotal |
|---|---|---|---|
| HC-SR501 PIR module | ¥10 | 1 | ¥10 |
| Arduino Nano | ¥18 | 1 | ¥18 |
| Photoresistor module | ¥5 | 1 | ¥5 |
| LED beads | ¥1 | 3 | ¥3 |
| Resistors and capacitors | ¥5 | 1 | ¥5 |
| PCB prototype board | ¥3 | 1 | ¥3 |
| Enclosure (3D printed) | ¥10 | 1 | ¥10 |
| Total | ¥54 |
Compared to commercial smart human sensors (¥80-200), DIY solution reduces cost by over 50%, and functions can be completely customized.
Power Consumption Optimization Tips
For battery-powered projects, power consumption is key. HC-SR501 static current is about 50μA, about 2mA when triggered. Here are methods to reduce power consumption:
Use Sleep Mode
#include <avr/sleep.h>
const int pirPin = 2;
volatile bool pirTriggered = false;
// PIR interrupt callback
void pirISR() {
pirTriggered = true;
}
void setup() {
pinMode(pirPin, INPUT);
attachInterrupt(digitalPinToInterrupt(pirPin), pirISR, RISING);
Serial.begin(9600);
}
void loop() {
if (pirTriggered) {
pirTriggered = false;
// Wake up processing
Serial.println("Human detected!");
// Execute tasks...
delay(1000);
}
// Enter sleep
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();
// Continue after wake up
sleep_disable();
}
After using sleep mode, standby current can drop below 10μA, two AA batteries can work for over 1 year.
Reduce Sampling Frequency
When continuous detection isn’t needed, can work intermittently:
void loop() {
// Work for 1 second
bool detected = readPIRWithFilter();
if (detected) {
handleDetection();
}
// Sleep for 10 seconds
delay(10000);
}
This reduces average power consumption by 90%.
ESP32 Version Code
If using ESP32, can utilize its deep sleep feature:
#include <esp_sleep.h>
#include <driver/rtc_io.h>
#define PIR_PIN 4
#define uS_TO_S_FACTOR 1000000
#define TIME_TO_SLEEP 5
RTC_DATA_ATTR int bootCount = 0;
void print_wakeup_reason() {
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason) {
case ESP_SLEEP_WAKEUP_EXT0:
Serial.println("Wakeup caused by external signal using RTC_IO");
break;
case ESP_SLEEP_WAKEUP_EXT1:
Serial.println("Wakeup caused by external signal using RTC_CNTL");
break;
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("Wakeup caused by timer");
break;
default:
Serial.println("Wakeup was not caused by deep sleep");
break;
}
}
void setup() {
Serial.begin(115200);
bootCount++;
Serial.println("Boot number: " + String(bootCount));
print_wakeup_reason();
// Configure PIR pin as wake source
esp_sleep_enable_ext0_wakeup(GPIO_NUM_4, HIGH);
if (digitalRead(PIR_PIN) == HIGH) {
Serial.println("Human detected! Sending notification...");
// Connect WiFi and send notification
// ...
}
Serial.println("Going to sleep now");
esp_deep_sleep_start();
}
void loop() {
// This will never be reached
}
ESP32’s deep sleep current is only about 10μA, suitable for long-term battery-powered applications.
Integration with Other Systems
Data Logging to Database
Use ESP32 to send data to cloud database for historical analysis:
#include <InfluxDbClient.h>
#include <InfluxDbCloud.h>
InfluxDbClient client("http://your-influx-server:8086", "iot_db");
Point motion_point("motion_events");
void logMotionEvent() {
motion_point.clearFields();
motion_point.addField("detected", 1);
motion_point.setTime(DateTime.now());
if (client.writePoint(motion_point)) {
Serial.println("Data recorded to InfluxDB");
}
}
void setup() {
// ... initialization code
client.setConnectionParamsV1();
}
Then create dashboard in Grafana to see daily human activity heatmap.
Integration with Home Assistant
If using Home Assistant, can integrate via MQTT:
#include <WiFi.h>
#include <PubSubClient.h>
const char* mqtt_server = "home-assistant.local";
const char* mqtt_topic = "home/sensor/pir_livingroom";
WiFiClient espClient;
PubSubClient client(espClient);
void reconnect() {
while (!client.connected()) {
if (client.connect("ESP32-PIR-Sensor")) {
Serial.println("MQTT connected");
} else {
delay(5000);
}
}
}
void setup() {
client.setServer(mqtt_server, 1883);
// ... other initialization
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
if (readPIRWithFilter()) {
client.publish(mqtt_topic, "ON");
Serial.println("MQTT: Sent ON");
}
}
Add in Home Assistant’s configuration.yaml:
binary_sensor:
- platform: mqtt
name: "Living Room Human Sensor"
state_topic: "home/sensor/pir_livingroom"
payload_on: "ON"
payload_off: "OFF"
device_class: motion
This way you can create automations in Home Assistant, like “turn on lights when human detected”.
Summary
PIR human sensors are low cost, low power, common choice for IoT projects. But to use them well requires:
-
Understand PIR working principle - detects infrared changes not presence, inability to detect stationary targets is normal
-
Do preheating well - wait 30-60 seconds after power-on for sensor to stabilize, avoid initial false triggers
-
Use software filtering algorithm to improve reliability - multiple sampling with majority voting, reduces occasional interference
-
Reasonable installation position is key - avoid air conditioner vents, heaters, direct sunlight and other heat source change areas
-
Choose appropriate model based on application scenario - HC-SR501 suitable for general scenarios, AM312 suitable for space-constrained portable devices
-
Make good use of cooldown time setting - avoid same trigger being counted repeatedly, improves detection accuracy
-
Use sleep mode for battery-powered projects - standby current can drop below 10μA, two AA batteries can last over a year
From simple LED indication, to smart home automation, to remote monitoring systems, PIR sensors can handle it all. The key is understanding its characteristics, using appropriate methods to avoid limitations.
Hope this blog post is helpful to you!