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 :
- Arduino IDE
- WiFi.h Library
- Adafruit SSD1306 Library
- ESPAsyncWebServer Library (download link)
- Async TCP Library (download link)
- 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 :
- ESP32 Development board x2
- Laser module
- Laser receiver module
- I2C Display
- 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.