Built-in project · Arduino Uno R3

PIR motion light with a relay on an Arduino Uno

After dark, an HC-SR501 PIR switches a 12 V lamp on through a relay, and it goes off 20 s after the last movement; a light sensor keeps it off by day. The whole build, an Arduino Uno and 5 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Beginner About 20 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $39. On Mokxi they're free.

PIR motion light with a relay · Arduino Uno R3live0.000 s 0.00x
Click to open it in the editor
The circuit itself, running here on the simulator. Press what can be pressed; click anything else to open it in the editor.

What you'll learn

  • Switching a higher voltage load with a relay
  • PIR motion sensing
  • Using a digital light module output
  • Restarting a timer on every event
  • Difficulty: Beginner
  • Time: About 20 minutes
  • Board: Arduino Uno R3
  • Parts: 7

How it works

An HC-SR501 PIR on pin 2 senses movement. A photoresistor module on pin 3 reads HIGH when it is dark. The sketch only reacts to motion if it is dark.

In the dark, any motion switches pin 7 HIGH, which closes the relay and powers a 12 V lamp. Each new motion restarts a timer.

The lamp goes off ON_MS (20 seconds) after the last motion. The sketch only checks darkness while the lamp is off, so the lamp's own light cannot fool it.

Parts list

7 parts, plus the jumper wires. They retail for about $39; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Relay module pin INArduino Uno R3 pin 7
Photoresistor sensor module pin DOArduino Uno R3 pin 3
Motion sensor pin OUTArduino Uno R3 pin 2
Motion sensor pin VCCArduino Uno R3 pin 5V
Photoresistor sensor module pin VCCArduino Uno R3 pin 5V
Relay module pin VCCArduino Uno R3 pin 5V
Motion sensor pin GNDArduino Uno R3 pin GND
Photoresistor sensor module pin GNDArduino Uno R3 pin GND
Relay module pin GNDArduino Uno R3 pin GND
Filament lamp pin 1Relay module pin NO
DC jack and adapter pin V+Relay module pin COM
DC jack and adapter pin V-Ground
Filament lamp pin 2Ground

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// PIR motion light with a relay: after dark, someone walking past switches a
// 12 V lamp on through a relay, and it stays on until nobody has moved for
// ON_MS. In daylight the PIR is ignored.
//
//   pin 2   the HC-SR501's OUT (VCC 5V, GND)
//   pin 3   the photoresistor module's DO: HIGH when darker than its trimmer
//   pin 7   the relay module's IN (high trigger, VCC 5V, GND)
//   relay   COM to the 12 V adapter's +, NO to the lamp, the lamp's other
//           lead to the adapter's -
//           (the adapter's - also to GND, sharing the Uno's ground)
//
// The HC-SR501 holds OUT high for a couple of seconds after the last movement
// (its Tx trimmer) and then needs a moment before it can trigger again. The
// sketch keeps its own, longer timer: every time OUT is high the timer starts
// over, so the light stays on while anyone is moving and goes off ON_MS after
// the last of them.
//
// Darkness is only read while the lamp is off. On a real porch the lamp
// shines on its own sensor, so a sketch that keeps checking would see
// "daylight", switch the lamp off, see "dark" and switch it on again, all
// night. Here the lamp's light does not reach the module, but the sketch is
// written for the real porch.
//
// The PIR's output is 3.3 V, which an Uno pin reads as HIGH (it wants 3.0 V).
// The lamp is 12 V; switching mains with a relay belongs in a closed box.
//
// Press Run, drag the light slider down to night, then click the PIR.

const int PIR_PIN = 2;
const int DARK_PIN = 3;
const int RELAY_PIN = 7;

const unsigned long ON_MS = 20000;  // 20 s after the last movement

bool lampOn = false;
bool dark = false;
bool known = false;  // whether dark has been read yet
unsigned long lastMotion = 0;

void lamp(bool state) {
  lampOn = state;
  digitalWrite(RELAY_PIN, state ? HIGH : LOW);
  Serial.println(state ? "lamp ON" : "lamp OFF");
}

void setup() {
  pinMode(PIR_PIN, INPUT);
  pinMode(DARK_PIN, INPUT);
  digitalWrite(RELAY_PIN, LOW);
  pinMode(RELAY_PIN, OUTPUT);
  Serial.begin(9600);
  Serial.println("PIR motion light: waiting for dark");
}

void loop() {
  unsigned long now = millis();

  if (!lampOn) {
    bool nowDark = digitalRead(DARK_PIN) == HIGH;
    if (nowDark != dark || !known) {
      dark = nowDark;
      known = true;
      Serial.println(dark ? "dark: watching for motion" : "daylight: PIR ignored");
    }
  }

  if (digitalRead(PIR_PIN) == HIGH) {
    if (dark) {
      lastMotion = now;
      if (!lampOn) lamp(true);
    }
  }

  if (lampOn && now - lastMotion >= ON_MS) {
    lamp(false);
  }
  delay(50);
}

Code walkthrough

The key functions of the sketch, and what each one does.

lamp sketch.ino

Sets lampOn, writes the relay pin HIGH or LOW to match, and prints "lamp ON" or "lamp OFF". Every change of the lamp goes through here.

void lamp(bool state) {
  lampOn = state;
  digitalWrite(RELAY_PIN, state ? HIGH : LOW);
  Serial.println(state ? "lamp ON" : "lamp OFF");
}

setup sketch.ino

Sets the PIR and dark pins as inputs. It writes the relay pin LOW before making it an output, so the lamp does not flash on at start-up.

void setup() {
  pinMode(PIR_PIN, INPUT);
  pinMode(DARK_PIN, INPUT);
  digitalWrite(RELAY_PIN, LOW);
  pinMode(RELAY_PIN, OUTPUT);
  Serial.begin(9600);
  Serial.println("PIR motion light: waiting for dark");
}

loop sketch.ino

While the lamp is off, it reads DARK_PIN and reports changes. If the PIR is HIGH and it is dark, it records lastMotion and turns the lamp on. When ON_MS has passed since the last motion, it turns the lamp off.

void loop() {
  unsigned long now = millis();

  if (!lampOn) {
    bool nowDark = digitalRead(DARK_PIN) == HIGH;
    if (nowDark != dark || !known) {
      dark = nowDark;
      known = true;
      Serial.println(dark ? "dark: watching for motion" : "daylight: PIR ignored");
    }
  }

  if (digitalRead(PIR_PIN) == HIGH) {
    if (dark) {
      lastMotion = now;
      if (!lampOn) lamp(true);
    }
  }

  if (lampOn && now - lastMotion >= ON_MS) {
    lamp(false);
  }
  delay(50);
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The lamp never turns on This project
    It has to be dark. Drag the light slider down to night, then click the PIR. In daylight the sketch ignores motion.
  • The relay clicks but the lamp stays dark This project
    Wire the adapter's + to COM and the lamp to NO. Join the adapter's - to ground and to the lamp's other lead.
  • The lamp stays on too long or not long enough This project
    Change ON_MS. It is in milliseconds.
  • The sensor reads high for ages after power up. Motion sensor
    A real PIR needs 30 to 60 seconds to settle. Ignore its output during that time, and keep still in front of it until the delay passes.
  • Wrong output or it never triggers. Motion sensor
    Power it from 5 V, join GND, and read OUT as a digital input (3.3 V logic high). Check the trimmers for delay and sensitivity.
  • The digital output never changes, or is always on, no matter the light. Photoresistor sensor module
    Turn the trimmer on the module until DO flips at the light level you want. The digital output is set by that threshold, and which direction means bright depends on the board.
  • Analog readings go down when it gets brighter, and the code expects the opposite. Photoresistor sensor module
    The LDR sits in a divider, so the direction depends on how the module is built. Print the raw value in the light and dark, then flip it in code with 1023 minus the reading if needed.
  • The relay does not click, or the board resets when it switches. Relay module
    A relay coil needs more current than a pin can give. Use a module with a transistor driver, and power VCC from 5 V. Share ground between the module and the Arduino.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.