Skip to Content
Project ListIntermediate9. Visitor Counter

9. Two-Sensor Visitor Counter

Use two spaced digital IR sensors to infer direction and maintain an occupancy estimate.

Level: Intermediate · Time: 50 minutes

This simple algorithm assumes one person crosses both sensors in sequence. Simultaneous or side-by-side traffic can produce incorrect counts; it is not a commercial people counter.

Wiring

SensorESP32
Sensor A OUTGPIO 18
Sensor B OUTGPIO 19
Power / GNDPer module documentation / common GND

The sketch assumes detection produces LOW.

Complete sketch

#include <PxServ.h> const char* WIFI_SSID = "your_wifi_ssid"; const char* WIFI_PASSWORD = "your_wifi_password"; const char* PXSERV_API_KEY = "your_pxserv_api_key"; const int SENSOR_A = 18; const int SENSOR_B = 19; const unsigned long SEQUENCE_TIMEOUT = 2000; PxServ client(PXSERV_API_KEY); int occupancy = 0; int sequenceStart = 0; unsigned long sequenceAt = 0; void publishCount(String direction) { client.setData("visitor/occupancy", String(occupancy)); client.setData("visitor/last-direction", direction); } void setup() { Serial.begin(115200); pinMode(SENSOR_A, INPUT_PULLUP); pinMode(SENSOR_B, INPUT_PULLUP); PxServ::connectWifi(WIFI_SSID, WIFI_PASSWORD); publishCount("startup"); } void loop() { bool a = digitalRead(SENSOR_A) == LOW; bool b = digitalRead(SENSOR_B) == LOW; unsigned long now = millis(); if (sequenceStart == 0) { if (a && !b) { sequenceStart = 1; sequenceAt = now; } if (b && !a) { sequenceStart = 2; sequenceAt = now; } } else if (now - sequenceAt > SEQUENCE_TIMEOUT) { sequenceStart = 0; } else if (sequenceStart == 1 && b) { occupancy++; publishCount("entry"); sequenceStart = 0; delay(500); } else if (sequenceStart == 2 && a) { occupancy = max(0, occupancy - 1); publishCount("exit"); sequenceStart = 0; delay(500); } delay(10); }

Test

Move your hand past A then B for entry and reverse the order for exit. Space sensors about 20–30 cm along the direction of travel and reduce their ranges so they do not overlap.

Last updated