Count the people in a room with Blynk

Two IR sensors side by side at a doorway tell whether someone walks in or out. Blynk shows how many people are inside, the visits today, and sends a notification when the room is full.
  • Level:Advanced
  • Duration: 1 h 10 min
  • Ages 12+
  • Platform: ESP32

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Count the people in a room with Blynk – EEMENTOR STEM™ lesson

Shops, libraries and classrooms often need to know how many people are inside. This counter works it out by itself. Two IR sensors sit side by side on the door frame. Someone walking in passes sensor A first and then B; someone walking out passes B first. The ESP32 counts up or down, and the Blynk app shows the number inside, a chart over the day and the total visits. Set the maximum with a slider: when the room is full, the red LED lights and you get a notification. It is a great way to see how busy your room really is.

What you'll learn

  • How the order of two sensors tells the direction
  • How to keep a count that can never go below zero
  • How to send a notification only once until things change
  • How to reset a count from the app and at midnight

What you need

  • ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
  • 2 IR obstacle avoidance sensor modules (45-in-1 Sensor Kit)
  • Green and red LEDs and 2 resistors, 220 Ω (ESP32 IoT Starter Kit)
  • Long jumper wires to reach the door frame
  • A free Blynk account and the Blynk IoT app on a phone
  • A 2.4 GHz Wi-Fi network
  • The printed parts from the 3D printing box below (doorway sensor bar), or the cardboard version described there
  • Breadboard and jumper wires (ESP32 IoT Starter Kit)

Wiring

Breadboard wiring diagram: Count the people in a room with Blynk
Breadboard view. Check each connection against the wiring table below.
From To
IR sensor A (outside) OUT ESP32 GPIO 34
IR sensor B (inside) OUT ESP32 GPIO 35
Both IR sensors + and GND 3V3 and GND
ESP32 GPIO 25 220 Ω resistor, then the green LED's long leg
ESP32 GPIO 26 220 Ω resistor, then the red LED's long leg
Both LED short legs GND

Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.

Set up Blynk

In the Blynk console, create a new template called Room Counter (hardware ESP32, connection type Wi-Fi) and add these datastreams. Lesson 1 of this path shows each step in detail.

Virtual pin Name Data type Range Phone widget
V0 Inside Integer 0–50 Gauge or Superchart
V1 Visits today Integer 0–1000 Value display
V2 Maximum Integer 1–30 Slider
V3 Status String – Label
V4 Reset Integer 0–1 Button (push)

Then open the template's Events & Notifications tab, add an event with the code room_full (name: Room is full) and turn on push notifications for it. The event code must match the sketch exactly.

Save the template, create a new device from it and copy its three BLYNK_… lines. In the phone app, open the device, tap the wrench icon and add a Gauge (V0), a Value display (V1), a Slider (V2), a Label (V3) and a Button in push mode (V4). Link each widget to its datastream.

The sketch

Replace the three BLYNK_… lines at the top with the ones from your device's Device info page, and add your Wi-Fi name and password (2.4 GHz only). The BLYNK_… lines must stay above #include <BlynkSimpleEsp32.h>.

Change TIME_ZONE to your own time zone. The example is for Central Europe; search for "POSIX time zone" and your city to find yours (for example EET-2EEST,M3.5.0/3,M10.5.0/4 for Estonia or GMT0BST,M3.5.0/1,M10.5.0 for the UK).

#define BLYNK_TEMPLATE_ID "YOUR_TEMPLATE_ID"
#define BLYNK_TEMPLATE_NAME "Room Counter"
#define BLYNK_AUTH_TOKEN "YOUR_AUTH_TOKEN"
#define BLYNK_PRINT Serial          // show connection messages in the Serial Monitor

#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include <time.h>

char ssid[] = "YOUR_WIFI_NAME";
char pass[] = "YOUR_WIFI_PASSWORD";
const char* TIME_ZONE = "CET-1CEST,M3.5.0,M10.5.0/3";   // your time zone

const int A_PIN = 34, B_PIN = 35;           // A outside, B inside
const int GREEN_PIN = 25, RED_PIN = 26;
const unsigned long WALK_MS = 1500;          // the second sensor must follow within 1.5 s

BlynkTimer timer;
int inside = 0, visits = 0, maximum = 10, today = -1;
bool fullSent = false;
bool aWas = false, bWas = false;
unsigned long aAt = 0, bAt = 0;              // when each sensor last saw someone arrive

void update() {
  bool full = inside >= maximum;
  digitalWrite(GREEN_PIN, !full);
  digitalWrite(RED_PIN, full);
  Blynk.virtualWrite(V0, inside);
  Blynk.virtualWrite(V1, visits);
  Blynk.virtualWrite(V3, full ? "Full" : (String(maximum - inside) + " places free"));
  if (full && !fullSent) {
    Blynk.logEvent("room_full", String("There are ") + inside + " people in the room.");
    fullSent = true;
  }
  if (inside < maximum - 1) fullSent = false;   // ready to warn again
}

void checkSensors() {
  bool a = digitalRead(A_PIN) == LOW;          // LOW: something in front
  bool b = digitalRead(B_PIN) == LOW;
  unsigned long now = millis();
  if (a && !aWas) {                            // someone reaches A
    if (bAt != 0 && now - bAt < WALK_MS) {     // B came first: walking out
      inside = max(0, inside - 1);
      bAt = 0;
      Serial.println("Out");
      update();
    } else {
      aAt = now;
    }
  }
  if (b && !bWas) {                            // someone reaches B
    if (aAt != 0 && now - aAt < WALK_MS) {     // A came first: walking in
      inside++;
      visits++;
      aAt = 0;
      Serial.println("In");
      update();
    } else {
      bAt = now;
    }
  }
  aWas = a;
  bWas = b;
}

void newDay() {
  time_t now = time(nullptr);
  if (now < 1700000000) return;
  struct tm local;
  localtime_r(&now, &local);
  if (today != -1 && local.tm_yday != today) {   // midnight: the room is empty again
    inside = 0;
    visits = 0;
    update();
  }
  today = local.tm_yday;
}

BLYNK_WRITE(V2) {                              // Maximum slider
  maximum = constrain(param.asInt(), 1, 30);
  update();
}

BLYNK_WRITE(V4) {                              // Reset button
  if (param.asInt() == 1) {
    inside = 0;
    update();
  }
}

BLYNK_CONNECTED() {
  Blynk.syncVirtual(V2);
  update();
}

void setup() {
  Serial.begin(115200);
  pinMode(A_PIN, INPUT);
  pinMode(B_PIN, INPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(RED_PIN, OUTPUT);
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  configTzTime(TIME_ZONE, "pool.ntp.org");
  timer.setInterval(60000L, newDay);
}

void loop() {
  Blynk.run();
  timer.run();
  checkSensors();
}

Try it

  1. Walk in through the door at a normal speed. The Serial Monitor says "In", and the app shows 1 inside and 1 visit.
  2. Walk out: "Out", and the number inside goes back to 0.
  3. Set the maximum to 2 and walk in twice: the red LED lights and your phone gets "Room is full".
  4. If the count drifts (two people at once can fool it), tap Reset when the room is empty.

How it works

One sensor can tell that someone passed, but not which way. Two sensors side by side can: whoever is walking in reaches A a moment before B, and whoever is walking out reaches B before A. The sketch remembers when each sensor last saw someone arrive. When B fires and A fired less than 1.5 seconds earlier, someone walked in. When A fires shortly after B, someone walked out. Using that time also stops a person who only looks through the door from being counted.

The count can't go below zero: max(0, inside - 1) keeps it there, even if someone was already inside when the counter started.

The "room is full" notification is sent once. It is only allowed again when the room has emptied by at least two places, so one person stepping in and out at the door doesn't send a flood of messages.

Every minute, newDay() checks the date from internet time. When the day changes, the counts start again from zero.

Try this next

  • Add a Superchart to see when the room was busiest during the day.
  • Show the number inside on the 4-digit display next to the door.
  • Count the visits per hour and find the busiest hour of the week.

Troubleshooting

  • The device shows "Offline" in the app: check the Serial Monitor at 115200 baud. "Invalid auth token" means the BLYNK_AUTH_TOKEN line is wrong; if it never connects, check the Wi-Fi name and password, and that your network is 2.4 GHz.
  • Everyone is counted as going in: sensors A and B are swapped. Swap the two wires, or the two pin numbers.
  • Nobody is counted: turn each sensor's screw until its light comes on when you walk past. Check that both see you, one after the other.
  • Two people side by side count as one: the sensors can only see one person at a time. Use it for a door where people walk through one by one.
  • A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

ESP32 IoT Starter Kit | Arduino IDE Compatible | Free Tutorials $57.00

Shop the kit