There are many ways to use sensors to automate things around your house. You could send an alert if your freezer stops working, or flip on lights when it gets dark. Sometimes you just want to collect information about your house. A good place to start is the basic motion detector. Motion detectors have all kinds of useful purposes, I have one that alerts me when someone is at the front door, and others that control lights based on presence. Today we'll look at how I built those sensors and the lessons I learned along the way.

<Code>  something, something, algorithm... </Code>

Simple motion sensors are basically just a switch; they see motion, they flip on (HIGH), the stop seeing motion, they turn off (LOW). Generally when we're using it to control a light, we don't want it to go immediately off when motion stops. Ideally we'd like it to stay on for a few minutes just in case we stepped out of its field of view or stepped in the other room for a minute. We can accomplish this with a timer on the Arduino. When the sensor first sees motion, we set a timer for something like 600,000ms (10min). If it sees motion again before the timer runs out, it resets it to a full 10min again. Once motion has stopped for 10min and the timer runs out, we fire off an "all clear". This will allow the sensor to appear in the on position for the full duration of motion.

Code Highlights

The code I started with originally came from the PIRsense page on the Arduino Playground. You can find the specific parts of setup there or at my GitHub.

Reading the motion detector:

void readPIR() {
  if(digitalRead(PIR_PIN) == HIGH){ // Motion Started
    if(lockLow){ // this is defined as true at boot
      //makes sure we wait for a transition to LOW before any further output is made:
      lockLow = false;
      debugPrinter("Motion detected", 1);
      sendAllClear = false;
      sendSensorData(249, 1);
      delay(50);
    }
    takeLowTime = true;
  }

  if(digitalRead(PIR_PIN) == LOW){ // Motion Ended
    if(takeLowTime){
      lowIn = millis();          //save the time of the transition from high to LOW
      takeLowTime = false;       //make sure this is only done at the start of a LOW phase
    }
    //if the sensor is low for more than the given pause,
    //we assume that no more motion is going to happen
    if(!lockLow && millis() - lowIn > PAUSE){
      //makes sure this block of code is only executed again after
      //a new motion sequence has been detected
      lockLow = true;
      debugPrinter("Motion ended.", 1);
      sendAllClear = true;
      lastClearStatus = currentMillis;
      delay(50);
    }
  }
}

Ok, it looks like a huge chunk of code just to read an on/off. It's not so bad, let's break it down. When the sensor sees motion, it goes HIGH. Once it goes HIGH it sends off a motion detected message and sets two flags. The first flag lets us know that we shouldn't fire a new motion event until after we get a LOW reading and the second flag tells us that we'll eventually need to send an "all clear" message.

When the motion eventually ends, it takes a few seconds to stabilize then makes note of the time and sets two more flags. The first flag tells us we can once again look for motion (HIGH) events and the second flag alerting us that we can send an all clear once the timer expires. Where's the timer you ask? Good question, coming right up!

Sending the "all clear"

void allClearTimer() {
  bool isAllClear = ((currentMillis - lastClearStatus) > allClearTimeout);
  if (sendAllClear && isAllClear) {
    debugPrinter("sending all clear", 1);
    sendSensorData(249,0);
    sendAllClear = false;
  }
}

This is just a state timer that compares the last time we saw motion stop and the current time. If difference in these times is greater than our timeout, we send out an "all clear" message. As a final step, we clear the flag that told us we needed to send this message.

Find this and more Arduino Projects on Clintgeek's GitHub.

Hardware

Hardware for this build is pretty bare bones. We need an Arduino, an rf24 radio, and a motion sensor. Motion sensors come in a couple of main varieties and are interchangeable.

The most common by far are PIR sensors. They have two sides that are sensitive to infrared energy and can detect warm objects that pass from one side to the other. They're cheap, easily available, and have a service life similar to a Twinkie's "best before" date. There is a downside though... I started with these on my front porch and they worked fine all summer and fall. Once temperatures started falling below 50F, they got pretty sketchy. Some days they'd ignore everything and other days they'd fire off once a minute or more. I'd probably pass on them for outdoor use unless you live somewhere with really temperate weather.

The less common ones are built with a doppler radar. Yes, the same thing that powers weather radars and police radar guns. They come in two varieties and the one you want is the cheaper one. The more expensive one can actually detect speed and direction, we just want to detect presence. These specs state that they can sense presence through 3/4" plywood. I've found that it can often see me through my solid wood garage door. They are impervious to temperature and should be just as long-lasting as the PIRs. The same code and libraries will work with both these and the PIR sensors.

Parts List

The links below are to actual products that I have used. They are not affiliate links and I'm not associated or sponsored by any of the sellers.

  • Arduino - I prefer the generic Nano clones due to cost and size but any Arduino compatible board will work.
  • Doppler Radar - You need either this or the PIR sensor
  • PIR Sensor - You need either this or the Doppler Radar
  • NRF24L01+ Radio - This is what links it back to whatever you're linking to. Your needs may vary depending on your setup.

Pin Diagram

Build Photos