1. An introduction to PIR sensors
1.1 Pros & cons
1.2 What you will learn
2. Interfacing a PIR sensor with Raspberry Pi
2.1 Setting up your environment
2.2 GPIO pin numbering schemes
2.3 Example PIR circuit & code
PIR is an acronym for passive infrared, and it’s particularly awesome because it doesn’t emit any infrared light (which means low power consumption). It only detects fairly rapid changes in its infrared field of view.
The sensing part itself is a bimetallic pyroelectric sensor consisting of materials that respond in two different ways to infrared energy, and its weak electric output signal is amplified by a low-noise JFET to yield a useable voltage level.
As the pyroelectric sensor plate is flat and not suitable for e.g. a 110-degree field of vision, a focusing stage called Fresnel lenses is added, beautifully similar to NASA’s deep-space antennas. This rounded plastic part sits in front of the sensor and is designed to reflect infrared onto both of the two metal plates that comprise the pyroelectric sensor, which in turn read changes in infrared patterns.
By design, with an amplifying stage, PIR sensors are capable of tripping a relay (to switch on a light, for example) or sending a signal to a nearby installation, informing it that there’s movement in a particular zone. They are omnipresent in our urban modernity, and for every nearby muffled “click” and sudden illumination, you’re in the presence of just such an advanced sensor.
PIR sensors are reliable, long-lived and most importantly, flexible in myriad application scenarios, usually having adjustable infrared threshold and ON-time.
All’s not well with PIR sensors, however. They are prone to false positives (from common phenomena like draft winds), and highly accurate motion detection is best handled by microwave sensors, which is now the standard for expensive car alarms, as an example..
PIRs also require up to 30-60 seconds before they settle when first powered on and will usually fire at least one false positive on the signal pin when first powered on. This is because it is designed to respond to changing infrared patterns, and powering up constitutes a change. This means your device may have to delay execution until the PIR is settled. Depending on the application, this can be a bit of a pain.
The HC-SR501 we’ll be using with a Raspberry Pi further down in this article has lots of knobs and levers. You can adjust nearly every important aspect of its functionality and how they interact to provide the perfect sensory input for your application.
Your PIR sensor is useless unless it is interfaced with something, and here you will learn to interface it with your Raspberry Pi. Example code is provided, utilizing WiringPi’s “gpio” utility in shell-script.
It will be easy to modify it to do exactly what you wish, especially if you consult the Advanced Bash Scripting Guide, which we’ll be installing momentarily.
You’ll need to set up your Raspberry Pi first. Follow this guide to accomplish this quickly and easily.
When this is done, copy the script below to your Raspberry Pi, and run it with:
1 2 3 |
sudo bash rpi_prepare.sh |
It will install most software packages relevant for interfacing with hardware over GPIO. The package “abs-guide” will install the Advanced Bash Scripting Guide, and is recommended for all users. Find it in:
‘/usr/share/doc/abs-guide/html/index.html’
ProTip: As user “pi,” paste the following into your terminal:
1 2 3 4 5 |
cd /usr/share/doc/abs-guide/html && \ python -m SimpleHTTPServer >/dev/null 2>&1 & cd - |
… and view the guide at http://raspberry.local:8000 – if mDNS between your workstation and the Pi isn’t playing nice, use:
1 2 3 |
hostname -I |
and get its IP address, then visit http://pi-ip:8000 …
— Alternatively, just download it in PDF format here.
This guide is exceptionally useful, even if you’re not going to be writing scripts, but will just be using the terminal.
[ begin rpi_prepare.sh ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#! /usr/bin/env bash set -eu -o pipefail DEBIAN_FRONTEND="noninteractive" DEBIAN_PRIORITY="critical" DEBCONF_NOWARNINGS="yes" export DEBIAN_FRONTEND DEBIAN_PRIORITY DEBCONF_NOWARNINGS _pkg_list="vim zip unzip curl wget pigpio wiringpi rsync wiringpi python-rpi.gpio python3-rpi.gpio rpi.gpio-common git python3-serial python-serial abs-guide python-gpiozero python-gpiozero-doc python3-gpiozero" # Upgrade system and installed packages sudo apt update || echo failed to update index list sudo dpkg --configure -a || echo failed to fix \ interrupted upgrades sudo apt --fix-broken --fix-missing install || echo \ failed to fix conflicts sudo apt -y --allow-downgrades --fix-broken \ --fix-missing dist-upgrade # Install $_pkg_list sudo apt update sudo apt-get -y install $_pkg_list |
[ end rpi_prepare.sh ]
If you’re running your Raspberry Pi headless and want to add SSH connectivity, create a file named ‘ssh’ on the microSD’s first partition (/boot), allowing SSH access on first boot. If you are using a USB-TTL adapter, issue the following command to ensure serial connectivity:
1 2 3 |
sudo echo 'enable_uart=1' >> /PATH/TO/BOOT/MOUNTPOINT/config.txt |
Medium/advanced users can skip this section. For people new to GPIO, it’s worth reading. On Raspberry Pi systems, you can expect pin references to come in at least two forms: BCM and BOARD. The BCM pinout is cryptic but very portable, whereas the BOARD pinout is as easy as 1-2-3, with the GPIO pins on the right of the board and the microSD slot facing away from you. But note that it is subject to change!
Pin 1 is 3V3, pin 2 is 5V, and so on. This numbering scheme lets you count from the top until you find the pin you want. A third one is WiringPi’s numbering scheme, and of course GPIO itself, which I don’t use or recommend. Stick with BCM or BOARD numbering and you’ll never stray from the path.
You can view all of these numbering schemes ad-hoc with the gpio utility from the “wiringpi” software
package, using the command:
1 2 3 |
gpio readall |
The output will look something like this:
Your mileage may vary, from board to board. See the Pinout.xyz website for comprehensive pinout diagrams for all Raspberry Pi boards. Much time will be saved!
You will need the following components in order to build this
Circuit:
BOM:
Raspberry Pi 4 | https://www.amazon.com/Raspberry-Model-2019-Quad-Bluetooth/dp/B07TD42S27/ref=sr_1_3?keywords=raspberry+pi+4&qid=1581051818&sr=8-3 |
HC-SR501 PIR sensor | https://protosupplies.com/product/hc-sr501-pir-motion-sensing-module/ |
Dupont wires | https://www.amazon.com/Elegoo-EL-CP-004-Multicolored-Breadboard-arduino/dp/B01EV70C78/ref=sr_1_2?keywords=dupont+wire&qid=1581051893&sr=8-2 |
Breadboard | https://www.amazon.com/Pcs-MCIGICM-Points-Solderless-Breadboard/dp/B07PCJP9DY/ref=sr_1_8?keywords=breadboard&qid=1581051922&sr=8-8 |
ROHM SLR343BC4TT32 3mm LED | https://www.digikey.com/products/en?keywords=ROHM%20SLR343BC4TT32 |
2x 1kOhm resistors | https://www.amazon.com/Projects-100EP5121K00-Ohm-Resistors-Pack/dp/B0185FIJ9A/ref=sr_1_3?keywords=resistor+1k+ohm&qid=1581052039&sr=8-3 |
Once you’ve got the parts together, completing the circuit on the breadboard is a snap. Follow the diagram below, and pay particular attention to which Pi GPIO pins you’re wiring up.
The HC-SR501 accepts input voltages from 5 to 12V, so pull a jumper wire from the 5V pin (BOARD2 and BOARD4, now that we’re so well versed in pin numbering schemes) to your breadboard’s 5V/red power rail.
Then pull a jumper wire from GND (BOARD6) to the black rail.
From there hook 5V to the HC-SR501 Pin 3, and hook GND to Pin 1.
Place a 1kOhm resistor between the PIR’s Pin 2 and Raspberry Pi’s BCM25/BOARD22 pin, for the sake of Pi pin longevity. The signal will not be affected. This is our input.
When you wish to tune the IR-sensitivity, trigger behavior (the 3P header with positions H and L) and ON-time for the signal pin’s 3V3 signal, consult thisguide to the HC-SR501. The datasheet is available here.
Below you’ll see I set the jumper to “no-repeat” (L), which allegedly means its signal will stay at 3V3 as long as there is motion — a bit unreliable. The capacitors also looked a bit unreliable, but some dabs of hot glue prevented them from leaving the module forever.
In the second image, you’ll see two potentiometers. The left is for adjusting sensitivity (CW to increase and vice versa), the right for adjusting ON-time (also CW to increase and also vice versa). Pretty neat.
We also want to add a small LED to indicate when motion is detected. I used a blue ROHM
SLR343BC4TT32 3mm LED. There should be a current limiting resistor in series with the Pi’s BCM25/BOARD18 pin and the LED’s anode (long leg), use at least 1kOhm here. Wire the LED’s cathode to the GND rail, and you’re done.
For our example application, we’ll be using WiringPi’s “gpio” utility to read 3V3/0V from the PIR to the Pi. It’s plenty, and I’d have preferred to catch rising edge and ignore the signal ON-time altogether, but that requires a hardware filter (2 1kOhm resistors, 1 50kOhm resistor, and a 100nF electrolytic capacitor) to snuff out all jitter…
… plus perhaps a software filter (check, wait 5ms, check again) to work reliably. It is overkill for determining the state of a pin, but now you know that catching edges require a bit more work on Raspberry Pi than on Arduino, for instance.
[ begin pir_wiringpi.sh ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#! /bin/bash # Detects when BOARD22/BCM25/WiringPi6 goes to 3V3, # and lights a LED for $_action_time seconds when this # happens. # Notes: # * Depends on 'gpio' utility from the wiringpi package. Install it # with 'apt-get -y install wiringpi'. # * Give PIR sensor 30-60s to adjust to ambient IR, you may # want to delay before reading signal from it. set -eu -o pipefail PATH=/bin:/usr/bin _pir_pin=25 # BCM25/BOARD22/WiringPi6 _led_pin=24 # BCM24/BOARD18/WiringPi5 # How long should the action run? _action_time=5 # Clean up on ^C and TERM, use 'gpio unexportall' to flush everything manually. trap "gpio unexport $_pir_pin ; gpio unexport $_led_pin" INT TERM # Explicitly uses BCM_GPIO pin numbering, no '-g' flag needed. gpio export $_pir_pin in gpio export $_led_pin out # Let PIR settle to ambient IR to avoid false positives? # Uncomment line below. #sleep 30 while true do _ret=$( gpio -g read $_pir_pin ) if [ $_ret -eq 1 ] then echo "[!] PIR is tripped, LED on ..." echo "( Do real stuff here ... helloworld with a bullhorn *wink* )" # ... gpio -g write $_led_pin 1 sleep $_action_time elif [ $_ret -eq 0 ] then gpio -g write $_led_pin 0 fi sleep 0.1 done |
[ end pir_wiringpi.sh ]
It is about half the length of a Python3 script utilizing the RPi.GPIO library with the same functionality, but much clearer.
You may be puzzled by the “trap” early in the script — it’s just a shell signal handler. If you press CTRL+C to break out of the running program, the GPIO states will be cleaned up.
Apart from this, the program just chugs along, checking if the PIR pin reads 0 (0V) or 1 (3V3). If it reads 0, nothing happens. If it reads 1, it performs some novel action for 5 seconds.
What exactly constitutes a novel action is entirely up to you!
If you’re drawing a blank, consider the reverse doorbell I thought of last Halloween. Instead of a button, use the PIR, and instead of an indoor bell, use an outdoor bullhorn (I have a 36W bullhorn, it’s AWESOME!). And when would this come in handy?
Only on Halloween? Combat pre-adolescent diabetes by denying other people’s children candy! Be a novel philanthrope!