Practical 8

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

FromToNote
PIR A OUTArduino D2Entrance direction reference
PIR B OUTArduino D3-
PIR VCC / GNDArduino 5V / GND-
7-seg segment a-g, dpArduino D4-D11Each via 220R resistor
Digit 1 / Digit 2 commonArduino D12 / D13Multiplexed 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

  1. The Arduino watches both PIR outputs for rising edges (motion detected).
  2. A small state machine records the trigger order: A,B = ENTRY (+1); B,A = EXIT (-1).
  3. The count is clamped to 0..MAX_CAPACITY and pushed to the 7-segment display.
  4. When count == MAX_CAPACITY, the buzzer sounds and the red LED stays ON.
  5. 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 System
2#include <SevSeg.h>
3
4const int PIR_A = 2; // First sensor on the entry path
5const int PIR_B = 3; // Second sensor on the entry path
6const int FULL_LED = A1;
7const int MAX_CAPACITY = 20;
8
9SevSeg sevseg;
10int count = 0;
11
12// Simple sequence tracker: which sensor fired first
13bool aFirst = false, bFirst = false;
14unsigned long lastTrigger = 0;
15const unsigned long SEQ_TIMEOUT = 1500; // ms
16
17bool wasA = false, wasB = false;
18
19void setup() {
20 pinMode(PIR_A, INPUT);
21 pinMode(PIR_B, INPUT);
22 pinMode(FULL_LED, OUTPUT);
23
24 byte numDigits = 2;
25 byte digitPins[] = { 12, 13 }; // Digit commons
26 byte segmentPins[] = { 4, 5, 6, 7, 8, 9, 10, 11 }; // a,b,c,d,e,f,g,dp
27 bool resistorsOnSegments = true;
28 sevseg.begin(COMMON_CATHODE, numDigits, digitPins, segmentPins,
29 resistorsOnSegments);
30 Serial.begin(9600);
31}
32
33void loop() {
34 bool a = digitalRead(PIR_A);
35 bool b = digitalRead(PIR_B);
36 unsigned long now = millis();
37
38 if (now - lastTrigger > SEQ_TIMEOUT) { // Reset stale sequence
39 aFirst = false; bFirst = false;
40 }
41
42 if (a && !wasA) { // Rising edge on PIR A
43 aFirst = true; lastTrigger = now;
44 if (bFirst) { // B then A = leaving
45 count = max(count - 1, 0);
46 Serial.println("EXIT");
47 aFirst = bFirst = false;
48 }
49 }
50 if (b && !wasB) { // Rising edge on PIR B
51 bFirst = true; lastTrigger = now;
52 if (aFirst) { // A then B = entering
53 count = min(count + 1, MAX_CAPACITY);
54 Serial.println("ENTRY");
55 aFirst = bFirst = false;
56 }
57 }
58 wasA = a; wasB = b;
59
60 bool full = (count >= MAX_CAPACITY);
61 digitalWrite(FULL_LED, full ? HIGH : LOW);
62
63 sevseg.setNumber(count, 0);
64 sevseg.refreshDisplay();
65}