Visitor / Object Counter System
Bidirectional counting with live display
Aim
To count people or objects entering and leaving a room using two detection points, and to display the live occupancy count on a display module.
Overview
Two PIR motion sensors (or IR beams) are placed in a line at the doorway. The order in which they trigger determines the direction: sensor A then B means someone entered, sensor B then A means someone left. The Arduino keeps the count between 0 and the room capacity and shows it on a 2-digit 7-segment display. A buzzer and a red LED warn when the room is full.
Components Required
- Arduino Uno
- 2 x PIR motion sensor (HC-SR501) or IR beam pairs
- 2-digit 7-segment display (common cathode) or SevSeg compatible display
- Buzzer
- Red LED (room full indicator)
- Breadboard and jumper wires
Circuit Connections
| From | To | Note |
|---|---|---|
| PIR A OUT | Arduino D2 | Entrance direction reference |
| PIR B OUT | Arduino D3 | - |
| PIR VCC / GND | Arduino 5V / GND | - |
| 7-seg segment a-g, dp | Arduino D4-D11 | Each via 220R resistor |
| Digit 1 / Digit 2 common | Arduino D12 / D13 | Multiplexed digits |
| Buzzer + | External pin or transistor on A0 | - |
| Red LED (via 220R) | Arduino A1 | - |
Interactive Circuit Diagram
Simulate the circuit: adjust the sensor inputs below and watch the outputs react according to the control logic.
Loading Wokwi components...
Working
- The Arduino watches both PIR outputs for rising edges (motion detected).
- A small state machine records the trigger order: A,B = ENTRY (+1); B,A = EXIT (-1).
- The count is clamped to 0..MAX_CAPACITY and pushed to the 7-segment display.
- When count == MAX_CAPACITY, the buzzer sounds and the red LED stays ON.
- A timeout resets a half-completed sequence so a single stray trigger cannot corrupt the count.
Control Logic
Direction is derived from trigger sequence, not from a single sensor, which is what makes the counter bidirectional. count = clamp(count + delta, 0, MAX). FULL alert whenever count == MAX.
Arduino Code
1// Practical 8: Visitor / Object Counter System2#include <SevSeg.h>34const int PIR_A = 2; // First sensor on the entry path5const int PIR_B = 3; // Second sensor on the entry path6const int FULL_LED = A1;7const int MAX_CAPACITY = 20;89SevSeg sevseg;10int count = 0;1112// Simple sequence tracker: which sensor fired first13bool aFirst = false, bFirst = false;14unsigned long lastTrigger = 0;15const unsigned long SEQ_TIMEOUT = 1500; // ms1617bool wasA = false, wasB = false;1819void setup() {20 pinMode(PIR_A, INPUT);21 pinMode(PIR_B, INPUT);22 pinMode(FULL_LED, OUTPUT);2324 byte numDigits = 2;25 byte digitPins[] = { 12, 13 }; // Digit commons26 byte segmentPins[] = { 4, 5, 6, 7, 8, 9, 10, 11 }; // a,b,c,d,e,f,g,dp27 bool resistorsOnSegments = true;28 sevseg.begin(COMMON_CATHODE, numDigits, digitPins, segmentPins,29 resistorsOnSegments);30 Serial.begin(9600);31}3233void loop() {34 bool a = digitalRead(PIR_A);35 bool b = digitalRead(PIR_B);36 unsigned long now = millis();3738 if (now - lastTrigger > SEQ_TIMEOUT) { // Reset stale sequence39 aFirst = false; bFirst = false;40 }4142 if (a && !wasA) { // Rising edge on PIR A43 aFirst = true; lastTrigger = now;44 if (bFirst) { // B then A = leaving45 count = max(count - 1, 0);46 Serial.println("EXIT");47 aFirst = bFirst = false;48 }49 }50 if (b && !wasB) { // Rising edge on PIR B51 bFirst = true; lastTrigger = now;52 if (aFirst) { // A then B = entering53 count = min(count + 1, MAX_CAPACITY);54 Serial.println("ENTRY");55 aFirst = bFirst = false;56 }57 }58 wasA = a; wasB = b;5960 bool full = (count >= MAX_CAPACITY);61 digitalWrite(FULL_LED, full ? HIGH : LOW);6263 sevseg.setNumber(count, 0);64 sevseg.refreshDisplay();65}