PIR-Based Occupancy Counter for Smart Office

Project Overview

This project creates an occupancy counter that tracks how many people are in a room. Using two PIR sensors placed at a doorway, the system detects direction of movement (entry vs. exit) and maintains a count of current occupants. The count is displayed on an LCD and can be sent to a server for occupancy monitoring.

Difficulty: Intermediate
Estimated time: 3-4 hours
Estimated cost: $30-40

How It Works

Two PIR sensors are placed at the top and bottom of a doorway (or left and right). When a person passes through, the order in which the sensors trigger indicates direction:

  • Sensor A then Sensor B = entry (count +1)
  • Sensor B then Sensor A = exit (count -1)

The system maintains a running count of people in the room, displayed on an LCD. An ESP32 can send occupancy data to a server or cloud platform.

Materials Needed

  • Arduino Uno or ESP32 (1)
  • HC-SR501 PIR sensors (2)
  • LCD display (16×2 with I2C)
  • Push buttons (for resetting count)
  • LEDs (red and green for status)
  • Resistors (220Ω for LEDs, 10k for buttons)
  • Jumper wires
  • Power supply (5V 1A)
  • Project enclosure

Circuit Diagram

Connection Table

Component Pin Arduino Pin
Sensor A (Entry) VCC 5V
Sensor A (Entry) GND GND
Sensor A (Entry) OUT Digital Pin 2
Sensor B (Exit) VCC 5V
Sensor B (Exit) GND GND
Sensor B (Exit) OUT Digital Pin 3
LCD (I2C) VCC 5V
LCD (I2C) GND GND
LCD (I2C) SDA A4 (SDA)
LCD (I2C) SCL A5 (SCL)
Reset Button One pin Digital Pin 4 (with 10k pull-up)
Reset Button Other pin GND
Green LED Anode Digital Pin 5 (through 220Ω)
Green LED Cathode GND
Red LED Anode Digital Pin 6 (through 220Ω)
Red LED Cathode GND

Arduino Code

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Pin definitions
const int sensorA = 2;  // Entry sensor (outside)
const int sensorB = 3;  // Exit sensor (inside)
const int resetButton = 4;
const int ledGreen = 5;
const int ledRed = 6;

// LCD (16x2, I2C address 0x27)
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Variables
int occupancy = 0;
bool sensorATriggered = false;
bool sensorBTriggered = false;
unsigned long sensorATime = 0;
unsigned long sensorBTime = 0;
unsigned long lastEventTime = 0;
const unsigned long eventWindow = 2000; // 2 seconds to pair triggers
const int maxOccupancy = 50; // Safety limit

void setup() {
  Serial.begin(9600);
  
  // Initialize pins
  pinMode(sensorA, INPUT);
  pinMode(sensorB, INPUT);
  pinMode(resetButton, INPUT_PULLUP);
  pinMode(ledGreen, OUTPUT);
  pinMode(ledRed, OUTPUT);
  
  // Initialize LCD
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Occupancy Counter");
  lcd.setCursor(0, 1);
  lcd.print("Initializing...");
  
  Serial.println("Occupancy Counter Starting...");
  Serial.println("Waiting 60 seconds for sensor warm-up...");
  delay(60000);
  
  updateDisplay();
  Serial.println("System Ready");
}

void updateDisplay() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("People in room:");
  lcd.setCursor(0, 1);
  lcd.print("    ");
  lcd.setCursor(5, 1);
  lcd.print(occupancy);
  
  // Update LEDs
  if (occupancy > 0) {
    digitalWrite(ledGreen, HIGH);
    digitalWrite(ledRed, LOW);
  } else {
    digitalWrite(ledGreen, LOW);
    digitalWrite(ledRed, HIGH);
  }
  
  Serial.print("Occupancy: ");
  Serial.println(occupancy);
}

void processEntry() {
  occupancy++;
  if (occupancy > maxOccupancy) occupancy = maxOccupancy;
  Serial.println("Entry detected (+1)");
  updateDisplay();
}

void processExit() {
  occupancy--;
  if (occupancy < 0) occupancy = 0;
  Serial.println("Exit detected (-1)");
  updateDisplay();
}

void checkTriggers() {
  unsigned long now = millis();
  
  // Check sensor A (entry)
  if (digitalRead(sensorA) == HIGH && !sensorATriggered) {
    sensorATriggered = true;
    sensorATime = now;
    Serial.println("Sensor A triggered");
  }
  
  // Check sensor B (exit)
  if (digitalRead(sensorB) == HIGH && !sensorBTriggered) {
    sensorBTriggered = true;
    sensorBTime = now;
    Serial.println("Sensor B triggered");
  }
  
  // Determine direction if both triggered within window
  if (sensorATriggered && sensorBTriggered) {
    if (abs((long)(sensorATime - sensorBTime)) < eventWindow) {
      if (sensorATime < sensorBTime) {
        // A first, then B = entry
        processEntry();
      } else {
        // B first, then A = exit
        processExit();
      }
    }
    // Reset trigger flags
    sensorATriggered = false;
    sensorBTriggered = false;
  }
  
  // Timeout: clear single triggers after window
  if (sensorATriggered && (now - sensorATime > eventWindow)) {
    sensorATriggered = false;
    Serial.println("Sensor A timeout - ignored");
  }
  if (sensorBTriggered && (now - sensorBTime > eventWindow)) {
    sensorBTriggered = false;
    Serial.println("Sensor B timeout - ignored");
  }
}

void loop() {
  // Check reset button
  if (digitalRead(resetButton) == LOW) {
    delay(50);
    if (digitalRead(resetButton) == LOW) {
      occupancy = 0;
      updateDisplay();
      Serial.println("Occupancy reset to 0");
      while (digitalRead(resetButton) == LOW) {
        delay(10);
      }
    }
  }
  
  checkTriggers();
  delay(50);
}

ESP32 Version with Wi-Fi Data Logging

For remote monitoring, use an ESP32 and send occupancy data to a server:

#include <WiFi.h>
#include <HTTPClient.h>

const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";
const char* serverURL = "http://yourserver.com/api/occupancy";

void sendOccupancyData() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(serverURL);
    http.addHeader("Content-Type", "application/json");
    String payload = "{\"occupancy\":" + String(occupancy) + "}";
    int httpCode = http.POST(payload);
    http.end();
  }
}

// Call sendOccupancyData() after each occupancy change

Installation Steps

  1. Position sensors: Mount one sensor on each side of the doorway at 1.5-2m height. Point them slightly downward and toward the doorway center. Ensure fields overlap.
  2. Adjust sensor settings: Set both sensors to minimum hold time (5 seconds) and maximum sensitivity.
  3. Connect circuit: Assemble on breadboard and test sensor pairing.
  4. Upload code: Load code to Arduino and open Serial Monitor to debug.
  5. Calibrate: Walk through doorway several times to verify direction detection is accurate.
  6. Adjust event window: If people move quickly through the door, reduce eventWindow. If slowly, increase.
  7. Final mounting: Secure sensors and enclosure, route wires neatly.

Calibration Tips

  • Sensor spacing: Place sensors 30-50cm apart for clear direction detection.
  • Avoid overlapping fields: Ensure sensors trigger sequentially, not simultaneously.
  • Test with groups: People walking together may be counted as one; adjust sensitivity to detect individuals.
  • Test with children: Ensure sensors are positioned to detect children as well as adults.

Troubleshooting

  • Direction detection inaccurate: Adjust sensor positions and event window. Ensure sensors are not triggered simultaneously.
  • Multiple counts for one person: Reduce sensor hold time to minimum. Add debouncing in code.
  • Missed counts: Increase sensor sensitivity. Ensure sensors cover the entire doorway width.
  • Count drifting over time: Implement periodic reset or add a confirmation mechanism (e.g., door contact sensor).
  • False triggers from pets: Use pet-immune lenses or mount sensors higher.

Project Extensions

  • Door contact sensor: Add a magnetic reed switch to only count when the door is open, reducing false counts from motion near door.
  • Real-time clock: Log occupancy by time of day to analyze peak usage patterns.
  • Thermal printer: Add a thermal printer to print occupancy reports on demand.
  • Traffic light indicator: Add a traffic light outside the room to indicate occupancy status (green = available, red = full).
  • Email alerts: Send email when occupancy reaches maximum capacity.
  • Integration with reservation system: Connect to booking platform to show real-time room availability.

Conclusion

This occupancy counter provides accurate, real-time room occupancy data. It’s ideal for meeting rooms, shared workspaces, and any area where capacity management is important. With Wi-Fi integration, occupancy data can be accessed remotely and used to optimize space utilization.

Leave a Reply

Your email address will not be published. Required fields are marked *