Wireless People Counter with ESP32

January 26, 2024 by
Christilien Fouche

How to count people with ESP32

How does this project work? 

For this project you'll be using 2 ESP32 dev modules. One will be counting the people and sending it back to another ESP32 which will be displaying the people that was counted. The counter ESP32 uses a laser laser module and a laser receiver module to create a ''beam'' effect. When someone walks past the laser the receiver module receives nothing and a person is counted. When the receiver module sees the laser again the next person is ready to be counted. The counter module doesn't determine direction so at the output side the amount of people counted is divided by 2 ( when someone walks in and out they are counted twice). While counting the people the ESP32 counter creates a ''hotspot'' which the display ESP32 connects to, making wireless data transfer possible via WIFI. When the display unit receives the amount of people counted it displays people counted followed by the amount. The connection between the two ESP boards is secured with a password. You can also display the amount of people counted on your PC using the IP address of the module.

What do I need for this project?

There are a few things required which will be divided into hardware and software:

Software Requirements: 

For the purpose of this project I'll be using Arduino IDE to write sketches to the ESP32 module. To use the Arduino's software to program an ESP32 board you'll  need to add the ESP32 drivers. Once you have uploaded the drivers you should be able to select the board from your Arduino IDE board menu(tutorial here). We will also be using the WIFI.h library which is quite useful when using WIFI as a wireless communication medium. Here is a list (with links) of all the software you'll need :

  1. Arduino IDE
  2. WiFi.h Library
  3. Adafruit SSD1306 Library
  4. ESPAsyncWebServer Library (download link)
  5. Async TCP Library (download link)
  6. Wire.h Library

Once you have all the software ready to go we can start moving on to the hardware. All the software used in this project is free to download and use.

Hardware Requirements: 

For the hardware side of the project we'll be using ESP32 development boards. The one ESP board will be sending data and the other one will be receiving the data. For the connections between the ESP modules and the sensors we will be using Female-Female jumper cables. 

Here is a list of all the hardware you require : 

  1. ESP32 Development board x2
  2. Laser module
  3. Laser receiver module
  4. I2C Display
  5. Female to Female Jumper Cables x10

This project is easily built without a breadboard. For real life function you might want to replace the Jumper Cables with extended wires to add the laser to one side of the entrance and the receiver to the other side. You can also add a solar panel with a battery if there is no power point available at the location where the project will be installed. The boards will need to be in WiFi range of one another which is approximately 400 meters line of sight.  

 With all the hardware and software ready lets get stared with the Coding:

Coding the ESP32 Sender 

When it comes to coding the modules it can be quite hard but I have already coded the basic structure. You can copy the code and change it as you like. Also feel free to share any improvements and ideas. 

This is the code for the Sender: 

// Include necessary libraries
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include <Wire.h>

// Set the Access Point name and password
const char* ssid = "ESP32_Traffic_Counter";
const char* password = "123456789";

// Initialize variables
int count = 0;
int A = 23;
bool Read = false;

// Create a new web server on port 80
AsyncWebServer server(80);

// Setup function, called once at the beginning of the program
void setup() {

// Set the input pin A to use the internal pull-up resistor
pinMode(A, INPUT_PULLUP);

// Initialize the serial communication at 115200 baud
Serial.begin(115200);
Serial.println();

// Set up the Access Point
Serial.print("Setting Access Point");
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);

// Set up the web server route for "/People_Counted" that returns the count value as a plain text response
server.on("/People_Counted", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send_P(200, "text/plain", String(count).c_str());
});

// Start the web server
server.begin();
}

// Loop function, called repeatedly
void loop() {
if (digitalRead(A) && Read) {
// If the input on pin A is high and Read is true, increment count and print it to serial monitor
Read = false;
count++;
Serial.println(count);
} else if (!digitalRead(A) && !Read) {
// If the input on pin A is low and Read is false, set Read to true
Read = true;
}
}

 

How does this code work? 

 First off start by adding the necessary libraries and declare the SSID(name) of the WIFI network as well as the password. If you do not need a password for your project there is a line of code further down that you can edit. 

// Include necessary libraries
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include <Wire.h>

// Set the Access Point name and password
const char* ssid = "ESP32_Traffic_Counter";
const char* password = "123456789";

 

The next few lines of code above the void setup is where you  will be declaring your variables. Int A is the pin number on the ESP32 where the output for the laser receiver module is connected. Count will be used to count the amount of people passing the beam. You will also create a web server instance on port 80. 

// Initialize variables
int count = 0;
int A = 23;
bool Read = false;

// Create a new web server on port 80
AsyncWebServer server(80);

 

 In the void setup you'll first be declaring pin 23 as a input pullup, every time someone passes the laser the internal pullup resistor will pull the output pin of the laser receiver module to HIGH. 

The serial port initialization is recommended for troubleshooting. You can view the amount of people counted as well as your WIFI status in the serial monitor at 115200 baud rate. 

Next the WIFI access point is initialized. If you do not need a password for your project you can just remove the password variable from the WIFI.softAP function. 

The last part of the void setup is where you create a path which you can access from your PC. The number of people counted will be returned as a plain text at the specified path. After the path has been set you can start the web server. The count variable is passed to the web server from the void Loop function.

// Setup function, called once at the beginning of the program
void setup() {

// Set the input pin A to use the internal pull-up resistor
pinMode(A, INPUT_PULLUP);

// Initialize the serial communication at 115200 baud
Serial.begin(115200);
Serial.println();

// Set up the Access Point
Serial.print("Setting Access Point");
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);

// Set up the web server route for "/People_Counted" that returns the count value as a plain text response
server.on("/People_Counted", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send_P(200, "text/plain", String(count).c_str());
});

// Start the web server
server.begin();
}

 

Lastly in the Void Loop function the code stops the counter from counting infinitely when the beam is broken. It also increments the count variable when someone passes the laser.

// Loop function, called repeatedly
void loop() {
if (digitalRead(A) && Read) {
// If the input on pin A is high and Read is true, increment count and print it to serial monitor
Read = false;
count++;
Serial.println(count);
} else if (!digitalRead(A) && !Read) {
// If the input on pin A is low and Read is false, set Read to true
Read = true;
}
}

 

Now just hit upload and the code should work fine. As we are done with the sender, next we will do the receiver: 

Coding the ESP32 receiver

So the receiver is quite simple as it just receives the data and prints it out on the i2c display. Here is the code we'll be working with : 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Wi-Fi credentials
const char* ssid = "ESP32_Traffic_Counter";  // Replace with your Wi-Fi SSID
const char* password = "123456789";           // Replace with your Wi-Fi password

// Server URL for people counting data
const char* serverNamePeople = "http://192.168.4.1/People_Counted";  // Replace with the actual server URL

// OLED display setup
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET 4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

String people = "5";  // Initialize people count

unsigned long previousMillis = 0;
const long interval = 5000;  // Update interval in milliseconds

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);

  // Initialize the OLED display
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }

  // Clear the display and show "Hello" on startup
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.print("Hello");
  display.display();

  Serial.println("Connecting to Wi-Fi");

  // Uncomment the following loop if you want to wait until connected
  /*
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  */

  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());
  delay(1000);
  display.clearDisplay();
  display.display();
}

void loop() {
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    if (WiFi.status() == WL_CONNECTED) {
      people = httpGETRequest(serverNamePeople);
      Serial.println("People Counted: " + people);

      display.clearDisplay();
      display.setTextSize(2);
      display.setTextColor(WHITE);
      display.setCursor(0, 0);
      display.print("Count: ");
      display.print(people.toInt() / 2);  // Assuming it's divided by 2 for display
      display.print(" ");
      display.setTextSize(1);
      display.cp437(true);
      display.display();

      previousMillis = currentMillis;
    } else {
      Serial.println("WiFi Disconnected");
      delay(1000);
    }
  }
}

// Function to perform an HTTP GET request and return the response as a string
String httpGETRequest(const char* serverName) {
  WiFiClient client;
  HTTPClient http;

  http.begin(client, serverName);

  int httpResponseCode = http.GET();
  String payload = "--";

  if (httpResponseCode > 0) {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
    payload = http.getString();
  } else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }

  http.end();

  return payload;
}

When we have a look at the receivers code it might look a little bit daunting but no worries we'll have a look at it in just a moment... So without stalling any further lets jump straight into it !! 

How does this code work? 

1
2
3
4
5
#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

In this section, we're including necessary libraries. Libraries are like toolboxes with pre-made tools to help us do various tasks. These libraries provide functions and features for connecting to Wi-Fi, making web requests, and controlling the display.

1
2
const char* ssid = "ESP32_Traffic_Counter";
const char* password = "123456789";

These two lines set up your Wi-Fi network name (SSID) and the password needed to access your Wi-Fi network. Think of it like entering a key to unlock your home Wi-Fi.

1
const char* serverNamePeople = "http://192.168.4.1/People_Counted";

 

 This line creates a variable called serverNamePeople that holds a web address (URL) where your ESP32 will go to get information about how many people have been counted. It's like writing down the address of a specific webpage.

1
2
3
4
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET 4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

 

 Here, we're setting up a small screen (OLED display) that you can use to show information. It's like a mini-TV for your device. The SCREEN_WIDTH and SCREEN_HEIGHT tell the screen how big it is, and OLED_RESET is for resetting the screen if needed.

1
2
3
String people = "5";
unsigned long previousMillis = 0;
const long interval = 5000;

In this part, we're setting up some variables:

  • people is a text variable that starts with the value "5". This variable will store how many people have been counted.
  • previousMillis is a special clock that helps us keep track of time.
  • interval is how often we want to check for updates, and it's set to 5000 milliseconds, which is 5 seconds.
1
2
3
void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);

The setup function is where you prepare your device. Here, we start communication with your computer (via Serial) and connect to Wi-Fi using the SSID and password you provided.

1
2
3
4
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
  Serial.println(F("SSD1306 allocation failed"));
  for(;;);
}

 

 This part initializes the display. If it fails, it shows an error message on the screen and waits indefinitely.

1
2
3
4
5
6
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print("Hello");
display.display();

This code clears the display and writes "Hello" on it. The display commands are like telling the display what to show.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void loop() {
  unsigned long currentMillis = millis();

  // Check if it's time for an update
  if (currentMillis - previousMillis >= interval) {

    // Ensure the device is connected to Wi-Fi
    if (WiFi.status() == WL_CONNECTED) {

      // Get people count from the web server
      people = httpGETRequest(serverNamePeople);

      // Display the people count on the OLED screen
      display.clearDisplay();
      display.setTextSize(2);
      display.setTextColor(WHITE);
      display.setCursor(0, 0);
      display.print("Count: ");
      display.print(people.toInt() / 2);  // Assuming it's divided by 2 for display
      display.print(" ");
      display.setTextSize(1);
      display.cp437(true);
      display.display();

      // Update the 'previousMillis' to remember when the last update occurred
      previousMillis = currentMillis;
    } else {
      // Handle Wi-Fi disconnection by displaying a message and waiting for a while
      Serial.println("WiFi Disconnected");
      delay(1000);
    }
  }
}

In this extended code snippet, we go through the loop function in more detail. We check if it's time for an update based on the specified interval. If it is, we proceed to get the people count from the web server only if the device is connected to Wi-Fi. We display this count on the OLED screen. If the device is not connected to Wi-Fi, we handle it by displaying a message and waiting for a while. The previousMillis variable is updated to keep track of the last update time.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
String httpGETRequest(const char* serverName) {
  WiFiClient client;    // Create a network client to communicate with the server
  HTTPClient http;      // Create an HTTP client for making web requests

  http.begin(client, serverName);  // Specify the server to connect to

  int httpResponseCode = http.GET();  // Send an HTTP GET request to the server
  String payload = "--";  // Initialize 'payload' with two dashes in case of an error

  // Check if the HTTP request was successful
  if (httpResponseCode > 0) {
    // Read the response from the server and store it in 'payload'
    payload = http.getString();
  } else {
    // Handle errors, e.g., by printing an error message
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }

  // End the HTTP connection
  http.end();

  // Return the response data received from the server
  return payload;
}

In this expanded code snippet, we delve into the httpGETRequest function. This function is responsible for making HTTP requests to a web server. It first creates a network client and an HTTP client for communication. It specifies the server to connect to using the provided serverName. The function then sends an HTTP GET request to the server and checks if the request was successful by examining the httpResponseCode. If successful, it reads and stores the server's response in the payload variable. If an error occurs, it prints an error message. Finally, the HTTP connection is closed, and the payload is returned, which contains the response data received from the server.

 


Share this post
Tags