Off-The-Shelf Hacker: Create an Early Warning Detector with Passive Infrared Sensors

For years I’ve wanted to build a constellation of passive infrared sensors (PIR) in my yard, giving me early warning of an intruder and perhaps activate the appropriate force field, on demand. Alarm systems are great when somebody tries to break into your house. What about lunatics from the local funny farm, the occasional snarling rabid dog or maybe even zombies? That extra 10-15 seconds of notice might be the difference between life and … well, you know.
Will a PIR sensor even pick up a zombie, since they’re sort of dead already and likely not emitting any infrared energy? We can’t know the answer to that unless we build a device and try it out. In a true Off-The-Shelf Hacker fashion, let’s hack a prototype.
PIR 101
Passive infrared sensors are digital devices that detect heat. Mammals emit tiny amounts of infrared heat by the very fact that they are living. Electronics monitor two halves of the sensor chip inside the device, and the signals cancel each other out when there’s no difference between them, which would otherwise indicate infrared light falling on the chip. If one-half senses a different level of IR light, the PIR sensor fires a pulse to the output pin indicating movement. There’s a lens system in front of the sensor that helps focus the infrared energy on the chip itself.
There are two types of PIR sensors and the output pin will go either high or low to indicate an object. The device used in this project is known as an “open collector,” meaning that the output is held high with a pull-up resistor until an object passes in front of the detector. The output is then pulled low, thus signaling detection. The open collector signaling method makes it easy to daisy chain multiple sensors on the same 3-wire bus.
I purchased my PIR sensor from Sparkfun and it’s of the open collector persuasion. The output pin goes low, with detection. Arduino marketplace Adafruit sells a newer, more adjustable sensor. Its output is normally low and goes high on detection. So, if you pirate code from the Internet, make sure you look for the appropriate high or low output. It’s a simple matter to reverse the “LOW” and “HIGH” values when you read the microcontroller input pin to properly interpret your detection events.
Wire It Up
One cool characteristic of microcontrollers is that you can take basic circuits and just switch input devices, to get different behaviors. For example, to build this project, I re-purposed the wireless temperature sensor, from a past OTSH project and just swapped the DS18B20 digital thermometer for the PIR sensor. Both sensors are digital and adjustments for the different devices are easily made in the code.
Take a look at the breadboard:

ESP8266-PIR Breadboard
Notice the wire colors from the PIR sensor to the breadboard. My device has red, white and black wires. Red is for VCC (positive). This particular sensor is rated for from 5 to 12 volts. Oddly, the white wire goes to ground (negative). Lastly, the black wire is output. The output of the PIR sensor connects to the general purpose digital input/output (GPIO) pin number 2 on the ESP8266 micro-controller. You’d think the black would be ground and the white would be the output. Nope. Don’t worry, I’ve hooked mine up wrong without ill effects.
Adafruit has a slightly more advanced PIR sensor with a little header and adjustable delay and sensitivity. The header has the ground pin on the left, output in the middle and VCC on the right, so wire color is your choice. The Banggood sensor looks to be the same as the Adafruit offering.
Other sensors might use different pin configurations or wire colors. Don’t forget that if you buy an Adafruit device, you’ll need to swap your logic around to respond to a HIGH output, upon detection, instead of a LOW output.
My PIR sensor was also a bit sensitive to input voltage. I ran it from the 5 volts supplied by the USB connection and it gave a lot of false positives. Increasing the voltage (to the PIR sensor) to 9 volts gave very reliable operation. At that voltage, I could detect objects to about eight feet.
Remember that the module detects the difference between the two halves of the sensor, so the object has to be moving for the output to change. You can connect the positive side of a 9-volt battery to the positive PIR sensor lead while removing the link that goes to +5 volts. Connect the negative side of the battery to the common ground. Connect the negative lead of the sensor to the common ground as well.
Bending Code
Much of the code I used was from the previous ESP8266 temperature sensor project. As before, you can initially join the ESP8266-PIR sensor to your local LAN using the on-board access point and web configuration page, using your smartphone.
Also, notice that I used the built-in standard “pin 13” LED to tell when the PIR sensor fired. Pin 13 is typically connected to a LED on Arduino projects for basic operation and testing.
Further down in the code, I also added a little counter that prints out to the serial monitor, showing how many times the PIR sensor pings the input pin. It will reset to zero during a reboot. I used the sequential numbers for testing and diagnostics.
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
#include <ESP8266WiFi.h> #include <DNSServer.h> #include <ESP8266WebServer.h> #include <WiFiManager.h> void configModeCallback (WiFiManager *myWiFiManager) { Serial.println("Entered config mode"); Serial.println(WiFi.softAPIP()); //if you used auto generated SSID, print it Serial.println(myWiFiManager->getConfigPortalSSID()); } /* * The setup function. We only start the sensors here */ WiFiServer server(1337); void printWiFiStatus(); int calibrationTime = 20; int pirPin = 2; //the digital pin connected to the PIR sensor's output int ledPin = 13; int tr = 1; void setup(void) { // start serial port Serial.begin(9600); Serial.println("ESP8266 Passive Infrared Detector Demo"); pinMode(pirPin, INPUT_PULLUP); pinMode(ledPin, OUTPUT); digitalWrite(pirPin, LOW); WiFiManager wifiManager; // wifiManager.resetSettings(); wifiManager.setAPCallback(configModeCallback); if(!wifiManager.autoConnect()) { Serial.println("failed to connect and hit timeout"); ESP.reset(); delay(1000); } // Start TCP server. server.begin(); // Calibrate sensor Serial.print("calibrating sensor "); for(int i = 0; i < calibrationTime; i++){ Serial.print("."); delay(1000); } Serial.println(" done"); Serial.println("SENSOR ACTIVE"); delay(50); } /* * Main function, get and show the temperature */ void loop(void) { // Check if module is still connected to WiFi. if (WiFi.status() != WL_CONNECTED) { Serial.println("WiFi connected inside void loop"); while (WiFi.status() != WL_CONNECTED) { Serial.println("WiFi.status connected loop"); delay(500); } // Print the new IP to Serial. printWiFiStatus(); } WiFiClient client = server.available(); if (client) { Serial.println("Client connected."); while (client.connected()) { // Serial.println("PIR sensor working"); // PIR detection function int proximity = digitalRead(pirPin); if (proximity == LOW) // If the sensor's output goes low, motion is detected { digitalWrite(ledPin, HIGH); // Serial.println("Motion detected!"); client.write("motion"); client.write("\n"); tr = tr + 1; Serial.println(tr); delay(200); } else { digitalWrite(ledPin, LOW); //Serial.println("Nothing"); // client.write("nothing"); // client.write("\n"); delay(200); } } } Serial.println("Client disconnected."); tr = 0; client.stop(); } void printWiFiStatus() { Serial.println(""); Serial.print("Connected to "); // Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } |
It took a while to get the PIR sensor to work with the ESP8266, illustrating one of the challenges of hacking physical computing devices.
While firmware-based microcontrollers, like the Arduino and ESP8266, are considered very fast, they have their limitations and you need to take those into account when building your projects. Add in a WiFi stack, as in the case of the ESP8266 and your real-time operation will likely have to make a few compromises.
I wanted to pick up all movement in front of the sensor and send a message to another computer on my LAN for logging and alarm purposes. The monitor machine could be a Linux laptop, a Raspberry Pi or even another ESP8266 device.
So, when the 8266 is sitting there in the main loop watching the PIR sensor pin for a state change, you have to allow the WiFi stack to occasionally take control, otherwise, the built-in watchdog timer (WDT) will trip and cause the microcontroller to reset. It’s a safety feature to prevent endless loops and other such problems.
It turned out that I had to add in a few delays to give the WiFi radio time to do its communication thing between sensor readings. A 200 ms delay between PIR readings worked well. There’s little chance that the PIR detector will miss seeing a moving object in that short time span. You’ll usually get multiple state changes anyway, in quick succession, when an object moves in front of the sensor.
Put in the delays, boost the PIR supply voltage to at least 9 volts and the sensor becomes rock solid.
At the other end of the network connection on my Linux notebook, is the netcat command, listening and printing out the “motion” text, whenever the PIR sensor fires. As spelled out in the other article, piping the output of netcat (we’re using Linux after all) to a file and adding in time/date information, using AWK, lets you log data without having to implement time/date routines on the 8266. Keeping it simple makes it easy to troubleshoot and maintain.
Next Steps
I used an ESP8266 model 07 for this project. The 07 has 9 GPIO pins. All we need for this sensor is one pin. Why not see if we can get the same functionality with an ESP8266 model 01?
I’ll explore what it takes to move from the “overkill” 07 model to the bare-bones 01 device, in a future OTSH article.
In the meantime, I’ll go round up test zombies.