Detecting People with a PIR Sensor

Draft. This tutorial has not been checked against the physical product yet. Confirm wiring, pin assignments and voltage limits against the product documentation before building it.
DifficultyBeginner
CategorySensors
Supported boardsArduino Uno R3 and compatible ATmega328P boards
Time needed45 minutes
PrerequisitesRead a Push Button
Tested boardNot yet verified
Tested onNot yet verified
Arduino IDE versionNot yet verified
LibraryNot yet verified

What it does

A passive infrared sensor watches for changes in the infrared light falling on it. Everything warm glows in the infrared, and when a person moves across the sensor’s view the pattern shifts, which the module reports as a single digital HIGH on one pin.

“Passive” is the important word. It emits nothing and measures nothing; it only notices change. That one fact explains almost all of its behaviour, including the part people find most frustrating: a person who stands perfectly still disappears. The sensor is not broken. There is no change for it to see.

It is also the easiest sensor in this library to read, because the module does all the work and hands you a HIGH or a LOW.

Before you begin

Required parts and tools

  • An Arduino Uno or compatible board.
  • One PIR motion module. This is Siratek product 2855, the common HC-SR501 type.
  • Three female-to-male jumper wires.

What is on the board

Under the white plastic dome is a sensor the size of a shirt button. The dome itself is a Fresnel lens, moulded into many small facets that divide the view into a fan of separate zones. Movement across the boundary between zones is what the sensor actually detects, which is why walking across the field triggers it far more reliably than walking straight at it.

On the back of the board:

  • A three-pin header.
  • Two orange-topped trimmers: sensitivity, meaning range, and time delay.
  • A yellow jumper with two positions, which sets the retriggering mode.
  • A voltage regulator, which is why the module takes 5 V but its output is a lower voltage.

Read your own pin order

The three pins are VCC, OUT and GND, and the order differs between batches of this module. Some put VCC on the left, some on the right.

The silkscreen is usually printed on the board underneath the white dome. The dome lifts off: it is a push fit, not glued. Take it off, read the three labels, put it back.

Guessing here reverses power across the module, so it is worth the thirty seconds.

The output is about 3.3 V

The module has its own regulator, and its HIGH is roughly 3.3 V rather than 5 V.

An Uno reads anything above about 3 V as HIGH, so this works. But the margin is small, and it is the reason this sensor is more sensitive than most to a long wire or a poor connection. If a reading seems erratic, measure the OUT pin with a meter while you wave at it, rather than assuming the sensor is faulty.

The two trimmers

Both turn with a small screwdriver, and both are worth setting deliberately rather than leaving at whatever the factory chose.

Sensitivity sets the range, roughly three to seven metres. Start it at minimum. A sensor at maximum range in a small room triggers on everything, including warm air moving, and feels hopelessly unreliable.

Time delay sets how long OUT stays HIGH after a trigger, from a few seconds to several minutes. Start it at minimum too, so you can see each detection separately while testing. Turn it up only when you know what you want.

Which trimmer is which varies by board. Turn one a little, test, and label it with a marker.

The jumper, which decides what happens during a long event

  • Repeat, sometimes marked H: while movement continues, the timer keeps restarting, so OUT stays HIGH the whole time and goes LOW once the movement stops and the delay runs out.
  • Single, sometimes marked L: OUT goes HIGH, stays HIGH for the delay, then goes LOW regardless of whether the movement is still happening, and only then can it trigger again.

Repeat is what you want almost always. A light that should stay on while someone is in the room needs it. Single mode is for counting discrete events.

Give it a minute to settle

When first powered, the module needs thirty seconds to a minute to establish what the room normally looks like. During that time it reports nonsense, often a long HIGH.

This is normal and it catches everyone, because the natural thing to do is upload the sketch and stare at the output immediately. Power it, wait a minute, then start testing.

Pin reference

Pin or label Connect to Purpose Notes
VCC Uno 5V Power 4.5 to 20 V; the regulator handles it
OUT Uno D2 Motion signal HIGH on detection, around 3.3 V
GND Uno GND Ground
A PIR module with VCC to 5V, GND to ground and OUT to pin 2, showing the two trimmers and the retrigger jumper on the back of the board
Three wires, and three settings on the board that matter more than the code.

Wiring steps

  1. Unplug the USB cable.
  2. Lift the white dome off and read the three pin labels. Put the dome back.
  3. GND to GND.
  4. VCC to 5V.
  5. OUT to D2.
  6. Set both trimmers to minimum and the jumper to repeat.
  7. Plug the USB cable back in, then leave it alone for a minute before judging anything.

Software and libraries

No library is required. The module gives you a plain digital signal, so this is digitalRead() and nothing more.

Use plain INPUT, not INPUT_PULLUP. The module drives the pin both ways, so an internal pull-up would be fighting it.

Minimal working example

// Siratek: reporting motion, printing only when the state changes.

const int pirPin = 2;
const int ledPin = 13;

int lastState = LOW;

void setup() {
  pinMode(pirPin, INPUT);
  pinMode(ledPin, OUTPUT);

  Serial.begin(9600);
  Serial.println("Siratek: warming up, about a minute.");
  delay(60000);
  Serial.println("ready.");
}

void loop() {
  int state = digitalRead(pirPin);

  if (state != lastState) {
    lastState = state;
    digitalWrite(ledPin, state);
    Serial.println(state == HIGH ? "motion" : "clear");
  }
}

Printing only on a change, rather than every pass, is what makes this readable. A loop that prints the state every time floods the serial monitor with thousands of identical lines and hides the moment you care about.

The onboard LED on pin 13 gives you a response you can see without looking at the laptop, which matters when you are across the room waving at the sensor.

A light that stays on while someone is there

The module’s own time delay could do this, but doing it in software gives you a number you can change without a screwdriver.

// Siratek: hold an output on for a while after the last movement.

const int pirPin = 2;
const int lampPin = 8;

const unsigned long holdFor = 15000;  // milliseconds after the last motion

unsigned long lastMotion = 0;
bool lampOn = false;

void setup() {
  pinMode(pirPin, INPUT);
  pinMode(lampPin, OUTPUT);
  Serial.begin(9600);
  delay(60000);
  Serial.println("Siratek: ready.");
}

void loop() {
  if (digitalRead(pirPin) == HIGH) {
    lastMotion = millis();
    if (!lampOn) {
      lampOn = true;
      digitalWrite(lampPin, HIGH);
      Serial.println("on");
    }
  }

  if (lampOn && millis() - lastMotion >= holdFor) {
    lampOn = false;
    digitalWrite(lampPin, LOW);
    Serial.println("off");
  }
}

Set the module’s own delay to minimum when using this, so the two timers do not fight. The hardware reports events; the software decides how long they last.

Expected result

After the warm-up, OUT goes HIGH when you move in front of the sensor and returns LOW a few seconds after you stop. With the trimmers at minimum you should need to be within a couple of metres.

Wave your hand across the sensor’s view rather than towards it, and you will see it trigger far more readily. That is the Fresnel lens doing its job.

Troubleshooting

Symptom Likely cause Check or fix
Triggers constantly at first Still warming up Wait a minute after power-up
Triggers at random afterwards Sensitivity too high, or a draught Lower sensitivity; keep it away from vents and sunlight
Never triggers Power reversed, pin order assumed Lift the dome and read the labels
Stays HIGH for ages Time delay trimmer Turn it down
One long HIGH instead of separate events Jumper in repeat, which is usually right Use single mode only if counting events
Misses a person standing still Working as designed PIR sees change, not presence
Misses someone walking towards it Fewer zone crossings Mount it across the path, not along it
Erratic over a long wire Output is only about 3.3 V Shorten the wire; measure OUT with a meter
Triggers when a lamp switches on Infrared from a hot filament Reposition, or lower sensitivity
Works indoors, useless outdoors Sunlight and moving warm air PIR is an indoor sensor

Next steps

  • Easier first: Read a Push Button, which is the same digitalRead() with no settings.
  • Related products: an active buzzer for an alert, or a relay to switch a real lamp.
  • Next project: combine it with the relay tutorial for a light that comes on when someone enters, using a low-voltage lamp.

Sources and attribution

All text, wiring guidance and code in this tutorial are original Siratek work.

  • HC-SR501 module documentation for the warm-up period, the adjustable range and delay, and the retriggering jumper.
  • The two trimmers, the jumper and the onboard regulator described here were read from the Siratek product photographs for item 2855.

Pin order varies between batches of this module, so this tutorial tells the reader to lift the dome and read their own silkscreen rather than giving a fixed order. Range and delay figures are typical values that depend on the trimmer settings. No third-party tutorial text, diagrams, photographs or code has been reproduced here.

Product used in this tutorial: PIR Motion Sensor
Shop
Sidebar
0 Wishlist
0 Cart